Merge branch 'develop' into travis/pinned_messages

This commit is contained in:
Travis Ralston 2017-10-14 16:10:32 -06:00
commit c34b55c6c7
185 changed files with 4991 additions and 3543 deletions

View file

@ -32,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() {
@ -40,7 +40,7 @@ module.exports = React.createClass({
width: 40,
height: 40,
resizeMethod: 'crop',
defaultToInitialLetter: true
defaultToInitialLetter: true,
};
},
@ -50,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;
@ -71,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,
});
}
},
@ -109,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 its 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,
@ -150,7 +149,7 @@ module.exports = React.createClass({
width: width + "px",
lineHeight: height + "px" }}
>
{initialLetter}
{ initialLetter }
</EmojiText>
);
const imgNode = (
@ -163,15 +162,15 @@ module.exports = React.createClass({
<AccessibleButton element='span' className="mx_BaseAvatar"
onClick={onClick} {...otherProps}
>
{textNode}
{imgNode}
{ textNode }
{ imgNode }
</AccessibleButton>
);
} else {
return (
<span className="mx_BaseAvatar" {...otherProps}>
{textNode}
{imgNode}
{ textNode }
{ imgNode }
</span>
);
}
@ -196,5 +195,5 @@ module.exports = React.createClass({
{...otherProps} />
);
}
}
},
});

View file

@ -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 (
<BaseAvatar {...otherProps} name={this.state.name} title={this.state.title}
idName={member.userId} url={this.state.imageUrl} onClick={onClick}/>
idName={member.userId} url={this.state.imageUrl} onClick={onClick} />
);
}
},
});

View file

@ -36,7 +36,7 @@ module.exports = React.createClass({
render: function() {
return (
<button className="mx_CreateRoomButton" onClick={this.onClick}>{_t("Create Room")}</button>
<button className="mx_CreateRoomButton" onClick={this.onClick}>{ _t("Create Room") }</button>
);
}
},
});

View file

@ -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 (
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
<option value={this.Presets.PrivateChat}>{_t("Private Chat")}</option>
<option value={this.Presets.PublicChat}>{_t("Public Chat")}</option>
<option value={this.Presets.Custom}>{_t("Custom")}</option>
<option value={this.Presets.PrivateChat}>{ _t("Private Chat") }</option>
<option value={this.Presets.PublicChat}>{ _t("Public Chat") }</option>
<option value={this.Presets.Custom}>{ _t("Custom") }</option>
</select>
);
}
},
});

View file

@ -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 (
<input type="text" className="mx_RoomAlias" placeholder={_t("Alias (optional)")}
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
value={this.props.alias}/>
value={this.props.alias} />
);
}
},
});

View file

@ -23,6 +23,7 @@ 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;
@ -241,32 +242,25 @@ module.exports = React.createClass({
_doNaiveGroupRoomSearch: function(query) {
const lowerCaseQuery = query.toLowerCase();
MatrixClientPeg.get().getGroupRooms(this.props.groupId).then((resp) => {
const results = [];
resp.chunk.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);
}).catch((err) => {
console.error('Error whilst searching group users: ', err);
this.setState({
searchError: err.errcode ? err.message : _t('Something went wrong!'),
});
}).done(() => {
this.setState({
busy: false,
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) {

View file

@ -155,7 +155,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
width={48} height={48}
/>
<div className="mx_ChatCreateOrReuseDialog_profile_name">
{this.state.profile.displayName || this.props.userId}
{ this.state.profile.displayName || this.props.userId }
</div>
</div>;
}
@ -177,7 +177,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
<BaseDialog className='mx_ChatCreateOrReuseDialog'
onFinished={ this.props.onFinished.bind(false) }
onFinished={this.props.onFinished.bind(false)}
title={title}
>
{ content }

View file

@ -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 (
<BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
onEnterPressed={this.onOk}
title={_t('Create Room')}
>
<div className="mx_Dialog_content">
<div className="mx_CreateRoomDialog_label">
<label htmlFor="textinput"> { _t('Room name (optional)') } </label>
</div>
<div>
<input id="textinput" ref="textinput" className="mx_CreateRoomDialog_input" autoFocus={true} size="64" />
</div>
<br />
<details className="mx_CreateRoomDialog_details">
<summary className="mx_CreateRoomDialog_details_summary">{ _t('Advanced options') }</summary>
<div>
<input type="checkbox" id="checkbox" ref="checkbox" defaultChecked={this.defaultNoFederate} />
<label htmlFor="checkbox">
{ _t('Block users on other matrix homeservers from joining this room') }
<br />
({ _t('This setting cannot be changed later!') })
</label>
</div>
</details>
</div>
<div className="mx_Dialog_buttons">
<button onClick={this.onCancel}>
{ _t('Cancel') }
</button>
<button className="mx_Dialog_primary" onClick={this.onOk}>
{ _t('Create Room') }
</button>
</div>
</BaseDialog>
);
},
});

View file

@ -83,7 +83,7 @@ export default class DeactivateAccountDialog extends React.Component {
let error = null;
if (this.state.errStr) {
error = <div className="error">
{this.state.errStr}
{ this.state.errStr }
</div>;
passwordBoxClass = 'error';
}
@ -94,30 +94,30 @@ export default class DeactivateAccountDialog extends React.Component {
let cancelButton = null;
if (!this.state.busy) {
cancelButton = <button onClick={this._onCancel} autoFocus={true}>
{_t("Cancel")}
{ _t("Cancel") }
</button>;
}
return (
<div className="mx_DeactivateAccountDialog">
<div className="mx_Dialog_title danger">
{_t("Deactivate Account")}
{ _t("Deactivate Account") }
</div>
<div className="mx_Dialog_content">
<p>{_t("This will make your account permanently unusable. You will not be able to re-register the same user ID.")}</p>
<p>{ _t("This will make your account permanently unusable. You will not be able to re-register the same user ID.") }</p>
<p>{_t("This action is irreversible.")}</p>
<p>{ _t("This action is irreversible.") }</p>
<p>{_t("To continue, please enter your password.")}</p>
<p>{ _t("To continue, please enter your password.") }</p>
<p>{_t("Password")}:</p>
<p>{ _t("Password") }:</p>
<input
type="password"
onChange={this._onPasswordFieldChange}
ref={(e) => {this._passwordField = e;}}
className={passwordBoxClass}
/>
{error}
{ error }
</div>
<div className="mx_Dialog_buttons">
<button
@ -125,10 +125,10 @@ export default class DeactivateAccountDialog extends React.Component {
onClick={this._onOk}
disabled={!okEnabled}
>
{okLabel}
{ okLabel }
</button>
{cancelButton}
{ cancelButton }
</div>
</div>
);

View file

@ -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 = (
<div>
<div>{this.state.authError.message || this.state.authError.toString()}</div>
<div>{ this.state.authError.message || this.state.authError.toString() }</div>
<br />
<AccessibleButton onClick={this._onDismissClick}
className="mx_UserSettings_button"
>
{_t("Dismiss")}
{ _t("Dismiss") }
</AccessibleButton>
</div>
);
@ -100,7 +100,7 @@ export default React.createClass({
onFinished={this.props.onFinished}
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
>
{content}
{ content }
</BaseDialog>
);
},

View file

@ -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,11 +116,11 @@ 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});

View file

@ -68,7 +68,7 @@ export default React.createClass({
<label htmlFor="textinput"> { this.props.description } </label>
</div>
<div>
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" onKeyDown={this.onKeyDown} />
<input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" />
</div>
</div>
<div className="mx_Dialog_buttons">

View file

@ -28,9 +28,9 @@ function DeviceListEntry(props) {
return (
<li>
<DeviceVerifyButtons device={ device } userId={ userId } />
<DeviceVerifyButtons device={device} userId={userId} />
{ device.deviceId }
<br/>
<br />
{ device.getDisplayName() }
</li>
);
@ -48,13 +48,13 @@ function UserUnknownDeviceList(props) {
const {userId, userDevices} = props;
const deviceListEntries = Object.keys(userDevices).map((deviceId) =>
<DeviceListEntry key={ deviceId } userId={ userId }
device={ userDevices[deviceId] } />,
<DeviceListEntry key={deviceId} userId={userId}
device={userDevices[deviceId]} />,
);
return (
<ul className="mx_UnknownDeviceDialog_deviceList">
{deviceListEntries}
{ deviceListEntries }
</ul>
);
}
@ -71,13 +71,13 @@ function UnknownDeviceList(props) {
const {devices} = props;
const userListEntries = Object.keys(devices).map((userId) =>
<li key={ userId }>
<li key={userId}>
<p>{ userId }:</p>
<UserUnknownDeviceList userId={ userId } userDevices={ devices[userId] } />
<UserUnknownDeviceList userId={userId} userDevices={devices[userId]} />
</li>,
);
return <ul>{userListEntries}</ul>;
return <ul>{ userListEntries }</ul>;
}
UnknownDeviceList.propTypes = {
@ -120,17 +120,17 @@ export default React.createClass({
if (blacklistUnverified) {
warning = (
<h4>
{_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.") }
</h4>
);
} else {
warning = (
<div>
<p>
{_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.") }
</p>
</div>
);
@ -149,22 +149,22 @@ export default React.createClass({
>
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
<h4>
{_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}) }
</h4>
{ warning }
{_t("Unknown devices")}:
{ _t("Unknown devices") }:
<UnknownDeviceList devices={this.props.devices} />
</GeminiScrollbar>
<div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" autoFocus={ true }
<button className="mx_Dialog_primary" autoFocus={true}
onClick={() => {
this.props.onFinished();
Resend.resendUnsentEvents(this.props.room);
}}>
{_t("Send anyway")}
{ _t("Send anyway") }
</button>
<button className="mx_Dialog_primary" autoFocus={ true }
<button className="mx_Dialog_primary" autoFocus={true}
onClick={() => {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148

View file

@ -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);
}

View file

@ -79,8 +79,8 @@ export default React.createClass({
onMouseLeave={this._onMouseLeave}
>
<TintableSvg src={this.props.iconPath} width={this.props.size} height={this.props.size} />
{tooltip}
{ tooltip }
</AccessibleButton>
);
}
},
});

View file

@ -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; }}
>
<AddressTile address={this.props.addressList[i]} justified={true} networkName="vector" networkUrl="img/search-icon-vector.svg" />
</div>
</div>,
);
}
}
@ -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() }
</div>
);
}
},
});

View file

@ -107,24 +107,24 @@ export default React.createClass({
let nameNode = null;
if (address.displayName) {
nameNode = <div className={nameClasses}>{ address.displayName }</div>
nameNode = <div className={nameClasses}>{ address.displayName }</div>;
}
info = (
<div className="mx_AddressTile_mx">
<div className={emailClasses}>{ address.address }</div>
{nameNode}
{ nameNode }
</div>
);
} else {
error = true;
var unknownClasses = classNames({
const unknownClasses = classNames({
"mx_AddressTile_unknown": true,
"mx_AddressTile_justified": this.props.justified,
});
info = (
<div className={unknownClasses}>{_t("Unknown Address")}</div>
<div className={unknownClasses}>{ _t("Unknown Address") }</div>
);
}
@ -151,5 +151,5 @@ export default React.createClass({
{ dismiss }
</div>
);
}
},
});

View file

@ -23,7 +23,7 @@ import PlatformPeg from '../../../PlatformPeg';
import ScalarAuthClient from '../../../ScalarAuthClient';
import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import sdk from '../../../index';
import AppPermission from './AppPermission';
import AppWarning from './AppWarning';
@ -195,9 +195,9 @@ export default React.createClass({
// These strings are translated at the point that they are inserted in to the DOM, in the render method
_deleteWidgetLabel() {
if (this._canUserModify()) {
return 'Delete widget';
return _td('Delete widget');
}
return 'Revoke widget access';
return _td('Revoke widget access');
},
/* TODO -- Store permission in account data so that it is persisted across multiple devices */

View file

@ -24,7 +24,7 @@ const CreateRoomButton = function(props) {
return (
<ActionButton action="view_create_room"
mouseOverAction={props.callout ? "callout_create_room" : null}
label={ _t("Create new room") }
label={_t("Create new room")}
iconPath="img/icons-create-room.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -30,7 +30,7 @@ export default React.createClass({
getInitialState: function() {
return {
device: this.props.device
device: this.props.device,
};
},
@ -60,37 +60,37 @@ export default React.createClass({
onUnverifyClick: function() {
MatrixClientPeg.get().setDeviceVerified(
this.props.userId, this.state.device.deviceId, false
this.props.userId, this.state.device.deviceId, false,
);
},
onBlacklistClick: function() {
MatrixClientPeg.get().setDeviceBlocked(
this.props.userId, this.state.device.deviceId, true
this.props.userId, this.state.device.deviceId, true,
);
},
onUnblacklistClick: function() {
MatrixClientPeg.get().setDeviceBlocked(
this.props.userId, this.state.device.deviceId, false
this.props.userId, this.state.device.deviceId, false,
);
},
render: function() {
var blacklistButton = null, verifyButton = null;
let blacklistButton = null, verifyButton = null;
if (this.state.device.isBlocked()) {
blacklistButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unblacklist"
onClick={this.onUnblacklistClick}>
{_t("Unblacklist")}
{ _t("Unblacklist") }
</button>
);
} else {
blacklistButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_blacklist"
onClick={this.onBlacklistClick}>
{_t("Blacklist")}
{ _t("Blacklist") }
</button>
);
}
@ -99,14 +99,14 @@ export default React.createClass({
verifyButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_unverify"
onClick={this.onUnverifyClick}>
{_t("Unverify")}
{ _t("Unverify") }
</button>
);
} else {
verifyButton = (
<button className="mx_MemberDeviceInfo_textButton mx_MemberDeviceInfo_verify"
onClick={this.onVerifyClick}>
{_t("Verify...")}
{ _t("Verify...") }
</button>
);
}

View file

@ -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 }
<span className="mx_DirectorySearchBox_clear_wrapper">
<span className="mx_DirectorySearchBox_clear" onClick={this._onClearClick} />
</span>

View file

@ -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}
</div>
{ this.props.children }
</div>;
}
};
}
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 }
</MenuOption>
);
});
if (options.length === 0) {
return [<div key="0" className="mx_Dropdown_option">
{_t("No results")}
{ _t("No results") }
</div>];
}
return options;
@ -279,7 +287,7 @@ export default class Dropdown extends React.Component {
/>;
}
menu = <div className="mx_Dropdown_menu" style={menuStyle}>
{this._getMenuOptions()}
{ this._getMenuOptions() }
</div>;
}
@ -288,12 +296,13 @@ export default class Dropdown extends React.Component {
this.props.getShortOption(this.props.value) :
this.childrenByKey[this.props.value];
currentValue = <div className="mx_Dropdown_option">
{selectedChild}
</div>
{ selectedChild }
</div>;
}
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 <div className={classnames(dropdownClasses)} ref={this._collectRoot}>
<AccessibleButton className="mx_Dropdown_input" onClick={this._onInputClick}>
{currentValue}
{ currentValue }
<span className="mx_Dropdown_arrow"></span>
{menu}
{ menu }
</AccessibleButton>
</div>;
}
@ -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,
};

View file

@ -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 <div className="mx_EditableItem">
<EditableText
className="mx_EditableItem_editable"
placeholderClassName="mx_EditableItem_editablePlaceholder"
placeholder={this.props.placeholder}
blurToCancel={false}
editable={true}
initialValue={this.props.initialValue}
onValueChanged={this.onChange} />
{ this.props.onAdd ?
<div className="mx_EditableItem_addButton">
<img className="mx_filterFlipColor"
src="img/plus.svg" width="14" height="14"
alt={_t("Add")} onClick={this.onAdd} />
</div>
:
<div className="mx_EditableItem_removeButton">
<img className="mx_filterFlipColor"
src="img/cancel-small.svg" width="14" height="14"
alt={_t("Delete")} onClick={this.onRemove} />
</div>
}
</div>;
},
});
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 <EditableItem
key={index}
index={index}
initialValue={item}
onChange={this.onItemEdited}
onRemove={this.onItemRemoved}
placeholder={this.props.placeholder}
/>;
});
const label = this.props.items.length > 0 ?
this.props.itemsLabel : this.props.noItemsLabel;
return (<div className="mx_EditableItemList">
<div className="mx_EditableItemList_label">
{ label }
</div>
{ editableItems }
<EditableItem
key={-1}
initialValue={this.props.newItem}
onAdd={this.onItemAdded}
onChange={this.onNewItemChanged}
addOnChange={true}
placeholder={this.props.placeholder}
/>
</div>);
},
});

View file

@ -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;
}
},
});

View file

@ -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 (
<Loader />
);
} else if (this.state.errorString) {
return (
<div className="error">{this.state.errorString}</div>
<div className="error">{ this.state.errorString }</div>
);
} else {
var EditableText = sdk.getComponent('elements.EditableText');
const EditableText = sdk.getComponent('elements.EditableText');
return (
<EditableText initialValue={this.state.value}
placeholder={this.props.placeholder}

View file

@ -183,10 +183,12 @@ export default class Flair extends React.Component {
this.state = {
profiles: [],
};
this.onRoomStateEvents = this.onRoomStateEvents.bind(this);
}
componentWillUnmount() {
this._unmounted = true;
this.context.matrixClient.removeListener('RoomState.events', this.onRoomStateEvents);
}
componentWillMount() {
@ -194,6 +196,13 @@ export default class Flair extends React.Component {
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) {
@ -224,6 +233,21 @@ export default class Flair extends React.Component {
}
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;
}
@ -241,7 +265,7 @@ export default class Flair extends React.Component {
return <FlairAvatar key={index} groupProfile={profile} />;
});
return (
<span className="mx_Flair" style={{"marginLeft": "5px", "verticalAlign": "-3px"}}>
<span className="mx_Flair">
{ avatars }
</span>
);
@ -250,6 +274,12 @@ export default class Flair extends React.Component {
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

View file

@ -23,7 +23,7 @@ const HomeButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
<ActionButton action="view_home_page"
label={ _t("Home") }
label={_t("Home")}
iconPath="img/icons-home.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -35,12 +35,12 @@ export default class LanguageDropdown extends React.Component {
this.state = {
searchQuery: '',
langs: null,
}
};
}
componentWillMount() {
languageHandler.getAllLanguagesFromJson().then((langs) => {
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 <div key={language.value}>
{language.label}
{ language.label }
</div>;
});
@ -108,8 +108,8 @@ export default class LanguageDropdown extends React.Component {
onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange}
searchEnabled={true} value={value}
>
{options}
</Dropdown>
{ options }
</Dropdown>;
}
}

View file

@ -96,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,
);
});
@ -119,7 +119,7 @@ module.exports = React.createClass({
return (
<span className="mx_TextualEvent mx_MemberEventListSummary_summary">
<EmojiText>
{summaries.join(", ")}
{ summaries.join(", ") }
</EmojiText>
</span>
);
@ -370,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 "";
@ -394,8 +394,8 @@ module.exports = React.createClass({
);
});
return (
<span className="mx_MemberEventListSummary_avatars" onClick={ this._toggleSummary }>
{avatars}
<span className="mx_MemberEventListSummary_avatars" onClick={this._toggleSummary}>
{ avatars }
</span>
);
},
@ -419,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':
@ -483,7 +479,7 @@ module.exports = React.createClass({
firstEvent.index < aggregateIndices[seq]) {
aggregateIndices[seq] = firstEvent.index;
}
}
},
);
return {
@ -494,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;
@ -506,7 +502,7 @@ module.exports = React.createClass({
if (fewEvents) {
return (
<div className="mx_MemberEventListSummary" data-scroll-tokens={eventIds}>
{expandedEvents}
{ expandedEvents }
</div>
);
}
@ -542,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;
@ -550,24 +546,24 @@ module.exports = React.createClass({
summaryContainer = (
<div className="mx_EventTile_line">
<div className="mx_EventTile_info">
{this._renderAvatars(avatarMembers)}
{this._renderSummary(aggregate.names, orderedTransitionSequences)}
{ this._renderAvatars(avatarMembers) }
{ this._renderSummary(aggregate.names, orderedTransitionSequences) }
</div>
</div>
);
}
const toggleButton = (
<div className={"mx_MemberEventListSummary_toggle"} onClick={this._toggleSummary}>
{expanded ? 'collapse' : 'expand'}
{ expanded ? 'collapse' : 'expand' }
</div>
);
return (
<div className="mx_MemberEventListSummary" data-scroll-tokens={eventIds}>
{toggleButton}
{summaryContainer}
{expanded ? <div className="mx_MemberEventListSummary_line">&nbsp;</div> : null}
{expandedEvents}
{ toggleButton }
{ summaryContainer }
{ expanded ? <div className="mx_MemberEventListSummary_line">&nbsp;</div> : null }
{ expandedEvents }
</div>
);
},

View file

@ -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 = <span>{ this.props.value }</span>;
}
else {
input = <input ref="custom" type="text" size="3" defaultValue={ this.props.value } onBlur={ this.onCustomBlur } onKeyDown={ this.onCustomKeyDown }/>;
} else {
input = <input ref="custom" type="text" size="3" defaultValue={this.props.value} onBlur={this.onCustomBlur} onKeyDown={this.onCustomKeyDown} />;
}
customPicker = <span> of { input }</span>;
}
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 = <span>{ selectValue }</span>;
}
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 <option value={op.value} key={op.value}>{op.text}</option>;
return <option value={op.value} key={op.value}>{ op.text }</option>;
});
select =
<select ref="select"
value={ this.props.controlled ? selectValue : undefined }
defaultValue={ !this.props.controlled ? selectValue : undefined }
onChange={ this.onSelectChange }>
value={this.props.controlled ? selectValue : undefined}
defaultValue={!this.props.controlled ? selectValue : undefined}
onChange={this.onSelectChange}>
{ options }
</select>;
}
@ -137,5 +134,5 @@ module.exports = React.createClass({
{ customPicker }
</span>
);
}
},
});

View file

@ -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 (
<div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div>
);
}
},
});

View file

@ -24,7 +24,7 @@ const RoomDirectoryButton = function(props) {
return (
<ActionButton action="view_room_directory"
mouseOverAction={props.callout ? "callout_room_directory" : null}
label={ _t("Room directory") }
label={_t("Room directory")}
iconPath="img/icons-directory.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -23,7 +23,7 @@ const SettingsButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
<ActionButton action="view_user_settings"
label={ _t("Settings") }
label={_t("Settings")}
iconPath="img/icons-settings.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -24,7 +24,7 @@ const StartChatButton = function(props) {
return (
<ActionButton action="view_create_chat"
mouseOverAction={props.callout ? "callout_start_chat" : null}
label={ _t("Start chat") }
label={_t("Start chat")}
iconPath="img/icons-people.svg"
size={props.size}
tooltip={props.tooltip}

View file

@ -16,9 +16,9 @@ limitations under the License.
'use strict';
var React = require('react');
var ReactDOM = require("react-dom");
var Tinter = require("../../../Tinter");
const React = require('react');
const ReactDOM = require("react-dom");
const Tinter = require("../../../Tinter");
var TintableSvg = React.createClass({
displayName: 'TintableSvg',
@ -63,16 +63,16 @@ var TintableSvg = React.createClass({
render: function() {
return (
<object className={ "mx_TintableSvg " + (this.props.className ? this.props.className : "") }
<object className={"mx_TintableSvg " + (this.props.className ? this.props.className : "")}
type="image/svg+xml"
data={ this.props.src }
width={ this.props.width }
height={ this.props.height }
onLoad={ this.onLoad }
data={this.props.src}
width={this.props.width}
height={this.props.height}
onLoad={this.onLoad}
tabIndex="-1"
/>
);
}
},
});
// Register with the Tinter so that we will be told if the tint changes

View file

@ -52,19 +52,19 @@ module.exports = React.createClass({
},
render: function() {
var self = this;
const self = this;
return (
<div>
<ul className="mx_UserSelector_UserIdList" ref="list">
{this.props.selected_users.map(function(user_id, i) {
return <li key={user_id}>{user_id} - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
})}
{ this.props.selected_users.map(function(user_id, i) {
return <li key={user_id}>{ user_id } - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
}) }
</ul>
<input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")}/>
<input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")} />
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">
{_t("Add User")}
{ _t("Add User") }
</button>
</div>
);
}
},
});

View file

@ -17,6 +17,7 @@ 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';
@ -34,7 +35,6 @@ export default React.createClass({
getInitialState: function() {
return {
fetching: false,
rooms: null,
truncateAt: INITIAL_LOAD_NUM_ROOMS,
searchQuery: "",
@ -43,21 +43,29 @@ export default React.createClass({
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() {
this.setState({fetching: true});
this.context.matrixClient.getGroupRooms(this.props.groupId).then((result) => {
this.setState({
rooms: result.chunk.map((apiRoom) => {
return groupRoomFromApiObject(apiRoom);
}),
fetching: false,
});
}).catch((e) => {
this.setState({fetching: false});
console.error("Failed to get group room list: ", e);
if (this._unmounted) return;
this.setState({
rooms: this._groupStore.getGroupRooms().map((apiRoom) => {
return groupRoomFromApiObject(apiRoom);
}),
});
},
@ -110,12 +118,7 @@ export default React.createClass({
},
render: function() {
if (this.state.fetching) {
const Spinner = sdk.getComponent("elements.Spinner");
return (<div className="mx_GroupRoomList">
<Spinner />
</div>);
} else if (this.state.rooms === null) {
if (this.state.rooms === null) {
return null;
}

View file

@ -21,6 +21,8 @@ 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',
@ -31,7 +33,35 @@ const GroupRoomTile = React.createClass({
},
getInitialState: function() {
return {};
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) {
@ -49,20 +79,34 @@ const GroupRoomTile = React.createClass({
});
},
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 name = this.props.groupRoom.name ||
this.props.groupRoom.canonicalAlias ||
_t("Unnamed Room");
const avatarUrl = this.context.matrixClient.mxcUrlToHttp(
this.props.groupRoom.avatarUrl,
36, 36, 'crop',
);
const av = (
<BaseAvatar name={name}
<BaseAvatar name={this.state.name}
width={36} height={36}
url={avatarUrl}
/>
@ -74,8 +118,11 @@ const GroupRoomTile = React.createClass({
{ av }
</div>
<div className="mx_GroupRoomTile_name">
{ name }
{ this.state.name }
</div>
<AccessibleButton className="mx_GroupRoomTile_delete" onClick={this.onDeleteClick}>
<img src="img/cancel-small.svg" />
</AccessibleButton>
</AccessibleButton>
);
},

View file

@ -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>(.*?)<\/a>/,
(sub) => { return <a href='https://riot.im/app'>{ sub }</a>; }), 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 = (
<div className="error">
{this.state.errorText}
{ this.state.errorText }
</div>
);
}
return (
<div ref="recaptchaContainer">
{_t("This Home Server would like to make sure you are not a robot")}
<br/>
{ _t("This Home Server would like to make sure you are not a robot") }
<br />
<div id={DIV_ID}></div>
{error}
{ error }
</div>
);
}
},
});

View file

@ -29,9 +29,9 @@ module.exports = React.createClass({
render: function() {
return (
<div>
<button onClick={this.props.onSubmit}>{_t("Sign in with CAS")}</button>
<button onClick={this.props.onSubmit}>{ _t("Sign in with CAS") }</button>
</div>
);
}
},
});

View file

@ -69,7 +69,7 @@ export default class CountryDropdown extends React.Component {
}
_flagImgForIso2(iso2) {
return <img src={`flags/${iso2}.png`}/>;
return <img src={`flags/${iso2}.png`} />;
}
_getShortOption(iso2) {
@ -111,8 +111,8 @@ export default class CountryDropdown extends React.Component {
const options = displayedCountries.map((country) => {
return <div key={country.iso2}>
{this._flagImgForIso2(country.iso2)}
{country.name} <span>(+{country.prefix})</span>
{ this._flagImgForIso2(country.iso2) }
{ country.name } <span>(+{ country.prefix })</span>
</div>;
});
@ -123,9 +123,9 @@ export default class CountryDropdown extends React.Component {
return <Dropdown className={this.props.className + " left_aligned"}
onOptionChange={this._onOptionChange} onSearchChange={this._onSearchChange}
menuWidth={298} getShortOption={this._getShortOption}
value={value} searchEnabled={true}
value={value} searchEnabled={true} disabled={this.props.disabled}
>
{options}
{ options }
</Dropdown>;
}
}
@ -137,4 +137,5 @@ CountryDropdown.propTypes = {
showPrefix: React.PropTypes.bool,
onOptionChange: React.PropTypes.func.isRequired,
value: React.PropTypes.string,
disabled: React.PropTypes.bool,
};

View file

@ -24,27 +24,27 @@ module.exports = React.createClass({
return (
<div className="mx_ErrorDialog">
<div className="mx_Dialog_title">
{_t("Custom Server Options")}
{ _t("Custom Server Options") }
</div>
<div className="mx_Dialog_content">
<span>
{_t("You can use the custom server options to sign into other Matrix " +
"servers by specifying a different Home server URL.")}
<br/>
{_t("This allows you to use this app with an existing Matrix account on " +
"a different home server.")}
<br/>
<br/>
{_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.") }
<br />
{ _t("This allows you to use this app with an existing Matrix account on " +
"a different home server.") }
<br />
<br />
{ _t("You can also set a custom identity server but this will typically prevent " +
"interaction with users based on email address.") }
</span>
</div>
<div className="mx_Dialog_buttons">
<button onClick={this.props.onFinished} autoFocus={true}>
{_t("Dismiss")}
{ _t("Dismiss") }
</button>
</div>
</div>
);
}
},
});

View file

@ -129,8 +129,8 @@ export const PasswordAuthEntry = React.createClass({
return (
<div>
<p>{_t("To continue, please enter your password.")}</p>
<p>{_t("Password:")}</p>
<p>{ _t("To continue, please enter your password.") }</p>
<p>{ _t("Password:") }</p>
<form onSubmit={this._onSubmit}>
<input
ref="passwordField"
@ -139,11 +139,11 @@ export const PasswordAuthEntry = React.createClass({
type="password"
/>
<div className="mx_button_row">
{submitButtonOrSpinner}
{ submitButtonOrSpinner }
</div>
</form>
<div className="error">
{this.props.errorText}
{ this.props.errorText }
</div>
</div>
);
@ -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 (
<div>
<CaptchaForm sitePublicKey={sitePublicKey}
onCaptchaResponse={this._onCaptchaResponse}
/>
<div className="error">
{this.props.errorText}
{ this.props.errorText }
</div>
</div>
);
@ -256,8 +256,8 @@ export const EmailIdentityAuthEntry = React.createClass({
} else {
return (
<div>
<p>{_t("An email has been sent to")} <i>{this.props.inputs.emailAddress}</i></p>
<p>{_t("Please check your email to continue registration.")}</p>
<p>{ _t("An email has been sent to") } <i>{ this.props.inputs.emailAddress }</i></p>
<p>{ _t("Please check your email to continue registration.") }</p>
</div>
);
}
@ -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 (
<div>
<p>{_t("A text message has been sent to")} +<i>{this._msisdn}</i></p>
<p>{_t("Please enter the code it contains:")}</p>
<p>{ _t("A text message has been sent to") } +<i>{ this._msisdn }</i></p>
<p>{ _t("Please enter the code it contains:") }</p>
<div className="mx_InteractiveAuthEntryComponents_msisdnWrapper">
<form onSubmit={this._onFormSubmit}>
<input type="text"
@ -386,7 +386,7 @@ export const MsisdnAuthEntry = React.createClass({
/>
</form>
<div className="error">
{this.state.errorText}
{ this.state.errorText }
</div>
</div>
</div>
@ -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 (
<div>
<a onClick={this._onShowFallbackClick}>{_t("Start authentication")}</a>
<a onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
<div className="error">
{this.props.errorText}
{ this.props.errorText }
</div>
</div>
);
@ -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;
}

View file

@ -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
</div>
);
}
},
});

View file

@ -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 <input
className="mx_Login_field mx_Login_email"
className={classNames(classes)}
key="email_input"
type="text"
name="username" // make it a little easier for browser's remember-password
@ -128,10 +134,12 @@ class PasswordLogin extends React.Component {
placeholder="joe@example.com"
value={this.state.username}
autoFocus
disabled={disabled}
/>;
case PasswordLogin.LOGIN_FIELD_MXID:
classes.mx_Login_username = true;
return <input
className="mx_Login_field mx_Login_username"
className={classNames(classes)}
key="username_input"
type="text"
name="username" // make it a little easier for browser's remember-password
@ -139,9 +147,12 @@ class PasswordLogin extends React.Component {
placeholder={_t('User name')}
value={this.state.username}
autoFocus
disabled={disabled}
/>;
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 <div className="mx_Login_phoneSection">
<CountryDropdown
className="mx_Login_phoneCountry mx_Login_field_prefix"
@ -150,9 +161,10 @@ class PasswordLogin extends React.Component {
value={this.state.phoneCountry}
isSmall={true}
showPrefix={true}
disabled={disabled}
/>
<input
className="mx_Login_phoneNumberField mx_Login_field mx_Login_field_has_prefix"
className={classNames(classes)}
ref="phoneNumber"
key="phone_input"
type="text"
@ -161,13 +173,14 @@ class PasswordLogin extends React.Component {
placeholder={_t("Mobile phone number")}
value={this.state.phoneNumber}
autoFocus
disabled={disabled}
/>
</div>;
}
}
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 (
<div>
@ -194,20 +218,23 @@ class PasswordLogin extends React.Component {
<Dropdown
className="mx_Login_type_dropdown"
value={this.state.loginType}
disabled={matrixIdText === ''}
onOptionChange={this.onLoginTypeChange}>
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ _t('my Matrix ID') }</span>
<span key={PasswordLogin.LOGIN_FIELD_MXID}>{ matrixIdText }</span>
<span key={PasswordLogin.LOGIN_FIELD_EMAIL}>{ _t('Email address') }</span>
<span key={PasswordLogin.LOGIN_FIELD_PHONE}>{ _t('Phone') }</span>
</Dropdown>
</div>
{loginField}
{ loginField }
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
name="password"
value={this.state.password} onChange={this.onPasswordChanged}
placeholder={ _t('Password') } />
placeholder={_t('Password')}
disabled={matrixIdText === ''}
/>
<br />
{forgotPasswordJsx}
<input className="mx_Login_submit" type="submit" value={ _t('Sign in') } />
{ forgotPasswordJsx }
<input className="mx_Login_submit" type="submit" value={_t('Sign in')} disabled={matrixIdText === ''} />
</form>
</div>
);

View file

@ -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:
<div>
{_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?") }
</div>,
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 = (
<div>
@ -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} />
</div>
);
let belowEmailSection;
@ -291,7 +291,7 @@ module.exports = React.createClass({
Sorry, but your university is not registered with us just yet.&nbsp;
Email us on&nbsp;
<a href={"mailto:" + this.props.teamsConfig.supportEmail}>
{this.props.teamsConfig.supportEmail}
{ this.props.teamsConfig.supportEmail }
</a>&nbsp;
to get your university signed up. Or continue to register with Riot to enjoy our open source platform.
</p>
@ -299,7 +299,7 @@ module.exports = React.createClass({
} else if (this.state.selectedTeam) {
belowEmailSection = (
<p className="mx_Login_support">
{_t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name})}
{ _t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name}) }
</p>
);
}
@ -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({
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
);
let placeholderUserName = _t("User name");
const placeholderUserName = _t("User name");
return (
<div>
<form onSubmit={this.onSubmit}>
{emailSection}
{belowEmailSection}
{phoneSection}
{ emailSection }
{ belowEmailSection }
{ phoneSection }
<input type="text" ref="username"
placeholder={ placeholderUserName } defaultValue={this.props.defaultUsername}
placeholder={placeholderUserName} defaultValue={this.props.defaultUsername}
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_USERNAME);}} />
<br />
@ -357,9 +357,9 @@ module.exports = React.createClass({
onBlur={function() {self.validateField(FIELD_PASSWORD_CONFIRM);}}
defaultValue={this.props.defaultPassword} />
<br />
{registerButton}
{ registerButton }
</form>
</div>
);
}
},
});

View file

@ -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 = (
<div className="mx_ServerConfig_selector">
@ -137,14 +136,14 @@ module.exports = React.createClass({
checked={!this.state.configVisible}
onChange={this.onServerConfigVisibleChange.bind(this, false)} />
<label className="mx_Login_label" htmlFor="basic">
{_t("Default server")}
{ _t("Default server") }
</label>
&nbsp;&nbsp;
<input className="mx_Login_radio" id="advanced" name="configVisible" type="radio"
checked={this.state.configVisible}
onChange={this.onServerConfigVisibleChange.bind(this, true)} />
<label className="mx_Login_label" htmlFor="advanced">
{_t("Custom server")}
{ _t("Custom server") }
</label>
</div>
);
@ -152,11 +151,11 @@ module.exports = React.createClass({
return (
<div>
{toggleButton}
{ toggleButton }
<div style={serverConfigStyle}>
<div className="mx_ServerConfig">
<label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">
{_t("Home server URL")}
{ _t("Home server URL") }
</label>
<input className="mx_Login_field" id="hsurl" type="text"
placeholder={this.props.defaultHsUrl}
@ -164,7 +163,7 @@ module.exports = React.createClass({
value={this.state.hs_url}
onChange={this.onHomeserverChanged} />
<label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">
{_t("Identity server URL")}
{ _t("Identity server URL") }
</label>
<input className="mx_Login_field" id="isurl" type="text"
placeholder={this.props.defaultIsUrl}
@ -172,11 +171,11 @@ module.exports = React.createClass({
value={this.state.is_url}
onChange={this.onIdentityServerChanged} />
<a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
{_t("What does this mean?")}
{ _t("What does this mean?") }
</a>
</div>
</div>
</div>
);
}
},
});

View file

@ -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 (
<span className="mx_MAudioBody" ref="body">
<img src="img/warning.svg" width="16" height="16"/>
{_t("Error decrypting audio")}
<img src="img/warning.svg" width="16" height="16" />
{ _t("Error decrypting audio") }
</span>
);
}
@ -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 (
<span className="mx_MAudioBody">
<img src="img/spinner.gif" alt={content.body} width="16" height="16"/>
<img src="img/spinner.gif" alt={content.body} width="16" height="16" />
</span>
);
}

View file

@ -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({
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<div style={{display: "none"}}>
{/*
{ /*
* 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.
*/}
<a ref="dummyLink"/>
*/ }
<a ref="dummyLink" />
</div>
<iframe src={renderer_url} onLoad={onIframeLoad} ref="iframe"/>
<iframe src={renderer_url} onLoad={onIframeLoad} ref="iframe" />
</div>
</span>
);
@ -356,13 +356,12 @@ module.exports = React.createClass({
</div>
</span>
);
}
else {
} else {
return (
<span className="mx_MFileBody">
<div className="mx_MImageBody_download">
<a href={contentUrl} download={fileName} target="_blank" rel="noopener">
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage"/>
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage" />
{ _t("Download %(text)s", { text: text }) }
</a>
</div>
@ -370,7 +369,7 @@ module.exports = React.createClass({
);
}
} else {
var extra = text ? (': ' + text) : '';
const extra = text ? (': ' + text) : '';
return <span className="mx_MFileBody">
{ _t("Invalid file%(extra)s", { extra: extra }) }
</span>;

View file

@ -191,8 +191,8 @@ module.exports = React.createClass({
if (this.state.error !== null) {
return (
<span className="mx_MImageBody" ref="body">
<img src="img/warning.svg" width="16" height="16"/>
{_t("Error decrypting image")}
<img src="img/warning.svg" width="16" height="16" />
{ _t("Error decrypting image") }
</span>
);
}
@ -210,7 +210,7 @@ module.exports = React.createClass({
}}>
<img src="img/spinner.gif" alt={content.body} width="32" height="32" style={{
"margin": "auto",
}}/>
}} />
</div>
</span>
);
@ -227,7 +227,7 @@ module.exports = React.createClass({
if (thumbUrl) {
return (
<span className="mx_MImageBody" ref="body">
<a href={contentUrl} onClick={ this.onClick }>
<a href={contentUrl} onClick={this.onClick}>
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
alt={content.body}
onMouseEnter={this.onImageEnter}
@ -239,13 +239,13 @@ module.exports = React.createClass({
} else if (content.body) {
return (
<span className="mx_MImageBody">
{_t("Image '%(Body)s' cannot be displayed.", {Body: content.body})}
{ _t("Image '%(Body)s' cannot be displayed.", {Body: content.body}) }
</span>
);
} else {
return (
<span className="mx_MImageBody">
{_t("This image cannot be displayed.")}
{ _t("This image cannot be displayed.") }
</span>
);
}

View file

@ -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 (
<span className="mx_MVideoBody" ref="body">
<img src="img/warning.svg" width="16" height="16"/>
{_t("Error decrypting video")}
<img src="img/warning.svg" width="16" height="16" />
{ _t("Error decrypting video") }
</span>
);
}
@ -144,7 +143,7 @@ module.exports = React.createClass({
"justify-items": "center",
"width": "100%",
}}>
<img src="img/spinner.gif" alt={content.body} width="16" height="16"/>
<img src="img/spinner.gif" alt={content.body} width="16" height="16" />
</div>
</span>
);

View file

@ -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) {

View file

@ -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) =>
<AccessibleButton key="avatar" className="mx_RoomAvatarEvent_avatar"
onClick={ this.onAvatarClick.bind(this, name) }>
<BaseAvatar width={14} height={14} url={ url }
name={ name } />
onClick={this.onAvatarClick.bind(this, name)}>
<BaseAvatar width={14} height={14} url={url}
name={name} />
</AccessibleButton>,
]
],
)
}
</div>

View file

@ -33,7 +33,13 @@ export default function SenderProfile(props) {
return (
<div className="mx_SenderProfile" dir="auto" onClick={props.onClick}>
<EmojiText className="mx_SenderProfile_name">{ name || '' }</EmojiText>
{ props.enableFlair ? <Flair userId={mxEvent.getSender()} /> : null }
{ props.enableFlair ?
<Flair
userId={mxEvent.getSender()}
roomId={mxEvent.getRoomId()}
showRelated={true} />
: null
}
{ props.aux ? <EmojiText className="mx_SenderProfile_aux"> { props.aux }</EmojiText> : null }
</div>
);

View file

@ -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:
<div>
{_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 }) }
</div>,
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,29 @@ 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, {
disableBigEmoji: UserSettingsStore.getSyncedSetting('TextualBody.disableBigEmoji', false),
});
if (this.props.highlightLink) {
body = <a href={ this.props.highlightLink }>{ body }</a>;
}
else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
body = <a href="#" onClick={ this.onStarterLinkClick.bind(this, content.data["org.matrix.neb.starter_link"]) }>{ body }</a>;
body = <a href={this.props.highlightLink}>{ body }</a>;
} else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
body = <a href="#" onClick={this.onStarterLinkClick.bind(this, content.data["org.matrix.neb.starter_link"])}>{ body }</a>;
}
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 <LinkPreviewWidget
key={ link }
link={ link }
mxEvent={ this.props.mxEvent }
onCancelClick={ this.onCancelClick }
onWidgetLoad={ this.props.onWidgetLoad }/>;
key={link}
link={link}
mxEvent={this.props.mxEvent}
onCancelClick={this.onCancelClick}
onWidgetLoad={this.props.onWidgetLoad} />;
});
}
@ -393,7 +387,7 @@ module.exports = React.createClass({
className="mx_MEmoteBody_sender"
onClick={this.onEmoteSenderClick}
>
{name}
{ name }
</EmojiText>
&nbsp;
{ body }

View file

@ -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 (
<EmojiText element="div" className="mx_TextualEvent">{text}</EmojiText>
<EmojiText element="div" className="mx_TextualEvent">{ text }</EmojiText>
);
},
});

View file

@ -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 = (
<select onChange={this.onCanonicalAliasChange} defaultValue={ this.state.canonicalAlias }>
<select onChange={this.onCanonicalAliasChange} defaultValue={this.state.canonicalAlias}>
<option value="" key="unset">{ _t('not specified') }</option>
{
Object.keys(self.state.domainToAliases).map(function(domain, i) {
return self.state.domainToAliases[domain].map(function(alias, j) {
return (
<option value={ alias } key={ i + "_" + j }>
<option value={alias} key={i + "_" + j}>
{ alias }
</option>
);
@ -218,34 +219,33 @@ module.exports = React.createClass({
}
</select>
);
}
else {
} else {
canonical_alias_section = (
<b>{ this.state.canonicalAlias || _t('not set') }</b>
);
}
var remote_aliases_section;
let remote_aliases_section;
if (this.state.remoteDomains.length) {
remote_aliases_section = (
<div>
<div className="mx_RoomSettings_aliasLabel">
{_t("Remote addresses for this room:")}
{ _t("Remote addresses for this room:") }
</div>
<div className="mx_RoomSettings_aliasesTable">
{ this.state.remoteDomains.map((domain, i) => {
return this.state.domainToAliases[domain].map(function(alias, j) {
return (
<div className="mx_RoomSettings_aliasesTableRow" key={ i + "_" + j }>
<div className="mx_RoomSettings_aliasesTableRow" key={i + "_" + j}>
<EditableText
className="mx_RoomSettings_alias mx_RoomSettings_editable"
blurToCancel={ false }
editable={ false }
initialValue={ alias } />
blurToCancel={false}
editable={false}
initialValue={alias} />
</div>
);
});
})}
}) }
</div>
</div>
);
@ -257,58 +257,24 @@ module.exports = React.createClass({
<div className="mx_RoomSettings_aliasLabel">
{ _t('The main address for this room is') }: { canonical_alias_section }
</div>
<div className="mx_RoomSettings_aliasLabel">
{ (this.state.domainToAliases[localDomain] &&
this.state.domainToAliases[localDomain].length > 0)
? _t('Local addresses for this room:')
: _t('This room has no local addresses') }
</div>
<div className="mx_RoomSettings_aliasesTable">
{ (this.state.domainToAliases[localDomain] || []).map((alias, i) => {
var deleteButton;
if (this.props.canSetAliases) {
deleteButton = (
<img src="img/cancel-small.svg" width="14" height="14"
alt={ _t('Delete') } onClick={ self.onAliasDeleted.bind(self, localDomain, i) } />
);
}
return (
<div className="mx_RoomSettings_aliasesTableRow" key={ i }>
<EditableText
className="mx_RoomSettings_alias mx_RoomSettings_editable"
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
placeholder={ _t('New address (e.g. #foo:%(localDomain)s)', { localDomain: localDomain}) }
blurToCancel={ false }
onValueChanged={ self.onAliasChanged.bind(self, localDomain, i) }
editable={ self.props.canSetAliases }
initialValue={ alias } />
<div className="mx_RoomSettings_deleteAlias mx_filterFlipColor">
{ deleteButton }
</div>
</div>
);
})}
{ this.props.canSetAliases ?
<div className="mx_RoomSettings_aliasesTableRow" key="new">
<EditableText
ref="add_alias"
className="mx_RoomSettings_alias mx_RoomSettings_editable"
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
placeholder={ _t('New address (e.g. #foo:%(localDomain)s)', { localDomain: localDomain}) }
blurToCancel={ false }
onValueChanged={ self.onAliasAdded } />
<div className="mx_RoomSettings_addAlias mx_filterFlipColor">
<img src="img/plus.svg" width="14" height="14" alt="Add"
onClick={ self.onAliasAdded.bind(self, undefined) }/>
</div>
</div> : ""
}
</div>
<EditableItemList
className={"mx_RoomSettings_localAliases"}
items={this.state.domainToAliases[localDomain] || []}
newItem={this.state.newAlias}
onNewItemChanged={this.onNewAliasChanged}
onItemAdded={this.onLocalAliasAdded}
onItemEdited={this.onLocalAliasChanged}
onItemRemoved={this.onLocalAliasDeleted}
itemsLabel={_t('Local addresses for this room:')}
noItemsLabel={_t('This room has no local addresses')}
placeholder={_t(
'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain},
)}
/>
{ remote_aliases_section }
</div>
);
}
},
});

View file

@ -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 (
<div className="mx_RoomSettings_roomColors">
{ROOM_COLORS.map((room_color, i) => {
var selected;
{ ROOM_COLORS.map((room_color, i) => {
let selected;
if (i === this.state.index) {
selected = (
<div className="mx_RoomSettings_roomColor_selected">
<img src="img/tick.svg" width="17" height="14" alt="./"/>
<img src="img/tick.svg" width="17" height="14" alt="./" />
</div>
);
}
var boundClick = this._onColorSchemeChanged.bind(this, i);
const boundClick = this._onColorSchemeChanged.bind(this, i);
return (
<div className="mx_RoomSettings_roomColor"
key={ "room_color_" + i }
key={"room_color_" + i}
style={{ backgroundColor: room_color[1] }}
onClick={ boundClick }>
onClick={boundClick}>
{ selected }
<div className="mx_RoomSettings_roomColorPrimary" style={{ backgroundColor: room_color[0] }}></div>
</div>
);
})}
}) }
</div>
);
}
},
});

View file

@ -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 (<div>
<h3>{ _t('Related Groups') }</h3>
<EditableItemList
items={this.state.newGroupsList}
className={"mx_RelatedGroupSettings"}
newItem={this.state.newGroupId}
onNewItemChanged={this.onNewGroupChanged}
onItemAdded={this.onGroupAdded}
onItemEdited={this.onGroupEdited}
onItemRemoved={this.onGroupDeleted}
itemsLabel={_t('Related groups for this room:')}
noItemsLabel={_t('This room has no related groups')}
placeholder={_t(
'New group ID (e.g. +foo:%(localDomain)s)', {localDomain},
)}
/>
</div>);
},
});

View file

@ -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 =
<label>
<input type="checkbox" ref="globalDisableUrlPreview"
onChange={ this.onGlobalDisableUrlPreviewChange }
checked={ this.state.globalDisableUrlPreview } />
{_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") }
</label>;
}
else {
} else {
disableRoomPreviewUrls =
<label>
{_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")}) }
</label>;
}
let urlPreviewText = null;
if (UserSettingsStore.getUrlPreviewsDisabled()) {
urlPreviewText = (
_tJsx("You have <a>disabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{sub}</a>)
_tJsx("You have <a>disabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
);
}
else {
} else {
urlPreviewText = (
_tJsx("You have <a>enabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{sub}</a>)
_tJsx("You have <a>enabled</a> URL previews by default.", /<a>(.*?)<\/a>/, (sub)=><a href="#/settings">{ sub }</a>)
);
}
return (
<div className="mx_RoomSettings_toggles">
<h3>{_t("URL Previews")}</h3>
<h3>{ _t("URL Previews") }</h3>
<label>
{urlPreviewText}
{ urlPreviewText }
</label>
{ disableRoomPreviewUrls }
<label>
<input type="checkbox" ref="userEnableUrlPreview"
onChange={ this.onUserEnableUrlPreviewChange }
checked={ this.state.userEnableUrlPreview } />
{_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)") }
</label>
<label>
<input type="checkbox" ref="userDisableUrlPreview"
onChange={ this.onUserDisableUrlPreviewChange }
checked={ this.state.userDisableUrlPreview } />
{_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)") }
</label>
</div>
);
}
},
});

View file

@ -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 ? (
<div key={i} className="mx_Autocomplete_ProviderSection">
<EmojiText element="div" className="mx_Autocomplete_provider_name">{completionResult.provider.getName()}</EmojiText>
{completionResult.provider.renderCompletions(completions)}
<EmojiText element="div" className="mx_Autocomplete_provider_name">{ completionResult.provider.getName() }</EmojiText>
{ completionResult.provider.renderCompletions(completions) }
</div>
) : null;
}).filter((completion) => !!completion);
return !this.state.hide && renderedCompletions.length > 0 ? (
<div className="mx_Autocomplete" ref={(e) => this.container = e}>
{renderedCompletions}
{ renderedCompletions }
</div>
) : null;
}

View file

@ -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.
@ -83,9 +84,9 @@ module.exports = React.createClass({
<div className="mx_RoomView_fileDropTarget">
<div className="mx_RoomView_fileDropTargetLabel"
title={_t("Drop File Here")}>
<TintableSvg src="img/upload-big.svg" width="45" height="59"/>
<br/>
{_t("Drop file here to upload")}
<TintableSvg src="img/upload-big.svg" width="45" height="59" />
<br />
{ _t("Drop file here to upload") }
</div>
</div>
);
@ -99,23 +100,23 @@ module.exports = React.createClass({
supportedText = _t(" (unsupported)");
} else {
joinNode = (<span>
{_tJsx(
{ _tJsx(
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.",
[/<voiceText>(.*?)<\/voiceText>/, /<videoText>(.*?)<\/videoText>/],
[
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub}</a>,
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'video');}} href="#">{sub}</a>,
]
)}
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'voice');}} href="#">{ sub }</a>,
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'video');}} href="#">{ sub }</a>,
],
) }
</span>);
}
// 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 = (
<div className="mx_RoomView_ongoingConfCallNotification">
{_t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText})}
{ _t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText}) }
&nbsp;
{joinNode}
{ joinNode }
</div>
);
}
@ -128,15 +129,12 @@ module.exports = React.createClass({
/>
);
let appsDrawer = null;
if(UserSettingsStore.isFeatureEnabled('matrix_apps')) {
appsDrawer = <AppsDrawer ref="appsDrawer"
room={this.props.room}
userId={this.props.userId}
maxHeight={this.props.maxHeight}
showApps={this.props.showApps}
/>;
}
const appsDrawer = <AppsDrawer ref="appsDrawer"
room={this.props.room}
userId={this.props.userId}
maxHeight={this.props.maxHeight}
showApps={this.props.showApps}
/>;
return (
<div className="mx_RoomView_auxPanel" style={{maxHeight: this.props.maxHeight}} >

View file

@ -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 = (
<div className="mx_EntityTile_details">
<img className="mx_EntityTile_chevron" src="img/member_chevron.png" width="8" height="12"/>
<EmojiText element="div" className="mx_EntityTile_name_hover" dir="auto">{name}</EmojiText>
<PresenceLabel activeAgo={ activeAgo }
<img className="mx_EntityTile_chevron" src="img/member_chevron.png" width="8" height="12" />
<EmojiText element="div" className="mx_EntityTile_name_hover" dir="auto">{ name }</EmojiText>
<PresenceLabel activeAgo={activeAgo}
currentlyActive={this.props.presenceCurrentlyActive}
presenceState={this.props.presenceState} />
</div>
);
}
else {
} else {
nameEl = (
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">{name}</EmojiText>
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">{ name }</EmojiText>
);
}
var inviteButton;
let inviteButton;
if (this.props.showInviteButton) {
inviteButton = (
<div className="mx_EntityTile_invite">
@ -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 = <img src="img/mod.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Moderator")}/>;
power = <img src="img/mod.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Moderator")} />;
}
if (powerLevel >= 99) {
power = <img src="img/admin.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Admin")}/>;
power = <img src="img/admin.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Admin")} />;
}
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 || <BaseAvatar name={this.props.name} width={36} height={36} />;
const av = this.props.avatarJsx || <BaseAvatar name={this.props.name} width={36} height={36} />;
return (
<AccessibleButton className={mainClassName} title={ this.props.title }
onClick={ this.props.onClick } onMouseEnter={ this.mouseEnter }
onMouseLeave={ this.mouseLeave }>
<AccessibleButton className={mainClassName} title={this.props.title}
onClick={this.props.onClick} onMouseEnter={this.mouseEnter}
onMouseLeave={this.mouseLeave}>
<div className="mx_EntityTile_avatar">
{ av }
{ power }
@ -165,5 +164,5 @@ module.exports = React.createClass({
{ inviteButton }
</AccessibleButton>
);
}
},
});

View file

@ -17,39 +17,39 @@ 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',
'm.room.pinned_events' : '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:
//
@ -178,7 +178,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);
@ -205,20 +205,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;
@ -226,8 +226,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;
}
@ -239,7 +239,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;
}
@ -254,12 +254,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;
}
@ -267,13 +266,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,
@ -282,19 +281,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 (<span className="mx_EventTile_readAvatars"></span>);
@ -305,11 +303,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;
}
@ -320,7 +318,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) {
@ -341,12 +339,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 = <span className="mx_EventTile_readAvatarRemainder"
onClick={this.toggleAllReadAvatars}
@ -370,7 +368,7 @@ module.exports = withMatrixClient(React.createClass({
},
onCryptoClicked: function(e) {
var event = this.props.mxEvent;
const event = this.props.mxEvent;
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '', (cb) => {
require(['../../../async-components/views/dialogs/EncryptedEventDialog'], cb);
@ -397,12 +395,12 @@ module.exports = withMatrixClient(React.createClass({
if (ev.getContent().msgtype === 'm.bad.encrypted') {
return <E2ePadlockUndecryptable {...props}/>;
return <E2ePadlockUndecryptable {...props} />;
} else if (ev.isEncrypted()) {
if (this.state.verified) {
return <E2ePadlockVerified {...props}/>;
return <E2ePadlockVerified {...props} />;
} else {
return <E2ePadlockUnverified {...props}/>;
return <E2ePadlockUnverified {...props} />;
}
} else {
// XXX: if the event is being encrypted (ie eventSendStatus ===
@ -413,7 +411,7 @@ module.exports = withMatrixClient(React.createClass({
// open padlock
const e2eEnabled = this.props.matrixClient.isRoomEncrypted(ev.getRoomId());
if (e2eEnabled) {
return <E2ePadlockUnencrypted {...props}/>;
return <E2ePadlockUnencrypted {...props} />;
}
}
@ -422,27 +420,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({
@ -469,9 +467,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;
@ -509,15 +507,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 = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} enableFlair={!aux} aux={aux} />;
}
else {
sender = <SenderProfile onClick={this.onSenderProfileClick} mxEvent={this.props.mxEvent} enableFlair={!aux} aux={aux} />;
} else {
sender = <SenderProfile mxEvent={this.props.mxEvent} enableFlair={true} />;
}
}
const editButton = (
<span className="mx_EventTile_editButton" title={ _t("Options") } onClick={this.onEditClicked} />
<span className="mx_EventTile_editButton" title={_t("Options")} onClick={this.onEditClicked} />
);
const timestamp = this.props.mxEvent.getTs() ?
@ -528,13 +525,13 @@ module.exports = withMatrixClient(React.createClass({
return (
<div className={classes}>
<div className="mx_EventTile_roomName">
<a href={ permalink } onClick={this.onPermalinkClicked}>
<a href={permalink} onClick={this.onPermalinkClicked}>
{ room ? room.name : '' }
</a>
</div>
<div className="mx_EventTile_senderDetails">
{ avatar }
<a href={ permalink } onClick={this.onPermalinkClicked}>
<a href={permalink} onClick={this.onPermalinkClicked}>
{ sender }
{ timestamp }
</a>
@ -549,8 +546,7 @@ module.exports = withMatrixClient(React.createClass({
</div>
</div>
);
}
else if (this.props.tileShape === "file_grid") {
} else if (this.props.tileShape === "file_grid") {
return (
<div className={classes}>
<div className="mx_EventTile_line" >
@ -564,7 +560,7 @@ module.exports = withMatrixClient(React.createClass({
</div>
<a
className="mx_EventTile_senderDetailsLink"
href={ permalink }
href={permalink}
onClick={this.onPermalinkClicked}
>
<div className="mx_EventTile_senderDetails">
@ -574,8 +570,7 @@ module.exports = withMatrixClient(React.createClass({
</a>
</div>
);
}
else {
} else {
return (
<div className={classes}>
<div className="mx_EventTile_msgOption">
@ -584,7 +579,7 @@ module.exports = withMatrixClient(React.createClass({
{ avatar }
{ sender }
<div className="mx_EventTile_line">
<a href={ permalink } onClick={this.onPermalinkClicked}>
<a href={permalink} onClick={this.onPermalinkClicked}>
{ timestamp }
</a>
{ this._renderE2EPadlock() }

View file

@ -16,16 +16,16 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
var sdk = require('../../../index');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var ImageUtils = require('../../../ImageUtils');
var Modal = require('../../../Modal');
const sdk = require('../../../index');
const MatrixClientPeg = require('../../../MatrixClientPeg');
const ImageUtils = require('../../../ImageUtils');
const Modal = require('../../../Modal');
var linkify = require('linkifyjs');
var linkifyElement = require('linkifyjs/element');
var linkifyMatrix = require('../../../linkify-matrix');
const linkify = require('linkifyjs');
const linkifyElement = require('linkifyjs/element');
const linkifyMatrix = require('../../../linkify-matrix');
linkifyMatrix(linkify);
module.exports = React.createClass({
@ -40,7 +40,7 @@ module.exports = React.createClass({
getInitialState: function() {
return {
preview: null
preview: null,
};
},
@ -52,7 +52,7 @@ module.exports = React.createClass({
}
this.setState(
{ preview: res },
this.props.onWidgetLoad
this.props.onWidgetLoad,
);
}, (error)=>{
console.error("Failed to get preview for " + this.props.link + " " + error);
@ -76,17 +76,17 @@ module.exports = React.createClass({
},
onImageClick: function(ev) {
var p = this.state.preview;
const p = this.state.preview;
if (ev.button != 0 || ev.metaKey) return;
ev.preventDefault();
var ImageView = sdk.getComponent("elements.ImageView");
const ImageView = sdk.getComponent("elements.ImageView");
var src = p["og:image"];
let src = p["og:image"];
if (src && src.startsWith("mxc://")) {
src = MatrixClientPeg.get().mxcUrlToHttp(src);
}
var params = {
const params = {
src: src,
width: p["og:image:width"],
height: p["og:image:height"],
@ -99,27 +99,27 @@ module.exports = React.createClass({
},
render: function() {
var p = this.state.preview;
const p = this.state.preview;
if (!p || Object.keys(p).length === 0) {
return <div/>;
return <div />;
}
// FIXME: do we want to factor out all image displaying between this and MImageBody - especially for lightboxing?
var image = p["og:image"];
var imageMaxWidth = 100, imageMaxHeight = 100;
let image = p["og:image"];
let imageMaxWidth = 100, imageMaxHeight = 100;
if (image && image.startsWith("mxc://")) {
image = MatrixClientPeg.get().mxcUrlToHttp(image, imageMaxWidth, imageMaxHeight);
}
var thumbHeight = imageMaxHeight;
let thumbHeight = imageMaxHeight;
if (p["og:image:width"] && p["og:image:height"]) {
thumbHeight = ImageUtils.thumbHeight(p["og:image:width"], p["og:image:height"], imageMaxWidth, imageMaxHeight);
}
var img;
let img;
if (image) {
img = <div className="mx_LinkPreviewWidget_image" style={{ height: thumbHeight }}>
<img style={{ maxWidth: imageMaxWidth, maxHeight: imageMaxHeight }} src={ image } onClick={ this.onImageClick }/>
<img style={{ maxWidth: imageMaxWidth, maxHeight: imageMaxHeight }} src={image} onClick={this.onImageClick} />
</div>;
}
@ -127,15 +127,15 @@ module.exports = React.createClass({
<div className="mx_LinkPreviewWidget" >
{ img }
<div className="mx_LinkPreviewWidget_caption">
<div className="mx_LinkPreviewWidget_title"><a href={ this.props.link } target="_blank" rel="noopener">{ p["og:title"] }</a></div>
<div className="mx_LinkPreviewWidget_title"><a href={this.props.link} target="_blank" rel="noopener">{ p["og:title"] }</a></div>
<div className="mx_LinkPreviewWidget_siteName">{ p["og:site_name"] ? (" - " + p["og:site_name"]) : null }</div>
<div className="mx_LinkPreviewWidget_description" ref="description">
{ p["og:description"] }
</div>
</div>
<img className="mx_LinkPreviewWidget_cancel" src="img/cancel.svg" width="18" height="18"
onClick={ this.props.onCancelClick }/>
onClick={this.props.onCancelClick} />
</div>
);
}
},
});

View file

@ -20,30 +20,30 @@ import { _t } from '../../../languageHandler';
export default class MemberDeviceInfo extends React.Component {
render() {
var indicator = null;
var DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons');
let indicator = null;
const DeviceVerifyButtons = sdk.getComponent('elements.DeviceVerifyButtons');
if (this.props.device.isBlocked()) {
indicator = (
<div className="mx_MemberDeviceInfo_blacklisted">
<img src="img/e2e-blocked.svg" width="12" height="12" style={{ marginLeft: "-1px" }} alt={_t("Blacklisted")}/>
<img src="img/e2e-blocked.svg" width="12" height="12" style={{ marginLeft: "-1px" }} alt={_t("Blacklisted")} />
</div>
);
} else if (this.props.device.isVerified()) {
indicator = (
<div className="mx_MemberDeviceInfo_verified">
<img src="img/e2e-verified.svg" width="10" height="12" alt={_t("Verified")}/>
<img src="img/e2e-verified.svg" width="10" height="12" alt={_t("Verified")} />
</div>
);
} else {
indicator = (
<div className="mx_MemberDeviceInfo_unverified">
<img src="img/e2e-warning.svg" width="15" height="12" style={{ marginLeft: "-2px" }} alt={_t("Unverified")}/>
<img src="img/e2e-warning.svg" width="15" height="12" style={{ marginLeft: "-2px" }} alt={_t("Unverified")} />
</div>
);
}
var deviceName = this.props.device.ambiguous ?
const deviceName = this.props.device.ambiguous ?
(this.props.device.getDisplayName() ? this.props.device.getDisplayName() : "") + " (" + this.props.device.deviceId + ")" :
this.props.device.getDisplayName();
@ -53,8 +53,8 @@ export default class MemberDeviceInfo extends React.Component {
title={_t("device id: ") + this.props.device.deviceId} >
<div className="mx_MemberDeviceInfo_deviceInfo">
<div className="mx_MemberDeviceInfo_deviceId">
{deviceName}
{indicator}
{ deviceName }
{ indicator }
</div>
</div>
<DeviceVerifyButtons userId={this.props.userId} device={this.props.device} />

View file

@ -55,7 +55,7 @@ module.exports = withMatrixClient(React.createClass({
kick: false,
ban: false,
mute: false,
modifyLevel: false
modifyLevel: false,
},
muted: false,
isTargetMod: false,
@ -97,7 +97,7 @@ module.exports = withMatrixClient(React.createClass({
},
componentWillUnmount: function() {
var client = this.props.matrixClient;
const client = this.props.matrixClient;
if (client) {
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
client.removeListener("Room", this.onRoom);
@ -120,10 +120,10 @@ module.exports = withMatrixClient(React.createClass({
},
_disambiguateDevices: function(devices) {
var names = Object.create(null);
for (var i = 0; i < devices.length; i++) {
const names = Object.create(null);
for (let i = 0; i < devices.length; i++) {
var name = devices[i].getDisplayName();
var indexList = names[name] || [];
const indexList = names[name] || [];
indexList.push(i);
names[name] = indexList;
}
@ -193,7 +193,7 @@ module.exports = withMatrixClient(React.createClass({
},
_updateStateForNewMember: function(member) {
var newState = this._calculateOpsPermissions(member);
const newState = this._calculateOpsPermissions(member);
newState.devicesLoading = true;
newState.devices = null;
this.setState(newState);
@ -211,11 +211,11 @@ module.exports = withMatrixClient(React.createClass({
return;
}
var cancelled = false;
let cancelled = false;
this._cancelDeviceList = function() { cancelled = true; };
var client = this.props.matrixClient;
var self = this;
const client = this.props.matrixClient;
const self = this;
client.downloadKeys([member.userId], true).then(() => {
return client.getStoredDevicesForUser(member.userId);
}).finally(function() {
@ -260,7 +260,7 @@ module.exports = withMatrixClient(React.createClass({
this.setState({ updating: this.state.updating + 1 });
this.props.matrixClient.kick(
this.props.member.roomId, this.props.member.userId,
reason || undefined
reason || undefined,
).then(function() {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
@ -272,11 +272,11 @@ module.exports = withMatrixClient(React.createClass({
title: _t("Failed to kick"),
description: ((err && err.message) ? err.message : "Operation failed"),
});
}
},
).finally(()=>{
this.setState({ updating: this.state.updating - 1 });
});
}
},
});
},
@ -299,7 +299,7 @@ module.exports = withMatrixClient(React.createClass({
} else {
promise = this.props.matrixClient.ban(
this.props.member.roomId, this.props.member.userId,
reason || undefined
reason || undefined,
);
}
promise.then(
@ -314,7 +314,7 @@ module.exports = withMatrixClient(React.createClass({
title: _t("Error"),
description: _t("Failed to ban user"),
});
}
},
).finally(()=>{
this.setState({ updating: this.state.updating - 1 });
});
@ -323,30 +323,29 @@ module.exports = withMatrixClient(React.createClass({
},
onMuteToggle: function() {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var room = this.props.matrixClient.getRoom(roomId);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const roomId = this.props.member.roomId;
const target = this.props.member.userId;
const room = this.props.matrixClient.getRoom(roomId);
if (!room) {
return;
}
var powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", ""
const powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", "",
);
if (!powerLevelEvent) {
return;
}
var isMuted = this.state.muted;
var powerLevels = powerLevelEvent.getContent();
var levelToSend = (
const isMuted = this.state.muted;
const powerLevels = powerLevelEvent.getContent();
const levelToSend = (
(powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
powerLevels.events_default
);
var level;
let level;
if (isMuted) { // unmute
level = levelToSend;
}
else { // mute
} else { // mute
level = levelToSend - 1;
}
level = parseInt(level);
@ -364,7 +363,7 @@ module.exports = withMatrixClient(React.createClass({
title: _t("Error"),
description: _t("Failed to mute user"),
});
}
},
).finally(()=>{
this.setState({ updating: this.state.updating - 1 });
});
@ -372,28 +371,28 @@ module.exports = withMatrixClient(React.createClass({
},
onModToggle: function() {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var room = this.props.matrixClient.getRoom(roomId);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const roomId = this.props.member.roomId;
const target = this.props.member.userId;
const room = this.props.matrixClient.getRoom(roomId);
if (!room) {
return;
}
var powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", ""
const powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", "",
);
if (!powerLevelEvent) {
return;
}
var me = room.getMember(this.props.matrixClient.credentials.userId);
const me = room.getMember(this.props.matrixClient.credentials.userId);
if (!me) {
return;
}
var defaultLevel = powerLevelEvent.getContent().users_default;
var modLevel = me.powerLevel - 1;
const defaultLevel = powerLevelEvent.getContent().users_default;
let modLevel = me.powerLevel - 1;
if (modLevel > 50 && defaultLevel < 50) modLevel = 50; // try to stick with the vector level defaults
// toggle the level
var newLevel = this.state.isTargetMod ? defaultLevel : modLevel;
const newLevel = this.state.isTargetMod ? defaultLevel : modLevel;
this.setState({ updating: this.state.updating + 1 });
this.props.matrixClient.setPowerLevel(roomId, target, parseInt(newLevel), powerLevelEvent).then(
function() {
@ -410,7 +409,7 @@ module.exports = withMatrixClient(React.createClass({
description: _t("Failed to toggle moderator status"),
});
}
}
},
).finally(()=>{
this.setState({ updating: this.state.updating - 1 });
});
@ -430,36 +429,36 @@ module.exports = withMatrixClient(React.createClass({
title: _t("Error"),
description: _t("Failed to change power level"),
});
}
},
).finally(()=>{
this.setState({ updating: this.state.updating - 1 });
}).done();
},
onPowerChange: function(powerLevel) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
var roomId = this.props.member.roomId;
var target = this.props.member.userId;
var room = this.props.matrixClient.getRoom(roomId);
var self = this;
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const roomId = this.props.member.roomId;
const target = this.props.member.userId;
const room = this.props.matrixClient.getRoom(roomId);
const self = this;
if (!room) {
return;
}
var powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", ""
const powerLevelEvent = room.currentState.getStateEvents(
"m.room.power_levels", "",
);
if (!powerLevelEvent) {
return;
}
if (powerLevelEvent.getContent().users) {
var myPower = powerLevelEvent.getContent().users[this.props.matrixClient.credentials.userId];
const myPower = powerLevelEvent.getContent().users[this.props.matrixClient.credentials.userId];
if (parseInt(myPower) === parseInt(powerLevel)) {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Promote to PL100 Warning', '', QuestionDialog, {
title: _t("Warning!"),
description:
<div>
{ _t("You will not be able to undo this change as you are promoting the user to have the same power level as yourself.") }<br/>
{ _t("You will not be able to undo this change as you are promoting the user to have the same power level as yourself.") }<br />
{ _t("Are you sure?") }
</div>,
button: _t("Continue"),
@ -469,12 +468,10 @@ module.exports = withMatrixClient(React.createClass({
}
},
});
}
else {
} else {
this._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
}
}
else {
} else {
this._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
}
},
@ -497,14 +494,14 @@ module.exports = withMatrixClient(React.createClass({
const defaultPerms = {
can: {},
muted: false,
modifyLevel: false
modifyLevel: false,
};
const room = this.props.matrixClient.getRoom(member.roomId);
if (!room) {
return defaultPerms;
}
const powerLevels = room.currentState.getStateEvents(
"m.room.power_levels", ""
"m.room.power_levels", "",
);
if (!powerLevels) {
return defaultPerms;
@ -516,10 +513,10 @@ module.exports = withMatrixClient(React.createClass({
const them = member;
return {
can: this._calculateCanPermissions(
me, them, powerLevels.getContent()
me, them, powerLevels.getContent(),
),
muted: this._isMuted(them, powerLevels.getContent()),
isTargetMod: them.powerLevel > powerLevels.getContent().users_default
isTargetMod: them.powerLevel > powerLevels.getContent().users_default,
};
},
@ -528,7 +525,7 @@ module.exports = withMatrixClient(React.createClass({
kick: false,
ban: false,
mute: false,
modifyLevel: false
modifyLevel: false,
};
const canAffectUser = them.powerLevel < me.powerLevel;
if (!canAffectUser) {
@ -556,7 +553,7 @@ module.exports = withMatrixClient(React.createClass({
if (!powerLevelContent || !member) {
return false;
}
var levelToSend = (
const levelToSend = (
(powerLevelContent.events ? powerLevelContent.events["m.room.message"] : null) ||
powerLevelContent.events_default
);
@ -566,19 +563,19 @@ module.exports = withMatrixClient(React.createClass({
onCancel: function(e) {
dis.dispatch({
action: "view_user",
member: null
member: null,
});
},
onMemberAvatarClick: function() {
var avatarUrl = this.props.member.user ? this.props.member.user.avatarUrl : this.props.member.events.member.getContent().avatar_url;
const avatarUrl = this.props.member.user ? this.props.member.user.avatarUrl : this.props.member.events.member.getContent().avatar_url;
if(!avatarUrl) return;
var httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl);
var ImageView = sdk.getComponent("elements.ImageView");
var params = {
const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl);
const ImageView = sdk.getComponent("elements.ImageView");
const params = {
src: httpUrl,
name: this.props.member.name
name: this.props.member.name,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
@ -596,11 +593,11 @@ module.exports = withMatrixClient(React.createClass({
return null;
}
var devices = this.state.devices;
var MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo');
var Spinner = sdk.getComponent("elements.Spinner");
const devices = this.state.devices;
const MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo');
const Spinner = sdk.getComponent("elements.Spinner");
var devComponents;
let devComponents;
if (this.state.devicesLoading) {
// still loading
devComponents = <Spinner />;
@ -610,10 +607,10 @@ module.exports = withMatrixClient(React.createClass({
devComponents = _t("No devices with registered encryption keys");
} else {
devComponents = [];
for (var i = 0; i < devices.length; i++) {
for (let i = 0; i < devices.length; i++) {
devComponents.push(<MemberDeviceInfo key={i}
userId={this.props.member.userId}
device={devices[i]}/>);
device={devices[i]} />);
}
}
@ -621,7 +618,7 @@ module.exports = withMatrixClient(React.createClass({
<div>
<h3>{ _t("Devices") }</h3>
<div className="mx_MemberInfo_devices">
{devComponents}
{ devComponents }
</div>
</div>
);
@ -633,7 +630,7 @@ module.exports = withMatrixClient(React.createClass({
if (this.props.member.userId !== this.props.matrixClient.getUserId()) {
ignoreButton = (
<AccessibleButton onClick={this.onIgnoreToggle} className="mx_MemberInfo_field">
{this.state.isIgnoring ? _t("Unignore") : _t("Ignore")}
{ this.state.isIgnoring ? _t("Unignore") : _t("Ignore") }
</AccessibleButton>
);
}
@ -644,14 +641,14 @@ module.exports = withMatrixClient(React.createClass({
<div>
<h3>{ _t("User Options") }</h3>
<div className="mx_MemberInfo_buttons">
{ignoreButton}
{ ignoreButton }
</div>
</div>
);
},
render: function() {
var startChat, kickButton, banButton, muteButton, giveModButton, spinner;
let startChat, kickButton, banButton, muteButton, giveModButton, spinner;
if (this.props.member.userId !== this.props.matrixClient.credentials.userId) {
const dmRoomMap = new DMRoomMap(this.props.matrixClient);
const dmRooms = dmRoomMap.getDMRoomsForUserId(this.props.member.userId);
@ -675,7 +672,7 @@ module.exports = withMatrixClient(React.createClass({
highlight={highlight}
isInvite={me.membership == "invite"}
onClick={this.onRoomTileClick}
/>
/>,
);
}
}
@ -696,14 +693,14 @@ module.exports = withMatrixClient(React.createClass({
startChat = <div>
<h3>{ _t("Direct chats") }</h3>
{tiles}
{startNewChat}
{ tiles }
{ startNewChat }
</div>;
}
if (this.state.updating) {
var Loader = sdk.getComponent("elements.Spinner");
spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>;
const Loader = sdk.getComponent("elements.Spinner");
spinner = <Loader imgClassName="mx_ContextualMenu_spinner" />;
}
if (this.state.can.kick) {
@ -712,7 +709,7 @@ module.exports = withMatrixClient(React.createClass({
kickButton = (
<AccessibleButton className="mx_MemberInfo_field"
onClick={this.onKick}>
{kickLabel}
{ kickLabel }
</AccessibleButton>
);
}
@ -724,7 +721,7 @@ module.exports = withMatrixClient(React.createClass({
banButton = (
<AccessibleButton className="mx_MemberInfo_field"
onClick={this.onBanOrUnban}>
{label}
{ label }
</AccessibleButton>
);
}
@ -733,31 +730,31 @@ module.exports = withMatrixClient(React.createClass({
muteButton = (
<AccessibleButton className="mx_MemberInfo_field"
onClick={this.onMuteToggle}>
{muteLabel}
{ muteLabel }
</AccessibleButton>
);
}
if (this.state.can.toggleMod) {
var giveOpLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
const giveOpLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
giveModButton = <AccessibleButton className="mx_MemberInfo_field" onClick={this.onModToggle}>
{giveOpLabel}
{ giveOpLabel }
</AccessibleButton>;
}
// TODO: we should have an invite button if this MemberInfo is showing a user who isn't actually in the current room yet
// e.g. clicking on a linkified userid in a room
var adminTools;
let adminTools;
if (kickButton || banButton || muteButton || giveModButton) {
adminTools =
<div>
<h3>{_t("Admin Tools")}</h3>
<h3>{ _t("Admin Tools") }</h3>
<div className="mx_MemberInfo_buttons">
{muteButton}
{kickButton}
{banButton}
{giveModButton}
{ muteButton }
{ kickButton }
{ banButton }
{ giveModButton }
</div>
</div>;
}
@ -767,35 +764,35 @@ module.exports = withMatrixClient(React.createClass({
if (this.props.member.user) {
var presenceState = this.props.member.user.presence;
var presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
var presenceLastTs = this.props.member.user.lastPresenceTs;
const presenceLastTs = this.props.member.user.lastPresenceTs;
var presenceCurrentlyActive = this.props.member.user.currentlyActive;
}
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
var PowerSelector = sdk.getComponent('elements.PowerSelector');
var PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const PowerSelector = sdk.getComponent('elements.PowerSelector');
const PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
const EmojiText = sdk.getComponent('elements.EmojiText');
return (
<div className="mx_MemberInfo">
<GeminiScrollbar autoshow={true}>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this.onCancel}> <img src="img/cancel.svg" width="18" height="18"/></AccessibleButton>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this.onCancel}> <img src="img/cancel.svg" width="18" height="18" /></AccessibleButton>
<div className="mx_MemberInfo_avatar">
<MemberAvatar onClick={this.onMemberAvatarClick} member={this.props.member} width={48} height={48} />
</div>
<EmojiText element="h2">{memberName}</EmojiText>
<EmojiText element="h2">{ memberName }</EmojiText>
<div className="mx_MemberInfo_profile">
<div className="mx_MemberInfo_profileField">
{ this.props.member.userId }
</div>
<div className="mx_MemberInfo_profileField">
{ _t("Level:") } <b><PowerSelector controlled={true} value={ parseInt(this.props.member.powerLevel) } disabled={ !this.state.can.modifyLevel } onChange={ this.onPowerChange }/></b>
{ _t("Level:") } <b><PowerSelector controlled={true} value={parseInt(this.props.member.powerLevel)} disabled={!this.state.can.modifyLevel} onChange={this.onPowerChange} /></b>
</div>
<div className="mx_MemberInfo_profileField">
<PresenceLabel activeAgo={ presenceLastActiveAgo }
currentlyActive={ presenceCurrentlyActive }
presenceState={ presenceState } />
<PresenceLabel activeAgo={presenceLastActiveAgo}
currentlyActive={presenceCurrentlyActive}
presenceState={presenceState} />
</div>
</div>
@ -811,5 +808,5 @@ module.exports = withMatrixClient(React.createClass({
</GeminiScrollbar>
</div>
);
}
},
}));

View file

@ -18,11 +18,11 @@ limitations under the License.
import React from 'react';
import { _t } from '../../../languageHandler';
var MatrixClientPeg = require("../../../MatrixClientPeg");
var sdk = require('../../../index');
var GeminiScrollbar = require('react-gemini-scrollbar');
var rate_limited_func = require('../../../ratelimitedfunc');
var CallHandler = require("../../../CallHandler");
const MatrixClientPeg = require("../../../MatrixClientPeg");
const sdk = require('../../../index');
const GeminiScrollbar = require('react-gemini-scrollbar');
const rate_limited_func = require('../../../ratelimitedfunc');
const CallHandler = require("../../../CallHandler");
const INITIAL_LOAD_NUM_MEMBERS = 30;
const INITIAL_LOAD_NUM_INVITED = 5;
@ -49,7 +49,7 @@ module.exports = React.createClass({
},
componentWillMount: function() {
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
cli.on("RoomState.members", this.onRoomStateMember);
cli.on("RoomMember.name", this.onRoomMemberName);
cli.on("RoomState.events", this.onRoomStateEvent);
@ -62,7 +62,7 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener("RoomState.members", this.onRoomStateMember);
cli.removeListener("RoomMember.name", this.onRoomMemberName);
@ -109,7 +109,7 @@ module.exports = React.createClass({
// member tile and re-render it. This is more efficient than every tile
// evar attaching their own listener.
// console.log("explicit presence from " + user.userId);
var tile = this.refs[user.userId];
const tile = this.refs[user.userId];
if (tile) {
this._updateList(); // reorder the membership list
}
@ -146,18 +146,18 @@ module.exports = React.createClass({
const newState = {
members: this.roomMembers(),
};
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join');
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite');
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite', this.state.searchQuery);
this.setState(newState);
}, 500),
getMemberDict: function() {
if (!this.props.roomId) return {};
var cli = MatrixClientPeg.get();
var room = cli.getRoom(this.props.roomId);
const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.roomId);
if (!room) return {};
var all_members = room.currentState.members;
const all_members = room.currentState.members;
Object.keys(all_members).map(function(userId) {
// work around a race where you might have a room member object
@ -175,19 +175,19 @@ module.exports = React.createClass({
},
roomMembers: function() {
var all_members = this.memberDict || {};
var all_user_ids = Object.keys(all_members);
var ConferenceHandler = CallHandler.getConferenceHandler();
const all_members = this.memberDict || {};
const all_user_ids = Object.keys(all_members);
const ConferenceHandler = CallHandler.getConferenceHandler();
all_user_ids.sort(this.memberSort);
var to_display = [];
var count = 0;
for (var i = 0; i < all_user_ids.length; ++i) {
var user_id = all_user_ids[i];
var m = all_members[user_id];
const to_display = [];
let count = 0;
for (let i = 0; i < all_user_ids.length; ++i) {
const user_id = all_user_ids[i];
const m = all_members[user_id];
if (m.membership == 'join' || m.membership == 'invite') {
if (m.membership === 'join' || m.membership === 'invite') {
if ((ConferenceHandler && !ConferenceHandler.isConferenceUser(user_id)) || !ConferenceHandler) {
to_display.push(user_id);
++count;
@ -233,8 +233,7 @@ module.exports = React.createClass({
memberString: function(member) {
if (!member) {
return "(null)";
}
else {
} else {
return "(" + member.name + ", " + member.powerLevel + ", " + member.user.lastActiveAgo + ", " + member.user.currentlyActive + ")";
}
},
@ -248,10 +247,10 @@ module.exports = React.createClass({
// ...and then alphabetically.
// We could tiebreak instead by "last recently spoken in this room" if we wanted to.
var memberA = this.memberDict[userIdA];
var memberB = this.memberDict[userIdB];
var userA = memberA.user;
var userB = memberB.user;
const memberA = this.memberDict[userIdA];
const memberB = this.memberDict[userIdB];
const userA = memberA.user;
const userB = memberB.user;
// if (!userA || !userB) {
// console.log("comparing " + memberA.name + " user=" + memberA.user + " with " + memberB.name + " user=" + memberB.user);
@ -269,15 +268,13 @@ module.exports = React.createClass({
// console.log(memberA + " and " + memberB + " have same power level");
if (memberA.name && memberB.name) {
// console.log("comparing names: " + memberA.name + " and " + memberB.name);
var nameA = memberA.name[0] === '@' ? memberA.name.substr(1) : memberA.name;
var nameB = memberB.name[0] === '@' ? memberB.name.substr(1) : memberB.name;
const nameA = memberA.name[0] === '@' ? memberA.name.substr(1) : memberA.name;
const nameB = memberB.name[0] === '@' ? memberB.name.substr(1) : memberB.name;
return nameA.localeCompare(nameB);
}
else {
} else {
return 0;
}
}
else {
} else {
// console.log("comparing power: " + memberA.powerLevel + " and " + memberB.powerLevel);
return memberB.powerLevel - memberA.powerLevel;
}
@ -305,6 +302,7 @@ module.exports = React.createClass({
const m = this.memberDict[userId];
if (query) {
query = query.toLowerCase();
const matchesName = m.name.toLowerCase().indexOf(query) !== -1;
const matchesId = m.userId.toLowerCase().indexOf(query) !== -1;
@ -313,7 +311,7 @@ module.exports = React.createClass({
}
}
return m.membership == membership;
return m.membership === membership;
});
},
@ -336,25 +334,25 @@ module.exports = React.createClass({
// The HS may have already converted these into m.room.member invites so
// we shouldn't add them if the 3pid invite state key (token) is in the
// member invite (content.third_party_invite.signed.token)
var room = MatrixClientPeg.get().getRoom(this.props.roomId);
var EntityTile = sdk.getComponent("rooms.EntityTile");
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
const EntityTile = sdk.getComponent("rooms.EntityTile");
if (room) {
room.currentState.getStateEvents("m.room.third_party_invite").forEach(
function(e) {
// any events without these keys are not valid 3pid invites, so we ignore them
var required_keys = ['key_validity_url', 'public_key', 'display_name'];
for (var i = 0; i < required_keys.length; ++i) {
const required_keys = ['key_validity_url', 'public_key', 'display_name'];
for (let i = 0; i < required_keys.length; ++i) {
if (e.getContent()[required_keys[i]] === undefined) return;
}
// discard all invites which have a m.room.member event since we've
// already added them.
var memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey());
const memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey());
if (memberEvent) {
return;
}
memberList.push(
<EntityTile key={e.getStateKey()} name={e.getContent().display_name} suppressOnHover={true} />
<EntityTile key={e.getStateKey()} name={e.getContent().display_name} suppressOnHover={true} />,
);
});
}
@ -372,7 +370,7 @@ module.exports = React.createClass({
},
_getChildrenInvited: function(start, end) {
return this._makeMemberTiles(this.state.filteredInvitedMembers.slice(start, end));
return this._makeMemberTiles(this.state.filteredInvitedMembers.slice(start, end), 'invite');
},
_getChildCountInvited: function() {
@ -402,7 +400,7 @@ module.exports = React.createClass({
<form autoComplete="off">
<input className="mx_MemberList_query" id="mx_MemberList_query" type="text"
onChange={this.onSearchQueryChanged} value={this.state.searchQuery}
placeholder={ _t('Filter room members') } />
placeholder={_t('Filter room members')} />
</form>
);
@ -415,9 +413,9 @@ module.exports = React.createClass({
getChildren={this._getChildrenJoined}
getChildCount={this._getChildCountJoined}
/>
{invitedSection}
{ invitedSection }
</GeminiScrollbar>
</div>
);
}
},
});

View file

@ -16,12 +16,12 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require('../../../index');
var dis = require('../../../dispatcher');
var Modal = require("../../../Modal");
const MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require('../../../index');
const dis = require('../../../dispatcher');
const Modal = require("../../../Modal");
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@ -68,16 +68,16 @@ module.exports = React.createClass({
},
render: function() {
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
var EntityTile = sdk.getComponent('rooms.EntityTile');
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const EntityTile = sdk.getComponent('rooms.EntityTile');
var member = this.props.member;
var name = this._getDisplayName();
var active = -1;
var presenceState = member.user ? member.user.presence : null;
const member = this.props.member;
const name = this._getDisplayName();
const active = -1;
const presenceState = member.user ? member.user.presence : null;
var av = (
const av = (
<MemberAvatar member={member} width={36} height={36} />
);
@ -88,11 +88,11 @@ module.exports = React.createClass({
return (
<EntityTile {...this.props} presenceState={presenceState}
presenceLastActiveAgo={ member.user ? member.user.lastActiveAgo : 0 }
presenceLastTs={ member.user ? member.user.lastPresenceTs : 0 }
presenceCurrentlyActive={ member.user ? member.user.currentlyActive : false }
presenceLastActiveAgo={member.user ? member.user.lastActiveAgo : 0}
presenceLastTs={member.user ? member.user.lastPresenceTs : 0}
presenceCurrentlyActive={member.user ? member.user.currentlyActive : false}
avatarJsx={av} title={this.getPowerLabel()} onClick={this.onClick}
name={name} powerLevel={this.props.member.powerLevel} />
);
}
},
});

View file

@ -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.
@ -89,13 +90,13 @@ export default class MessageComposer extends React.Component {
}
uploadFiles(files) {
let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
let TintableSvg = sdk.getComponent("elements.TintableSvg");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const TintableSvg = sdk.getComponent("elements.TintableSvg");
let fileList = [];
const fileList = [];
for (let i=0; i<files.length; i++) {
fileList.push(<li key={i}>
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> {files[i].name || _t('Attachment')}
<TintableSvg key={i} src="img/files.svg" width="16" height="16" /> { files[i].name || _t('Attachment') }
</li>);
}
@ -105,7 +106,7 @@ export default class MessageComposer extends React.Component {
<div>
<p>{ _t('Are you sure you want to upload the following files?') }</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{fileList}
{ fileList }
</ul>
</div>
),
@ -271,32 +272,30 @@ export default class MessageComposer extends React.Component {
if (this.props.callState && this.props.callState !== 'ended') {
hangupButton =
<div key="controls_hangup" className="mx_MessageComposer_hangup" onClick={this.onHangupClick}>
<img src="img/hangup.svg" alt={ _t('Hangup') } title={ _t('Hangup') } width="25" height="26"/>
<img src="img/hangup.svg" alt={_t('Hangup')} title={_t('Hangup')} width="25" height="26" />
</div>;
} else {
callButton =
<div key="controls_call" className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title={ _t('Voice call') }>
<TintableSvg src="img/icon-call.svg" width="35" height="35"/>
<div key="controls_call" className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title={_t('Voice call')}>
<TintableSvg src="img/icon-call.svg" width="35" height="35" />
</div>;
videoCallButton =
<div key="controls_videocall" className="mx_MessageComposer_videocall" onClick={this.onCallClick} title={ _t('Video call') }>
<TintableSvg src="img/icons-video.svg" width="35" height="35"/>
<div key="controls_videocall" className="mx_MessageComposer_videocall" onClick={this.onCallClick} title={_t('Video call')}>
<TintableSvg src="img/icons-video.svg" width="35" height="35" />
</div>;
}
// Apps
if (UserSettingsStore.isFeatureEnabled('matrix_apps')) {
if (this.props.showApps) {
hideAppsButton =
<div key="controls_hide_apps" className="mx_MessageComposer_apps" onClick={this.onHideAppsClick} title={_t("Hide Apps")}>
<TintableSvg src="img/icons-hide-apps.svg" width="35" height="35"/>
</div>;
} else {
showAppsButton =
<div key="show_apps" className="mx_MessageComposer_apps" onClick={this.onShowAppsClick} title={_t("Show Apps")}>
<TintableSvg src="img/icons-show-apps.svg" width="35" height="35"/>
</div>;
}
if (this.props.showApps) {
hideAppsButton =
<div key="controls_hide_apps" className="mx_MessageComposer_apps" onClick={this.onHideAppsClick} title={_t("Hide Apps")}>
<TintableSvg src="img/icons-hide-apps.svg" width="35" height="35" />
</div>;
} else {
showAppsButton =
<div key="show_apps" className="mx_MessageComposer_apps" onClick={this.onShowAppsClick} title={_t("Show Apps")}>
<TintableSvg src="img/icons-show-apps.svg" width="35" height="35" />
</div>;
}
const canSendMessages = this.props.room.currentState.maySendMessage(
@ -308,8 +307,8 @@ export default class MessageComposer extends React.Component {
// complex because of conference calls.
const uploadButton = (
<div key="controls_upload" className="mx_MessageComposer_upload"
onClick={this.onUploadClick} title={ _t('Upload file') }>
<TintableSvg src="img/icons-upload.svg" width="35" height="35"/>
onClick={this.onUploadClick} title={_t('Upload file')}>
<TintableSvg src="img/icons-upload.svg" width="35" height="35" />
<input ref="uploadInput" type="file"
style={uploadInputStyle}
multiple
@ -363,7 +362,7 @@ export default class MessageComposer extends React.Component {
const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
return <img className={className}
title={ _t(name) }
title={_t(name)}
onMouseDown={onFormatButtonClicked}
key={name}
src={`img/button-text-${name}${suffix}.svg`}
@ -375,18 +374,18 @@ export default class MessageComposer extends React.Component {
<div className="mx_MessageComposer mx_fadable" style={{ opacity: this.props.opacity }}>
<div className="mx_MessageComposer_wrapper">
<div className="mx_MessageComposer_row">
{controls}
{ controls }
</div>
</div>
<div className="mx_MessageComposer_formatbar_wrapper">
<div className="mx_MessageComposer_formatbar" style={this.state.showFormatting ? {} : {display: 'none'}}>
{formatButtons}
{ formatButtons }
<div style={{flex: 1}}></div>
<img title={ this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off") }
<img title={this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off")}
onMouseDown={this.onToggleMarkdownClicked}
className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />
<img title={ _t("Hide Text Formatting Toolbar") }
<img title={_t("Hide Text Formatting Toolbar")}
onClick={this.onToggleFormattingClicked}
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
src="img/icon-text-cancel.svg" />

View file

@ -30,7 +30,7 @@ import SlashCommands from '../../../SlashCommands';
import KeyCode from '../../../KeyCode';
import Modal from '../../../Modal';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import Analytics from '../../../Analytics';
import dis from '../../../dispatcher';
@ -224,7 +224,7 @@ export default class MessageComposerInput extends React.Component {
return (
<a href={url} data-offset-key={entityProps.offsetKey}>
{entityProps.children}
{ entityProps.children }
</a>
);
},
@ -286,7 +286,7 @@ export default class MessageComposerInput extends React.Component {
/// XXX: Not doing rich-text quoting from formatted-body because draft-js
/// has regressed such that when links are quoted, errors are thrown. See
/// https://github.com/vector-im/riot-web/issues/4756.
let body = escape(payload.text);
const body = escape(payload.text);
if (body) {
let content = RichText.htmlToContentState(`<blockquote>${body}</blockquote>`);
if (!this.state.isRichtextEnabled) {
@ -464,7 +464,7 @@ export default class MessageComposerInput extends React.Component {
// autocomplete will probably have different completions to show.
if (
!state.editorState.getSelection().equals(
this.state.editorState.getSelection()
this.state.editorState.getSelection(),
)
&& state.editorState.getCurrentContent().getPlainText() ===
this.state.editorState.getCurrentContent().getPlainText()
@ -974,7 +974,6 @@ export default class MessageComposerInput extends React.Component {
editorState = EditorState.forceSelection(editorState,
editorState.getSelection());
this.setState({editorState});
}
return false;
}
@ -1032,10 +1031,10 @@ export default class MessageComposerInput extends React.Component {
buttons. */
getSelectionInfo(editorState: EditorState) {
const styleName = {
BOLD: 'bold',
ITALIC: 'italic',
STRIKETHROUGH: 'strike',
UNDERLINE: 'underline',
BOLD: _td('bold'),
ITALIC: _td('italic'),
STRIKETHROUGH: _td('strike'),
UNDERLINE: _td('underline'),
};
const originalStyle = editorState.getCurrentInlineStyle().toArray();
@ -1044,10 +1043,10 @@ export default class MessageComposerInput extends React.Component {
.filter((styleName) => !!styleName);
const blockName = {
'code-block': 'code',
'blockquote': 'quote',
'unordered-list-item': 'bullet',
'ordered-list-item': 'numbullet',
'code-block': _td('code'),
'blockquote': _td('quote'),
'unordered-list-item': _td('bullet'),
'ordered-list-item': _td('numbullet'),
};
const originalBlockType = editorState.getCurrentContent()
.getBlockForKey(editorState.getSelection().getStartKey())
@ -1134,12 +1133,12 @@ export default class MessageComposerInput extends React.Component {
onConfirm={this.setDisplayedCompletion}
onSelectionChange={this.setDisplayedCompletion}
query={this.getAutocompleteQuery(content)}
selection={selection}/>
selection={selection} />
</div>
<div className={className}>
<img className="mx_MessageComposer_input_markdownIndicator mx_filterFlipColor"
onMouseDown={this.onMarkdownToggleClicked}
title={ this.state.isRichtextEnabled ? _t("Markdown is disabled") : _t("Markdown is enabled")}
title={this.state.isRichtextEnabled ? _t("Markdown is disabled") : _t("Markdown is enabled")}
src={`img/button-md-${!this.state.isRichtextEnabled}.png`} />
<Editor ref="editor"
dir="auto"
@ -1157,7 +1156,7 @@ export default class MessageComposerInput extends React.Component {
onUpArrow={this.onUpArrow}
onDownArrow={this.onDownArrow}
onEscape={this.onEscape}
spellCheck={true}/>
spellCheck={true} />
</div>
</div>
);

View file

@ -34,23 +34,23 @@ module.exports = React.createClass({
currentlyActive: React.PropTypes.bool,
// offline, online, etc
presenceState: React.PropTypes.string
presenceState: React.PropTypes.string,
},
getDefaultProps: function() {
return {
ago: -1,
presenceState: null
presenceState: null,
};
},
getDuration: function(time) {
if (!time) return;
var t = parseInt(time / 1000);
var s = t % 60;
var m = parseInt(t / 60) % 60;
var h = parseInt(t / (60 * 60)) % 24;
var d = parseInt(t / (60 * 60 * 24));
const t = parseInt(time / 1000);
const s = t % 60;
const m = parseInt(t / 60) % 60;
const h = parseInt(t / (60 * 60)) % 24;
const d = parseInt(t / (60 * 60 * 24));
if (t < 60) {
if (t < 0) {
return _t("for %(amount)ss", {amount: 0});
@ -58,37 +58,36 @@ module.exports = React.createClass({
return _t("for %(amount)ss", {amount: s});
}
if (t < 60 * 60) {
return _t("for %(amount)sm", {amount: m});
return _t("for %(amount)sm", {amount: m});
}
if (t < 24 * 60 * 60) {
return _t("for %(amount)sh", {amount: h});
return _t("for %(amount)sh", {amount: h});
}
return _t("for %(amount)sd", {amount: d});
return _t("for %(amount)sd", {amount: d});
},
getPrettyPresence: function(presence) {
if (presence === "online") return _t("Online");
if (presence === "unavailable") return _t("Idle"); // XXX: is this actually right?
if (presence === "offline") return _t("Offline");
return "Unknown";
return _t("Unknown");
},
render: function() {
if (this.props.activeAgo >= 0) {
let duration = this.getDuration(this.props.activeAgo);
let ago = this.props.currentlyActive || !duration ? "" : duration;
const duration = this.getDuration(this.props.activeAgo);
const ago = this.props.currentlyActive || !duration ? "" : duration;
return (
<div className="mx_PresenceLabel">
{ this.getPrettyPresence(this.props.presenceState) } { ago }
</div>
);
}
else {
} else {
return (
<div className="mx_PresenceLabel">
{ this.getPrettyPresence(this.props.presenceState) }
</div>
);
}
}
},
});

View file

@ -16,18 +16,18 @@ limitations under the License.
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
const React = require('react');
const ReactDOM = require('react-dom');
var sdk = require('../../../index');
const sdk = require('../../../index');
var Velociraptor = require('../../../Velociraptor');
const Velociraptor = require('../../../Velociraptor');
require('../../../VelocityBounce');
import { _t } from '../../../languageHandler';
import DateUtils from '../../../DateUtils';
var bounce = false;
let bounce = false;
try {
if (global.localStorage) {
bounce = global.localStorage.getItem('avatar_bounce') == 'true';
@ -90,7 +90,7 @@ module.exports = React.createClass({
componentWillUnmount: function() {
// before we remove the rr, store its location in the map, so that if
// it reappears, it can be animated from the right place.
var rrInfo = this.props.readReceiptInfo;
const rrInfo = this.props.readReceiptInfo;
if (!rrInfo) {
return;
}
@ -102,7 +102,7 @@ module.exports = React.createClass({
return;
}
var avatarNode = ReactDOM.findDOMNode(this);
const avatarNode = ReactDOM.findDOMNode(this);
rrInfo.top = avatarNode.offsetTop;
rrInfo.left = avatarNode.offsetLeft;
rrInfo.parent = avatarNode.offsetParent;
@ -115,14 +115,14 @@ module.exports = React.createClass({
}
// treat new RRs as though they were off the top of the screen
var oldTop = -15;
let oldTop = -15;
var oldInfo = this.props.readReceiptInfo;
const oldInfo = this.props.readReceiptInfo;
if (oldInfo && oldInfo.parent) {
oldTop = oldInfo.top + oldInfo.parent.getBoundingClientRect().top;
}
var newElement = ReactDOM.findDOMNode(this);
const newElement = ReactDOM.findDOMNode(this);
let startTopOffset;
if (!newElement.offsetParent) {
// this seems to happen sometimes for reasons I don't understand
@ -137,8 +137,8 @@ module.exports = React.createClass({
startTopOffset = oldTop - newElement.offsetParent.getBoundingClientRect().top;
}
var startStyles = [];
var enterTransitionOpts = [];
const startStyles = [];
const enterTransitionOpts = [];
if (oldInfo && oldInfo.left) {
// start at the old height and in the old h pos
@ -146,7 +146,7 @@ module.exports = React.createClass({
startStyles.push({ top: startTopOffset+"px",
left: oldInfo.left+"px" });
var reorderTransitionOpts = {
const reorderTransitionOpts = {
duration: 100,
easing: 'easeOut',
};
@ -171,12 +171,12 @@ module.exports = React.createClass({
render: function() {
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
if (this.state.suppressDisplay) {
return <div/>;
return <div />;
}
var style = {
const style = {
left: this.props.leftOffset+'px',
top: '0px',
visibility: this.props.hidden ? 'hidden' : 'visible',

View file

@ -130,6 +130,10 @@ module.exports = React.createClass({
}).done();
},
onAvatarRemoveClick: function() {
MatrixClientPeg.get().sendStateEvent(this.props.room.roomId, 'm.room.avatar', {url: null}, '');
},
onShowRhsClick: function(ev) {
dis.dispatch({ action: 'show_right_panel' });
},
@ -270,11 +274,15 @@ module.exports = React.createClass({
<div className="mx_RoomHeader_avatarPicker_edit">
<label htmlFor="avatarInput" ref="file_label">
<img src="img/camera.svg"
alt={_t("Upload avatar")} title={_t("Upload avatar")}
width="17" height="15" />
alt={_t("Upload avatar")} title={_t("Upload avatar")}
width="17" height="15" />
</label>
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
</div>
<div className="mx_RoomHeader_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
<img src="img/cancel.svg" width="10"
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
</div>
</div>
);
} else if (this.props.room || (this.props.oobData && this.props.oobData.name)) {

View file

@ -16,20 +16,20 @@ limitations under the License.
*/
'use strict';
var React = require("react");
var ReactDOM = require("react-dom");
const React = require("react");
const ReactDOM = require("react-dom");
import { _t, _tJsx } from '../../../languageHandler';
var GeminiScrollbar = require('react-gemini-scrollbar');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var CallHandler = require('../../../CallHandler');
var RoomListSorter = require("../../../RoomListSorter");
var Unread = require('../../../Unread');
var dis = require("../../../dispatcher");
var sdk = require('../../../index');
var rate_limited_func = require('../../../ratelimitedfunc');
var Rooms = require('../../../Rooms');
const GeminiScrollbar = require('react-gemini-scrollbar');
const MatrixClientPeg = require("../../../MatrixClientPeg");
const CallHandler = require('../../../CallHandler');
const RoomListSorter = require("../../../RoomListSorter");
const Unread = require('../../../Unread');
const dis = require("../../../dispatcher");
const sdk = require('../../../index');
const rate_limited_func = require('../../../ratelimitedfunc');
const Rooms = require('../../../Rooms');
import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt');
const Receipt = require('../../../utils/Receipt');
const HIDE_CONFERENCE_CHANS = true;
@ -55,7 +55,7 @@ function phraseForSection(section) {
return _t('Drop here to tag %(section)s', {section: section});
}
return _t('Drop here %(toAction)s', {toAction: verb});
};
}
module.exports = React.createClass({
displayName: 'RoomList',
@ -78,7 +78,7 @@ module.exports = React.createClass({
componentWillMount: function() {
this.mounted = false;
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
cli.on("Room", this.onRoom);
cli.on("deleteRoom", this.onDeleteRoom);
cli.on("Room.timeline", this.onRoomTimeline);
@ -98,7 +98,7 @@ module.exports = React.createClass({
// loop count to stop a stack overflow if the user keeps waggling the
// mouse for >30s in a row, or if running under mocha
this._delayedRefreshRoomListLoopCount = 0
this._delayedRefreshRoomListLoopCount = 0;
},
componentDidMount: function() {
@ -124,13 +124,12 @@ module.exports = React.createClass({
var call = CallHandler.getCall(payload.room_id);
if (call && call.call_state === 'ringing') {
this.setState({
incomingCall: call
incomingCall: call,
});
this._repositionIncomingCallBox(undefined, true);
}
else {
} else {
this.setState({
incomingCall: null
incomingCall: null,
});
}
break;
@ -174,7 +173,7 @@ module.exports = React.createClass({
onArchivedHeaderClick: function(isHidden, scrollToPosition) {
if (!isHidden) {
var self = this;
const self = this;
this.setState({ isLoadingLeftRooms: true });
// Try scrolling to position
@ -265,7 +264,7 @@ module.exports = React.createClass({
},
getRoomLists: function() {
var self = this;
const self = this;
const lists = {};
lists["im.vector.fake.invite"] = [];
@ -288,35 +287,28 @@ module.exports = React.createClass({
if (me.membership == "invite") {
lists["im.vector.fake.invite"].push(room);
}
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
} else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
// skip past this room & don't put it in any lists
}
else if (me.membership == "join" || me.membership === "ban" ||
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey()))
{
} else if (me.membership == "join" || me.membership === "ban" ||
(me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) {
// Used to split rooms via tags
var tagNames = Object.keys(room.tags);
const tagNames = Object.keys(room.tags);
if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i];
for (let i = 0; i < tagNames.length; i++) {
const tagName = tagNames[i];
lists[tagName] = lists[tagName] || [];
lists[tagName].push(room);
}
}
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
} else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
lists["im.vector.fake.direct"].push(room);
}
else {
} else {
lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
} else if (me.membership === "leave") {
lists["im.vector.fake.archived"].push(room);
}
else {
} else {
console.error("unrecognised membership: " + me.membership + " - this should never happen");
}
});
@ -343,7 +335,7 @@ module.exports = React.createClass({
_getScrollNode: function() {
if (!this.mounted) return null;
var panel = ReactDOM.findDOMNode(this);
const panel = ReactDOM.findDOMNode(this);
if (!panel) return null;
if (panel.classList.contains('gm-prevented')) {
@ -367,22 +359,22 @@ module.exports = React.createClass({
},
_repositionIncomingCallBox: function(e, firstTime) {
var incomingCallBox = document.getElementById("incomingCallBox");
const incomingCallBox = document.getElementById("incomingCallBox");
if (incomingCallBox && incomingCallBox.parentElement) {
var scrollArea = this._getScrollNode();
const scrollArea = this._getScrollNode();
if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
const scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
// Use the offset of the top of the component from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
const scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
var top = (incomingCallBox.parentElement.getBoundingClientRect().top + window.pageYOffset);
let top = (incomingCallBox.parentElement.getBoundingClientRect().top + window.pageYOffset);
// Make sure we don't go too far up, if the headers aren't sticky
top = (top < scrollAreaOffset) ? scrollAreaOffset : top;
// make sure we don't go too far down, if the headers aren't sticky
var bottomMargin = scrollAreaOffset + (scrollAreaHeight - 45);
const bottomMargin = scrollAreaOffset + (scrollAreaHeight - 45);
top = (top > bottomMargin) ? bottomMargin : top;
incomingCallBox.style.top = top + "px";
@ -393,14 +385,14 @@ module.exports = React.createClass({
// Doing the sticky headers as raw DOM, for speed, as it gets very stuttery if done
// properly through React
_initAndPositionStickyHeaders: function(initialise, scrollToPosition) {
var scrollArea = this._getScrollNode();
const scrollArea = this._getScrollNode();
if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
const scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
// Use the offset of the top of the componet from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
const scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
if (initialise) {
// Get a collection of sticky header containers references
@ -420,7 +412,7 @@ module.exports = React.createClass({
sticky.dataset.originalPosition = sticky.offsetTop - scrollArea.offsetTop;
// Save and set the sticky heights
var originalHeight = sticky.getBoundingClientRect().height;
const originalHeight = sticky.getBoundingClientRect().height;
sticky.dataset.originalHeight = originalHeight;
sticky.style.height = originalHeight;
@ -429,8 +421,8 @@ module.exports = React.createClass({
}
}
var self = this;
var scrollStuckOffset = 0;
const self = this;
let scrollStuckOffset = 0;
// Scroll to the passed in position, i.e. a header was clicked and in a scroll to state
// rather than a collapsable one (see RoomSubList.isCollapsableOnClick method for details)
if (scrollToPosition !== undefined) {
@ -438,11 +430,11 @@ module.exports = React.createClass({
}
// Stick headers to top and bottom, or free them
Array.prototype.forEach.call(this.stickies, function(sticky, i, stickyWrappers) {
var stickyPosition = sticky.dataset.originalPosition;
var stickyHeight = sticky.dataset.originalHeight;
var stickyHeader = sticky.childNodes[0];
var topStuckHeight = stickyHeight * i;
var bottomStuckHeight = stickyHeight * (stickyWrappers.length - i);
const stickyPosition = sticky.dataset.originalPosition;
const stickyHeight = sticky.dataset.originalHeight;
const stickyHeader = sticky.childNodes[0];
const topStuckHeight = stickyHeight * i;
const bottomStuckHeight = stickyHeight * (stickyWrappers.length - i);
if (self.scrollAreaSufficient && stickyPosition < (scrollArea.scrollTop + topStuckHeight)) {
// Top stickies
@ -472,7 +464,7 @@ module.exports = React.createClass({
},
_updateStickyHeaders: function(initialise, scrollToPosition) {
var self = this;
const self = this;
if (initialise) {
// Useing setTimeout to ensure that the code is run after the painting
@ -507,25 +499,25 @@ module.exports = React.createClass({
switch (section) {
case 'im.vector.fake.direct':
return <div className="mx_RoomList_emptySubListTip">
{_tJsx(
{ _tJsx(
"Press <StartChatButton> to start a chat with someone",
[/<StartChatButton>/],
[
(sub) => <StartChatButton size="16" callout={true}/>
]
)}
(sub) => <StartChatButton size="16" callout={true} />,
],
) }
</div>;
case 'im.vector.fake.recent':
return <div className="mx_RoomList_emptySubListTip">
{_tJsx(
{ _tJsx(
"You're not in any rooms yet! Press <CreateRoomButton> to make a room or"+
" <RoomDirectoryButton> to browse the directory",
[/<CreateRoomButton>/, /<RoomDirectoryButton>/],
[
(sub) => <CreateRoomButton size="16" callout={true}/>,
(sub) => <RoomDirectoryButton size="16" callout={true}/>
]
)}
(sub) => <CreateRoomButton size="16" callout={true} />,
(sub) => <RoomDirectoryButton size="16" callout={true} />,
],
) }
</div>;
}
@ -574,113 +566,112 @@ module.exports = React.createClass({
const inviteSectionExtraTiles = this._makeGroupInviteTiles();
var self = this;
const self = this;
return (
<GeminiScrollbar className="mx_RoomList_scrollbar"
autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll">
autoshow={true} onScroll={self._whenScrolling} ref="gemscroll">
<div className="mx_RoomList">
<RoomSubList list={ self.state.lists['im.vector.fake.invite'] }
label={ _t('Invites') }
editable={ false }
<RoomSubList list={self.state.lists['im.vector.fake.invite']}
label={_t('Invites')}
editable={false}
order="recent"
isInvite={true}
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms }
extraTiles={ inviteSectionExtraTiles }
selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms}
extraTiles={inviteSectionExtraTiles}
/>
<RoomSubList list={ self.state.lists['m.favourite'] }
label={ _t('Favourites') }
<RoomSubList list={self.state.lists['m.favourite']}
label={_t('Favourites')}
tagName="m.favourite"
emptyContent={this._getEmptyContent('m.favourite')}
editable={ true }
editable={true}
order="manual"
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} />
<RoomSubList list={ self.state.lists['im.vector.fake.direct'] }
label={ _t('People') }
<RoomSubList list={self.state.lists['im.vector.fake.direct']}
label={_t('People')}
tagName="im.vector.fake.direct"
emptyContent={this._getEmptyContent('im.vector.fake.direct')}
headerItems={this._getHeaderItems('im.vector.fake.direct')}
editable={ true }
editable={true}
order="recent"
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
alwaysShowHeader={ true }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
alwaysShowHeader={true}
searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} />
<RoomSubList list={ self.state.lists['im.vector.fake.recent'] }
label={ _t('Rooms') }
editable={ true }
<RoomSubList list={self.state.lists['im.vector.fake.recent']}
label={_t('Rooms')}
editable={true}
emptyContent={this._getEmptyContent('im.vector.fake.recent')}
headerItems={this._getHeaderItems('im.vector.fake.recent')}
order="recent"
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} />
{ Object.keys(self.state.lists).map((tagName) => {
if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
return <RoomSubList list={ self.state.lists[tagName] }
key={ tagName }
label={ tagName }
tagName={ tagName }
return <RoomSubList list={self.state.lists[tagName]}
key={tagName}
label={tagName}
tagName={tagName}
emptyContent={this._getEmptyContent(tagName)}
editable={ true }
editable={true}
order="manual"
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />;
selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} />;
}
}) }
<RoomSubList list={ self.state.lists['m.lowpriority'] }
label={ _t('Low priority') }
<RoomSubList list={self.state.lists['m.lowpriority']}
label={_t('Low priority')}
tagName="m.lowpriority"
emptyContent={this._getEmptyContent('m.lowpriority')}
editable={ true }
editable={true}
order="recent"
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
selectedRoom={self.props.selectedRoom}
incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} />
<RoomSubList list={ self.state.lists['im.vector.fake.archived'] }
label={ _t('Historical') }
editable={ false }
<RoomSubList list={self.state.lists['im.vector.fake.archived']}
label={_t('Historical')}
editable={false}
order="recent"
selectedRoom={ self.props.selectedRoom }
collapsed={ self.props.collapsed }
alwaysShowHeader={ true }
startAsHidden={ true }
showSpinner={ self.state.isLoadingLeftRooms }
onHeaderClick= { self.onArchivedHeaderClick }
incomingCall={ self.state.incomingCall }
searchFilter={ self.props.searchFilter }
onShowMoreRooms={ self.onShowMoreRooms } />
selectedRoom={self.props.selectedRoom}
collapsed={self.props.collapsed}
alwaysShowHeader={true}
startAsHidden={true}
showSpinner={self.state.isLoadingLeftRooms}
onHeaderClick= {self.onArchivedHeaderClick}
incomingCall={self.state.incomingCall}
searchFilter={self.props.searchFilter}
onShowMoreRooms={self.onShowMoreRooms} />
</div>
</GeminiScrollbar>
);
}
},
});

View file

@ -16,9 +16,9 @@ limitations under the License.
'use strict';
var React = require('react');
var sdk = require('../../../index');
var MatrixClientPeg = require('../../../MatrixClientPeg');
const React = require('react');
const sdk = require('../../../index');
const MatrixClientPeg = require('../../../MatrixClientPeg');
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@ -29,10 +29,10 @@ module.exports = React.createClass({
},
componentWillMount: function() {
var room = this.props.room;
var name = room.currentState.getStateEvents('m.room.name', '');
var myId = MatrixClientPeg.get().credentials.userId;
var defaultName = room.getDefaultRoomName(myId);
const room = this.props.room;
const name = room.currentState.getStateEvents('m.room.name', '');
const myId = MatrixClientPeg.get().credentials.userId;
const defaultName = room.getDefaultRoomName(myId);
this._initialName = name ? name.getContent().name : '';
@ -47,16 +47,16 @@ module.exports = React.createClass({
},
render: function() {
var EditableText = sdk.getComponent("elements.EditableText");
const EditableText = sdk.getComponent("elements.EditableText");
return (
<div className="mx_RoomHeader_name">
<EditableText ref="editor"
className="mx_RoomHeader_nametext mx_RoomHeader_editable"
placeholderClassName="mx_RoomHeader_placeholder"
placeholder={ this._placeholderName }
blurToCancel={ false }
initialValue={ this._initialName }
placeholder={this._placeholderName}
blurToCancel={false}
initialValue={this._initialName}
dir="auto" />
</div>
);

View file

@ -17,9 +17,9 @@ limitations under the License.
'use strict';
var React = require('react');
var sdk = require('../../../index');
var MatrixClientPeg = require('../../../MatrixClientPeg');
const React = require('react');
const sdk = require('../../../index');
const MatrixClientPeg = require('../../../MatrixClientPeg');
import { _t, _tJsx } from '../../../languageHandler';
@ -61,7 +61,7 @@ module.exports = React.createClass({
getInitialState: function() {
return {
busy: false
busy: false,
};
},
@ -72,7 +72,7 @@ module.exports = React.createClass({
if (this.props.inviterName && this.props.invitedEmail) {
this.setState({busy: true});
MatrixClientPeg.get().lookupThreePid(
'email', this.props.invitedEmail
'email', this.props.invitedEmail,
).finally(() => {
this.setState({busy: false});
}).done((result) => {
@ -90,10 +90,10 @@ module.exports = React.createClass({
},
render: function() {
var joinBlock, previewBlock;
let joinBlock, previewBlock;
if (this.props.spinner || this.state.busy) {
var Spinner = sdk.getComponent("elements.Spinner");
const Spinner = sdk.getComponent("elements.Spinner");
return (<div className="mx_RoomPreviewBar">
<Spinner />
</div>);
@ -110,11 +110,11 @@ module.exports = React.createClass({
const banned = myMember && myMember.membership == 'ban';
if (this.props.inviterName) {
var emailMatchBlock;
let emailMatchBlock;
if (this.props.invitedEmail) {
if (this.state.threePidFetchError) {
emailMatchBlock = <div className="error">
{_t("Unable to ascertain that the address this invite was sent to matches one associated with your account.")}
{ _t("Unable to ascertain that the address this invite was sent to matches one associated with your account.") }
</div>;
} else if (this.state.invitedEmailMxid != MatrixClientPeg.get().credentials.userId) {
emailMatchBlock =
@ -123,10 +123,10 @@ module.exports = React.createClass({
<img src="img/warning.svg" width="24" height="23" title= "/!\\" alt="/!\\" />
</div>
<div className="mx_RoomPreviewBar_warningText">
{_t("This invitation was sent to an email address which is not associated with this account:")}
<b><span className="email">{this.props.invitedEmail}</span></b>
<br/>
{_t("You may wish to login with a different account, or add this email to this account.")}
{ _t("This invitation was sent to an email address which is not associated with this account:") }
<b><span className="email">{ this.props.invitedEmail }</span></b>
<br />
{ _t("You may wish to login with a different account, or add this email to this account.") }
</div>
</div>;
}
@ -141,53 +141,51 @@ module.exports = React.createClass({
'Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?',
[/<acceptText>(.*?)<\/acceptText>/, /<declineText>(.*?)<\/declineText>/],
[
(sub) => <a onClick={ this.props.onJoinClick }>{sub}</a>,
(sub) => <a onClick={ this.props.onRejectClick }>{sub}</a>
]
)}
(sub) => <a onClick={this.props.onJoinClick}>{ sub }</a>,
(sub) => <a onClick={this.props.onRejectClick}>{ sub }</a>,
],
) }
</div>
{emailMatchBlock}
{ emailMatchBlock }
</div>
);
} else if (kicked || banned) {
const roomName = this._roomNameElement(_t('This room'));
const kickerMember = this.props.room.currentState.getMember(
myMember.events.member.getSender()
myMember.events.member.getSender(),
);
const kickerName = kickerMember ?
kickerMember.name : myMember.events.member.getSender();
let reason;
if (myMember.events.member.getContent().reason) {
reason = <div>{_t("Reason: %(reasonText)s", {reasonText: myMember.events.member.getContent().reason})}</div>
reason = <div>{ _t("Reason: %(reasonText)s", {reasonText: myMember.events.member.getContent().reason}) }</div>;
}
let rejoinBlock;
if (!banned) {
rejoinBlock = <div><a onClick={ this.props.onJoinClick }><b>{_t("Rejoin")}</b></a></div>;
rejoinBlock = <div><a onClick={this.props.onJoinClick}><b>{ _t("Rejoin") }</b></a></div>;
}
let actionText;
if (kicked) {
actionText = _t("You have been kicked from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName});
}
else if (banned) {
} else if (banned) {
actionText = _t("You have been banned from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName});
} // no other options possible due to the kicked || banned check above.
joinBlock = (
<div>
<div className="mx_RoomPreviewBar_join_text">
{actionText}
{ actionText }
<br />
{reason}
{rejoinBlock}
<a onClick={ this.props.onForgetClick }><b>{_t("Forget room")}</b></a>
{ reason }
{ rejoinBlock }
<a onClick={this.props.onForgetClick}><b>{ _t("Forget room") }</b></a>
</div>
</div>
);
} else if (this.props.error) {
var name = this.props.roomAlias || _t("This room");
var error;
const name = this.props.roomAlias || _t("This room");
let error;
if (this.props.error.errcode == 'M_NOT_FOUND') {
error = _t("%(roomName)s does not exist.", {roomName: name});
} else {
@ -206,11 +204,11 @@ module.exports = React.createClass({
<div>
<div className="mx_RoomPreviewBar_join_text">
{ _t('You are trying to access %(roomName)s.', {roomName: name}) }
<br/>
<br />
{ _tJsx("<a>Click here</a> to join the discussion!",
/<a>(.*?)<\/a>/,
(sub) => <a onClick={ this.props.onJoinClick }><b>{sub}</b></a>
)}
(sub) => <a onClick={this.props.onJoinClick}><b>{ sub }</b></a>,
) }
</div>
</div>
);
@ -232,5 +230,5 @@ module.exports = React.createClass({
</div>
</div>
);
}
},
});

View file

@ -17,7 +17,7 @@ limitations under the License.
import Promise from 'bluebird';
import React from 'react';
import { _t, _tJsx } from '../../../languageHandler';
import { _t, _tJsx, _td } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import SdkConfig from '../../../SdkConfig';
import sdk from '../../../index';
@ -31,14 +31,39 @@ import AccessibleButton from '../elements/AccessibleButton';
// parse a string as an integer; if the input is undefined, or cannot be parsed
// as an integer, return a default.
function parseIntWithDefault(val, def) {
var res = parseInt(val);
const res = parseInt(val);
return isNaN(res) ? def : res;
}
const plEventsToLabels = {
// These will be translated for us later.
"m.room.avatar": _td("To change the room's avatar, you must be a"),
"m.room.name": _td("To change the room's name, you must be a"),
"m.room.canonical_alias": _td("To change the room's main address, you must be a"),
"m.room.history_visibility": _td("To change the room's history visibility, you must be a"),
"m.room.power_levels": _td("To change the permissions in the room, you must be a"),
"m.room.topic": _td("To change the topic, you must be a"),
"im.vector.modular.widgets": _td("To modify widgets in the room, you must be a"),
};
const plEventsToShow = {
// If an event is listed here, it will be shown in the PL settings. Defaults will be calculated.
"m.room.avatar": {isState: true},
"m.room.name": {isState: true},
"m.room.canonical_alias": {isState: true},
"m.room.history_visibility": {isState: true},
"m.room.power_levels": {isState: true},
"m.room.topic": {isState: true},
"im.vector.modular.widgets": {isState: true},
};
const BannedUser = React.createClass({
propTypes: {
canUnban: React.PropTypes.bool,
member: React.PropTypes.object.isRequired, // js-sdk RoomMember
by: React.PropTypes.string.isRequired,
reason: React.PropTypes.string,
},
@ -77,8 +102,10 @@ const BannedUser = React.createClass({
return (
<li>
{ unbanButton }
<strong>{this.props.member.name}</strong> {this.props.member.userId}
{this.props.reason ? " " +_t('Reason') + ": " + this.props.reason : ""}
<span title={_t("Banned by %(displayName)s", {displayName: this.props.by})}>
<strong>{ this.props.member.name }</strong> { this.props.member.userId }
{ this.props.reason ? " " +_t('Reason') + ": " + this.props.reason : "" }
</span>
</li>
);
},
@ -93,7 +120,7 @@ module.exports = React.createClass({
},
getInitialState: function() {
var tags = {};
const tags = {};
Object.keys(this.props.room.tags).forEach(function(tagName) {
tags[tagName] = ['yep'];
});
@ -122,7 +149,7 @@ module.exports = React.createClass({
MatrixClientPeg.get().on("RoomMember.membership", this._onRoomMemberMembership);
MatrixClientPeg.get().getRoomDirectoryVisibility(
this.props.room.roomId
this.props.room.roomId,
).done((result) => {
this.setState({ isRoomPublished: result.visibility === "public" });
this._originalIsRoomPublished = result.visibility === "public";
@ -152,13 +179,13 @@ module.exports = React.createClass({
setName: function(name) {
this.setState({
name: name
name: name,
});
},
setTopic: function(topic) {
this.setState({
topic: topic
topic: topic,
});
},
@ -169,7 +196,7 @@ module.exports = React.createClass({
* `{ state: "fulfilled", value: v }` or `{ state: "rejected", reason: r }`.
*/
save: function() {
var stateWasSetDefer = Promise.defer();
const stateWasSetDefer = Promise.defer();
// the caller may have JUST called setState on stuff, so we need to re-render before saving
// else we won't use the latest values of things.
// We can be a bit cheeky here and set a loading flag, and listen for the callback on that
@ -196,8 +223,8 @@ module.exports = React.createClass({
_calcSavePromises: function() {
const roomId = this.props.room.roomId;
var promises = this.saveAliases(); // returns Promise[]
var originalState = this.getInitialState();
const promises = this.saveAliases(); // returns Promise[]
const originalState = this.getInitialState();
// diff between original state and this.state to work out what has been changed
console.log("Original: %s", JSON.stringify(originalState));
@ -215,14 +242,14 @@ module.exports = React.createClass({
promises.push(MatrixClientPeg.get().sendStateEvent(
roomId, "m.room.history_visibility",
{ history_visibility: this.state.history_visibility },
""
"",
));
}
if (this.state.isRoomPublished !== originalState.isRoomPublished) {
promises.push(MatrixClientPeg.get().setRoomDirectoryVisibility(
roomId,
this.state.isRoomPublished ? "public" : "private"
this.state.isRoomPublished ? "public" : "private",
));
}
@ -230,7 +257,7 @@ module.exports = React.createClass({
promises.push(MatrixClientPeg.get().sendStateEvent(
roomId, "m.room.join_rules",
{ join_rule: this.state.join_rule },
""
"",
));
}
@ -238,33 +265,33 @@ module.exports = React.createClass({
promises.push(MatrixClientPeg.get().sendStateEvent(
roomId, "m.room.guest_access",
{ guest_access: this.state.guest_access },
""
"",
));
}
// power levels
var powerLevels = this._getPowerLevels();
const powerLevels = this._getPowerLevels();
if (powerLevels) {
promises.push(MatrixClientPeg.get().sendStateEvent(
roomId, "m.room.power_levels", powerLevels, ""
roomId, "m.room.power_levels", powerLevels, "",
));
}
// tags
if (this.state.tags_changed) {
var tagDiffs = ObjectUtils.getKeyValueArrayDiffs(originalState.tags, this.state.tags);
const tagDiffs = ObjectUtils.getKeyValueArrayDiffs(originalState.tags, this.state.tags);
// [ {place: add, key: "m.favourite", val: ["yep"]} ]
tagDiffs.forEach(function(diff) {
switch (diff.place) {
case "add":
promises.push(
MatrixClientPeg.get().setRoomTag(roomId, diff.key, {})
MatrixClientPeg.get().setRoomTag(roomId, diff.key, {}),
);
break;
case "del":
promises.push(
MatrixClientPeg.get().deleteRoomTag(roomId, diff.key)
MatrixClientPeg.get().deleteRoomTag(roomId, diff.key),
);
break;
default:
@ -275,18 +302,21 @@ module.exports = React.createClass({
}
// color scheme
var p;
let p;
p = this.saveColor();
if (!p.isFulfilled()) {
promises.push(p);
}
// url preview settings
var ps = this.saveUrlPreviewSettings();
const ps = this.saveUrlPreviewSettings();
if (ps.length > 0) {
promises.push(ps);
}
// related groups
promises.push(this.saveRelatedGroups());
// encryption
p = this.saveEnableEncryption();
if (!p.isFulfilled()) {
@ -304,6 +334,11 @@ module.exports = React.createClass({
return this.refs.alias_settings.saveSettings();
},
saveRelatedGroups: function() {
if (!this.refs.related_groups) { return Promise.resolve(); }
return this.refs.related_groups.saveSettings();
},
saveColor: function() {
if (!this.refs.color_settings) { return Promise.resolve(); }
return this.refs.color_settings.saveSettings();
@ -317,13 +352,13 @@ module.exports = React.createClass({
saveEnableEncryption: function() {
if (!this.refs.encrypt) { return Promise.resolve(); }
var encrypt = this.refs.encrypt.checked;
const encrypt = this.refs.encrypt.checked;
if (!encrypt) { return Promise.resolve(); }
var roomId = this.props.room.roomId;
const roomId = this.props.room.roomId;
return MatrixClientPeg.get().sendStateEvent(
roomId, "m.room.encryption",
{ algorithm: "m.megolm.v1.aes-sha2" }
{ algorithm: "m.megolm.v1.aes-sha2" },
);
},
@ -335,7 +370,7 @@ module.exports = React.createClass({
},
_isRoomBlacklistUnverified: function() {
var blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom;
const blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom;
if (blacklistUnverifiedDevicesPerRoom) {
return blacklistUnverifiedDevicesPerRoom[this.props.room.roomId];
}
@ -343,7 +378,7 @@ module.exports = React.createClass({
},
_setRoomBlacklistUnverified: function(value) {
var blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom || {};
const blacklistUnverifiedDevicesPerRoom = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevicesPerRoom || {};
blacklistUnverifiedDevicesPerRoom[this.props.room.roomId] = value;
UserSettingsStore.setLocalSetting('blacklistUnverifiedDevicesPerRoom', blacklistUnverifiedDevicesPerRoom);
@ -361,10 +396,15 @@ module.exports = React.createClass({
_getPowerLevels: function() {
if (!this.state.power_levels_changed) return undefined;
var powerLevels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
let powerLevels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
powerLevels = powerLevels ? powerLevels.getContent() : {};
var newPowerLevels = {
for (const key of Object.keys(this.refs).filter((k) => k.startsWith("event_levels_"))) {
const eventType = key.substring("event_levels_".length);
powerLevels.events[eventType] = parseInt(this.refs[key].getValue());
}
const newPowerLevels = {
ban: parseInt(this.refs.ban.getValue()),
kick: parseInt(this.refs.kick.getValue()),
redact: parseInt(this.refs.redact.getValue()),
@ -381,39 +421,40 @@ module.exports = React.createClass({
onPowerLevelsChanged: function() {
this.setState({
power_levels_changed: true
power_levels_changed: true,
});
},
_yankValueFromEvent: function(stateEventType, keyName, defaultValue) {
// E.g.("m.room.name","name") would yank the "name" content key from "m.room.name"
var event = this.props.room.currentState.getStateEvents(stateEventType, '');
const event = this.props.room.currentState.getStateEvents(stateEventType, '');
if (!event) {
return defaultValue;
}
return event.getContent()[keyName] || defaultValue;
const content = event.getContent();
return keyName in content ? content[keyName] : defaultValue;
},
_onHistoryRadioToggle: function(ev) {
var self = this;
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const self = this;
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
// cancel the click unless the user confirms it
ev.preventDefault();
var value = ev.target.value;
const value = ev.target.value;
Modal.createTrackedDialog('Privacy warning', '', QuestionDialog, {
title: _t('Privacy warning'),
description:
<div>
{ _t('Changes to who can read history will only apply to future messages in this room') }.<br/>
{ _t('Changes to who can read history will only apply to future messages in this room') }.<br />
{ _t('The visibility of existing history will be unchanged') }.
</div>,
button: _t('Continue'),
onFinished: function(confirmed) {
if (confirmed) {
self.setState({
history_visibility: value
history_visibility: value,
});
}
},
@ -421,7 +462,6 @@ module.exports = React.createClass({
},
_onRoomAccessRadioToggle: function(ev) {
// join_rule
// INVITE | PUBLIC
// ----------------------+----------------
@ -459,7 +499,7 @@ module.exports = React.createClass({
_onToggle: function(keyName, checkedValue, uncheckedValue, ev) {
console.log("Checkbox toggle: %s %s", keyName, ev.target.checked);
var state = {};
const state = {};
state[keyName] = ev.target.checked ? checkedValue : uncheckedValue;
this.setState(state);
},
@ -468,26 +508,24 @@ module.exports = React.createClass({
if (event.target.checked) {
if (tagName === 'm.favourite') {
delete this.state.tags['m.lowpriority'];
}
else if (tagName === 'm.lowpriority') {
} else if (tagName === 'm.lowpriority') {
delete this.state.tags['m.favourite'];
}
this.state.tags[tagName] = this.state.tags[tagName] || ["yep"];
}
else {
} else {
delete this.state.tags[tagName];
}
this.setState({
tags: this.state.tags,
tags_changed: true
tags_changed: true,
});
},
mayChangeRoomAccess: function() {
var cli = MatrixClientPeg.get();
var roomState = this.props.room.currentState;
const cli = MatrixClientPeg.get();
const roomState = this.props.room.currentState;
return (roomState.mayClientSendStateEvent("m.room.join_rules", cli) &&
roomState.mayClientSendStateEvent("m.room.guest_access", cli));
},
@ -504,8 +542,8 @@ module.exports = React.createClass({
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
}, function(err) {
var errCode = err.errcode || _t('unknown error code');
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const errCode = err.errcode || _t('unknown error code');
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, {
title: _t('Error'),
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
@ -516,7 +554,7 @@ module.exports = React.createClass({
onEnableEncryptionClick() {
if (!this.refs.encrypt.checked) return;
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('E2E Enable Warning', '', QuestionDialog, {
title: _t('Warning!'),
description: (
@ -528,7 +566,7 @@ module.exports = React.createClass({
<p>{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.</p>
</div>
),
onFinished: confirm=>{
onFinished: (confirm)=>{
if (!confirm) {
this.refs.encrypt.checked = false;
}
@ -541,18 +579,26 @@ module.exports = React.createClass({
this.forceUpdate();
},
_renderEncryptionSection: function() {
var cli = MatrixClientPeg.get();
var roomState = this.props.room.currentState;
var isEncrypted = cli.isRoomEncrypted(this.props.room.roomId);
var isGlobalBlacklistUnverified = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevices;
var isRoomBlacklistUnverified = this._isRoomBlacklistUnverified();
_populateDefaultPlEvents: function(eventsSection, stateLevel, eventsLevel) {
for (const desiredEvent of Object.keys(plEventsToShow)) {
if (!(desiredEvent in eventsSection)) {
eventsSection[desiredEvent] = (plEventsToShow[desiredEvent].isState ? stateLevel : eventsLevel);
}
}
},
var settings =
_renderEncryptionSection: function() {
const cli = MatrixClientPeg.get();
const roomState = this.props.room.currentState;
const isEncrypted = cli.isRoomEncrypted(this.props.room.roomId);
const isGlobalBlacklistUnverified = UserSettingsStore.getLocalSettings().blacklistUnverifiedDevices;
const isRoomBlacklistUnverified = this._isRoomBlacklistUnverified();
const settings =
<label>
<input type="checkbox" ref="blacklistUnverified"
defaultChecked={ isGlobalBlacklistUnverified || isRoomBlacklistUnverified }
disabled={ isGlobalBlacklistUnverified || (this.refs.encrypt && !this.refs.encrypt.checked) }/>
defaultChecked={isGlobalBlacklistUnverified || isRoomBlacklistUnverified}
disabled={isGlobalBlacklistUnverified || (this.refs.encrypt && !this.refs.encrypt.checked)} />
{ _t('Never send encrypted messages to unverified devices in this room from this device') }.
</label>;
@ -560,7 +606,7 @@ module.exports = React.createClass({
return (
<div>
<label>
<input type="checkbox" ref="encrypt" onClick={ this.onEnableEncryptionClick }/>
<input type="checkbox" ref="encrypt" onClick={this.onEnableEncryptionClick} />
<img className="mx_RoomSettings_e2eIcon mx_filterFlipColor" src="img/e2e-unencrypted.svg" width="12" height="12" />
{ _t('Enable encryption') } { _t('(warning: cannot be disabled again!)') }
</label>
@ -587,58 +633,68 @@ module.exports = React.createClass({
// TODO: go through greying out things you don't have permission to change
// (or turning them into informative stuff)
var AliasSettings = sdk.getComponent("room_settings.AliasSettings");
var ColorSettings = sdk.getComponent("room_settings.ColorSettings");
var UrlPreviewSettings = sdk.getComponent("room_settings.UrlPreviewSettings");
var EditableText = sdk.getComponent('elements.EditableText');
var PowerSelector = sdk.getComponent('elements.PowerSelector');
var Loader = sdk.getComponent("elements.Spinner");
const AliasSettings = sdk.getComponent("room_settings.AliasSettings");
const ColorSettings = sdk.getComponent("room_settings.ColorSettings");
const UrlPreviewSettings = sdk.getComponent("room_settings.UrlPreviewSettings");
const RelatedGroupSettings = sdk.getComponent("room_settings.RelatedGroupSettings");
const EditableText = sdk.getComponent('elements.EditableText');
const PowerSelector = sdk.getComponent('elements.PowerSelector');
const Loader = sdk.getComponent("elements.Spinner");
var cli = MatrixClientPeg.get();
var roomState = this.props.room.currentState;
var user_id = cli.credentials.userId;
const cli = MatrixClientPeg.get();
const roomState = this.props.room.currentState;
const user_id = cli.credentials.userId;
var power_level_event = roomState.getStateEvents('m.room.power_levels', '');
var power_levels = power_level_event ? power_level_event.getContent() : {};
var events_levels = power_levels.events || {};
var user_levels = power_levels.users || {};
const power_level_event = roomState.getStateEvents('m.room.power_levels', '');
const power_levels = power_level_event ? power_level_event.getContent() : {};
const events_levels = power_levels.events || {};
const user_levels = power_levels.users || {};
var ban_level = parseIntWithDefault(power_levels.ban, 50);
var kick_level = parseIntWithDefault(power_levels.kick, 50);
var redact_level = parseIntWithDefault(power_levels.redact, 50);
var invite_level = parseIntWithDefault(power_levels.invite, 50);
var send_level = parseIntWithDefault(power_levels.events_default, 0);
var state_level = power_level_event ? parseIntWithDefault(power_levels.state_default, 50) : 0;
var default_user_level = parseIntWithDefault(power_levels.users_default, 0);
const ban_level = parseIntWithDefault(power_levels.ban, 50);
const kick_level = parseIntWithDefault(power_levels.kick, 50);
const redact_level = parseIntWithDefault(power_levels.redact, 50);
const invite_level = parseIntWithDefault(power_levels.invite, 50);
const send_level = parseIntWithDefault(power_levels.events_default, 0);
const state_level = power_level_event ? parseIntWithDefault(power_levels.state_default, 50) : 0;
const default_user_level = parseIntWithDefault(power_levels.users_default, 0);
var current_user_level = user_levels[user_id];
this._populateDefaultPlEvents(events_levels, state_level, send_level);
let current_user_level = user_levels[user_id];
if (current_user_level === undefined) {
current_user_level = default_user_level;
}
var can_change_levels = roomState.mayClientSendStateEvent("m.room.power_levels", cli);
const can_change_levels = roomState.mayClientSendStateEvent("m.room.power_levels", cli);
var canSetTag = !cli.isGuest();
const canSetTag = !cli.isGuest();
var self = this;
const self = this;
var userLevelsSection;
let relatedGroupsSection;
if (UserSettingsStore.isFeatureEnabled('feature_groups')) {
relatedGroupsSection = <RelatedGroupSettings ref="related_groups"
roomId={this.props.room.roomId}
canSetRelatedGroups={roomState.mayClientSendStateEvent("m.room.related_groups", cli)}
relatedGroupsEvent={this.props.room.currentState.getStateEvents('m.room.related_groups', '')} />;
}
let userLevelsSection;
if (Object.keys(user_levels).length) {
userLevelsSection =
<div>
<h3>{ _t('Privileged Users') }</h3>
<ul className="mx_RoomSettings_userLevels">
{Object.keys(user_levels).map(function(user, i) {
{ Object.keys(user_levels).map(function(user, i) {
return (
<li className="mx_RoomSettings_userLevel" key={user}>
{ _t("%(user)s is a", {user: user}) } <PowerSelector value={ user_levels[user] } disabled={true}/>
{ _t("%(user)s is a", {user: user}) } <PowerSelector value={user_levels[user]} disabled={true} />
</li>
);
})}
}) }
</ul>
</div>;
}
else {
} else {
userLevelsSection = <div>{ _t('No users have specific privileges in this room') }.</div>;
}
@ -650,18 +706,21 @@ module.exports = React.createClass({
<div>
<h3>{ _t('Banned users') }</h3>
<ul className="mx_RoomSettings_banned">
{banned.map(function(member) {
{ banned.map(function(member) {
const banEvent = member.events.member.getContent();
const sender = self.props.room.getMember(member.events.member.getSender());
let bannedBy = member.events.member.getSender(); // start by falling back to mxid
if (sender) bannedBy = sender.name;
return (
<BannedUser key={member.userId} canUnban={canBanUsers} member={member} reason={banEvent.reason} />
<BannedUser key={member.userId} canUnban={canBanUsers} member={member} reason={banEvent.reason} by={bannedBy} />
);
})}
}) }
</ul>
</div>;
}
var unfederatableSection;
if (this._yankValueFromEvent("m.room.create", "m.federate") === false) {
let unfederatableSection;
if (this._yankValueFromEvent("m.room.create", "m.federate", true) === false) {
unfederatableSection = (
<div className="mx_RoomSettings_powerLevel">
{ _t('This room is not accessible by remote Matrix servers') }.
@ -669,19 +728,18 @@ module.exports = React.createClass({
);
}
var leaveButton = null;
var myMember = this.props.room.getMember(user_id);
let leaveButton = null;
const myMember = this.props.room.getMember(user_id);
if (myMember) {
if (myMember.membership === "join") {
leaveButton = (
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={ this.onLeaveClick }>
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={this.onLeaveClick}>
{ _t('Leave room') }
</AccessibleButton>
);
}
else if (myMember.membership === "leave") {
} else if (myMember.membership === "leave") {
leaveButton = (
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={ this.onForgetClick }>
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={this.onForgetClick}>
{ _t('Forget room') }
</AccessibleButton>
);
@ -691,7 +749,7 @@ module.exports = React.createClass({
// TODO: support editing custom events_levels
// TODO: support editing custom user_levels
var tags = [
const tags = [
{ name: "m.favourite", label: _t('Favourite'), ref: "tag_favourite" },
{ name: "m.lowpriority", label: _t('Low priority'), ref: "tag_lowpriority" },
];
@ -706,13 +764,13 @@ module.exports = React.createClass({
if (canSetTag || self.state.tags) {
var tagsSection =
<div className="mx_RoomSettings_tags">
{_t("Tagged as: ")}{ canSetTag ?
{ _t("Tagged as: ") }{ canSetTag ?
(tags.map(function(tag, i) {
return (<label key={ i }>
return (<label key={i}>
<input type="checkbox"
ref={ tag.ref }
checked={ tag.name in self.state.tags }
onChange={ self._onTagChange.bind(self, tag.name) }/>
ref={tag.ref}
checked={tag.name in self.state.tags}
onChange={self._onTagChange.bind(self, tag.name)} />
{ tag.label }
</label>);
})) : (self.state.tags && self.state.tags.join) ? self.state.tags.join(", ") : ""
@ -722,11 +780,11 @@ module.exports = React.createClass({
// If there is no history_visibility, it is assumed to be 'shared'.
// http://matrix.org/docs/spec/r0.0.0/client_server.html#id31
var historyVisibility = this.state.history_visibility || "shared";
const historyVisibility = this.state.history_visibility || "shared";
var addressWarning;
var aliasEvents = this.props.room.currentState.getStateEvents('m.room.aliases') || [];
var aliasCount = 0;
let addressWarning;
const aliasEvents = this.props.room.currentState.getStateEvents('m.room.aliases') || [];
let aliasCount = 0;
aliasEvents.forEach((event) => {
aliasCount += event.getContent().aliases.length;
});
@ -737,16 +795,16 @@ module.exports = React.createClass({
{ _tJsx(
'To link to a room it must have <a>an address</a>.',
/<a>(.*?)<\/a>/,
(sub) => <a href="#addresses">{sub}</a>
)}
(sub) => <a href="#addresses">{ sub }</a>,
) }
</div>;
}
var inviteGuestWarning;
let inviteGuestWarning;
if (this.state.join_rule !== "public" && this.state.guest_access === "forbidden") {
inviteGuestWarning =
<div className="mx_RoomSettings_warning">
{ _t('Guests cannot join this room even if explicitly invited.') } <a href="#" onClick={ (e) => {
{ _t('Guests cannot join this room even if explicitly invited.') } <a href="#" onClick={(e) => {
this.setState({ join_rule: "invite", guest_access: "can_join" });
e.preventDefault();
}}>{ _t('Click here to fix') }</a>.
@ -766,61 +824,61 @@ module.exports = React.createClass({
{ inviteGuestWarning }
<label>
<input type="radio" name="roomVis" value="invite_only"
disabled={ !this.mayChangeRoomAccess() }
disabled={!this.mayChangeRoomAccess()}
onChange={this._onRoomAccessRadioToggle}
checked={this.state.join_rule !== "public"}/>
checked={this.state.join_rule !== "public"} />
{ _t('Only people who have been invited') }
</label>
<label>
<input type="radio" name="roomVis" value="public_no_guests"
disabled={ !this.mayChangeRoomAccess() }
disabled={!this.mayChangeRoomAccess()}
onChange={this._onRoomAccessRadioToggle}
checked={this.state.join_rule === "public" && this.state.guest_access !== "can_join"}/>
checked={this.state.join_rule === "public" && this.state.guest_access !== "can_join"} />
{ _t('Anyone who knows the room\'s link, apart from guests') }
</label>
<label>
<input type="radio" name="roomVis" value="public_with_guests"
disabled={ !this.mayChangeRoomAccess() }
disabled={!this.mayChangeRoomAccess()}
onChange={this._onRoomAccessRadioToggle}
checked={this.state.join_rule === "public" && this.state.guest_access === "can_join"}/>
checked={this.state.join_rule === "public" && this.state.guest_access === "can_join"} />
{ _t('Anyone who knows the room\'s link, including guests') }
</label>
{ addressWarning }
<br/>
<br />
{ this._renderEncryptionSection() }
<label>
<input type="checkbox" disabled={ !roomState.mayClientSendStateEvent("m.room.aliases", cli) }
onChange={ this._onToggle.bind(this, "isRoomPublished", true, false)}
checked={this.state.isRoomPublished}/>
{_t("Publish this room to the public in %(domain)s's room directory?", { domain: MatrixClientPeg.get().getDomain() })}
<input type="checkbox" disabled={!roomState.mayClientSendStateEvent("m.room.aliases", cli)}
onChange={this._onToggle.bind(this, "isRoomPublished", true, false)}
checked={this.state.isRoomPublished} />
{ _t("Publish this room to the public in %(domain)s's room directory?", { domain: MatrixClientPeg.get().getDomain() }) }
</label>
</div>
<div className="mx_RoomSettings_settings">
<h3>{ _t('Who can read history?') }</h3>
<label>
<input type="radio" name="historyVis" value="world_readable"
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
disabled={!roomState.mayClientSendStateEvent("m.room.history_visibility", cli)}
checked={historyVisibility === "world_readable"}
onChange={this._onHistoryRadioToggle} />
{_t("Anyone")}
{ _t("Anyone") }
</label>
<label>
<input type="radio" name="historyVis" value="shared"
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
disabled={!roomState.mayClientSendStateEvent("m.room.history_visibility", cli)}
checked={historyVisibility === "shared"}
onChange={this._onHistoryRadioToggle} />
{ _t('Members only') } ({ _t('since the point in time of selecting this option') })
</label>
<label>
<input type="radio" name="historyVis" value="invited"
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
disabled={!roomState.mayClientSendStateEvent("m.room.history_visibility", cli)}
checked={historyVisibility === "invited"}
onChange={this._onHistoryRadioToggle} />
{ _t('Members only') } ({ _t('since they were invited') })
</label>
<label >
<input type="radio" name="historyVis" value="joined"
disabled={ !roomState.mayClientSendStateEvent("m.room.history_visibility", cli) }
disabled={!roomState.mayClientSendStateEvent("m.room.history_visibility", cli)}
checked={historyVisibility === "joined"}
onChange={this._onHistoryRadioToggle} />
{ _t('Members only') } ({ _t('since they joined') })
@ -834,11 +892,11 @@ module.exports = React.createClass({
<ColorSettings ref="color_settings" room={this.props.room} />
</div>
<a id="addresses"/>
<a id="addresses" />
<AliasSettings ref="alias_settings"
roomId={this.props.room.roomId}
canSetCanonicalAlias={ roomState.mayClientSendStateEvent("m.room.canonical_alias", cli) }
canSetCanonicalAlias={roomState.mayClientSendStateEvent("m.room.canonical_alias", cli)}
canSetAliases={
true
/* Originally, we arbitrarily restricted creating aliases to room admins: roomState.mayClientSendStateEvent("m.room.aliases", cli) */
@ -846,47 +904,53 @@ module.exports = React.createClass({
canonicalAliasEvent={this.props.room.currentState.getStateEvents('m.room.canonical_alias', '')}
aliasEvents={this.props.room.currentState.getStateEvents('m.room.aliases')} />
{ relatedGroupsSection }
<UrlPreviewSettings ref="url_preview_settings" room={this.props.room} />
<h3>{ _t('Permissions') }</h3>
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('The default role for new room members is') } </span>
<PowerSelector ref="users_default" value={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < default_user_level} onChange={this.onPowerLevelsChanged}/>
<PowerSelector ref="users_default" value={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < default_user_level} onChange={this.onPowerLevelsChanged} />
</div>
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send messages') }, { _t('you must be a') } </span>
<PowerSelector ref="events_default" value={send_level} controlled={false} disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send messages, you must be a') } </span>
<PowerSelector ref="events_default" value={send_level} controlled={false} disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged} />
</div>
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To invite users into the room') }, { _t('you must be a') } </span>
<PowerSelector ref="invite" value={invite_level} controlled={false} disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To invite users into the room, you must be a') } </span>
<PowerSelector ref="invite" value={invite_level} controlled={false} disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged} />
</div>
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To configure the room') }, { _t('you must be a') } </span>
<PowerSelector ref="state_default" value={state_level} controlled={false} disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To configure the room, you must be a') } </span>
<PowerSelector ref="state_default" value={state_level} controlled={false} disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged} />
</div>
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To kick users') }, { _t('you must be a') } </span>
<PowerSelector ref="kick" value={kick_level} controlled={false} disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To kick users, you must be a') } </span>
<PowerSelector ref="kick" value={kick_level} controlled={false} disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged} />
</div>
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To ban users') }, { _t('you must be a') } </span>
<PowerSelector ref="ban" value={ban_level} controlled={false} disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To ban users, you must be a') } </span>
<PowerSelector ref="ban" value={ban_level} controlled={false} disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged} />
</div>
<div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To remove other users\' messages') }, { _t('you must be a') } </span>
<PowerSelector ref="redact" value={redact_level} controlled={false} disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To remove other users\' messages, you must be a') } </span>
<PowerSelector ref="redact" value={redact_level} controlled={false} disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged} />
</div>
{Object.keys(events_levels).map(function(event_type, i) {
{ Object.keys(events_levels).map(function(event_type, i) {
let label = plEventsToLabels[event_type];
if (label) label = _t(label);
else label = _tJsx("To send events of type <eventType/>, you must be a", /<eventType\/>/, () => <code>{ event_type }</code>);
return (
<div className="mx_RoomSettings_powerLevel" key={event_type}>
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send events of type') } <code>{ event_type }</code>, { _t('you must be a') } </span>
<PowerSelector value={ events_levels[event_type] } controlled={false} disabled={true} onChange={self.onPowerLevelsChanged}/>
<span className="mx_RoomSettings_powerLevelKey">{ label } </span>
<PowerSelector ref={"event_levels_"+event_type} value={events_levels[event_type]} onChange={self.onPowerLevelsChanged}
controlled={false} disabled={!can_change_levels || current_user_level < events_levels[event_type]} />
</div>
);
})}
}) }
{ unfederatableSection }
</div>
@ -901,5 +965,5 @@ module.exports = React.createClass({
</div>
</div>
);
}
},
});

View file

@ -17,17 +17,17 @@ limitations under the License.
'use strict';
var React = require('react');
var ReactDOM = require("react-dom");
var classNames = require('classnames');
var MatrixClientPeg = require('../../../MatrixClientPeg');
const React = require('react');
const ReactDOM = require("react-dom");
const classNames = require('classnames');
const MatrixClientPeg = require('../../../MatrixClientPeg');
import DMRoomMap from '../../../utils/DMRoomMap';
var sdk = require('../../../index');
var ContextualMenu = require('../../structures/ContextualMenu');
var RoomNotifs = require('../../../RoomNotifs');
var FormattingUtils = require('../../../utils/FormattingUtils');
const sdk = require('../../../index');
const ContextualMenu = require('../../structures/ContextualMenu');
const RoomNotifs = require('../../../RoomNotifs');
const FormattingUtils = require('../../../utils/FormattingUtils');
import AccessibleButton from '../elements/AccessibleButton';
var UserSettingsStore = require('../../../UserSettingsStore');
const UserSettingsStore = require('../../../UserSettingsStore');
import ActiveRoomObserver from '../../../ActiveRoomObserver';
import RoomViewStore from '../../../stores/RoomViewStore';
@ -56,8 +56,8 @@ module.exports = React.createClass({
getInitialState: function() {
return({
hover : false,
badgeHover : false,
hover: false,
badgeHover: false,
menuDisplayed: false,
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
selected: this.props.room.roomId === RoomViewStore.getRoomId(),
@ -74,7 +74,7 @@ module.exports = React.createClass({
},
_isDirectMessageRoom: function(roomId) {
var dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
const dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
if (dmRooms) {
return true;
} else {
@ -102,7 +102,7 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
if (cli) {
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
}
@ -116,12 +116,12 @@ module.exports = React.createClass({
},
onMouseEnter: function() {
this.setState( { hover : true });
this.setState( { hover: true });
this.badgeOnMouseEnter();
},
onMouseLeave: function() {
this.setState( { hover : false });
this.setState( { hover: false });
this.badgeOnMouseLeave();
},
@ -129,25 +129,24 @@ module.exports = React.createClass({
// Only allow non-guests to access the context menu
// and only change it if it needs to change
if (!MatrixClientPeg.get().isGuest() && !this.state.badgeHover) {
this.setState( { badgeHover : true } );
this.setState( { badgeHover: true } );
}
},
badgeOnMouseLeave: function() {
this.setState( { badgeHover : false } );
this.setState( { badgeHover: false } );
},
onBadgeClicked: function(e) {
// Only allow none guests to access the context menu
if (!MatrixClientPeg.get().isGuest()) {
// If the badge is clicked, then no longer show tooltip
if (this.props.collapsed) {
this.setState({ hover: false });
}
var RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
var elementRect = e.target.getBoundingClientRect();
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
const elementRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const x = elementRect.right + window.pageXOffset + 3;
@ -155,7 +154,7 @@ module.exports = React.createClass({
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
var self = this;
const self = this;
ContextualMenu.createMenu(RoomTileContextMenu, {
chevronOffset: chevronOffset,
left: x,
@ -164,7 +163,7 @@ module.exports = React.createClass({
onFinished: function() {
self.setState({ menuDisplayed: false });
self.props.refreshSubList();
}
},
});
this.setState({ menuDisplayed: true });
}
@ -173,17 +172,17 @@ module.exports = React.createClass({
},
render: function() {
var myUserId = MatrixClientPeg.get().credentials.userId;
var me = this.props.room.currentState.members[myUserId];
const myUserId = MatrixClientPeg.get().credentials.userId;
const me = this.props.room.currentState.members[myUserId];
var notificationCount = this.props.room.getUnreadNotificationCount();
const notificationCount = this.props.room.getUnreadNotificationCount();
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
const badges = notifBadges || mentionBadges;
var classes = classNames({
const classes = classNames({
'mx_RoomTile': true,
'mx_RoomTile_selected': this.state.selected,
'mx_RoomTile_unread': this.props.unread,
@ -194,53 +193,53 @@ module.exports = React.createClass({
'mx_RoomTile_noBadges': !badges,
});
var avatarClasses = classNames({
const avatarClasses = classNames({
'mx_RoomTile_avatar': true,
});
var badgeClasses = classNames({
const badgeClasses = classNames({
'mx_RoomTile_badge': true,
'mx_RoomTile_badgeButton': this.state.badgeHover || this.state.menuDisplayed,
});
// XXX: We should never display raw room IDs, but sometimes the
// room name js sdk gives is undefined (cannot repro this -- k)
var name = this.props.room.name || this.props.room.roomId;
let name = this.props.room.name || this.props.room.roomId;
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
var badge;
var badgeContent;
let badge;
let badgeContent;
if (this.state.badgeHover || this.state.menuDisplayed) {
badgeContent = "\u00B7\u00B7\u00B7";
} else if (badges) {
var limitedCount = FormattingUtils.formatCount(notificationCount);
const limitedCount = FormattingUtils.formatCount(notificationCount);
badgeContent = notificationCount ? limitedCount : '!';
} else {
badgeContent = '\u200B';
}
badge = <div className={ badgeClasses } onClick={this.onBadgeClicked}>{ badgeContent }</div>;
badge = <div className={badgeClasses} onClick={this.onBadgeClicked}>{ badgeContent }</div>;
const EmojiText = sdk.getComponent('elements.EmojiText');
var label;
var tooltip;
let label;
let tooltip;
if (!this.props.collapsed) {
var nameClasses = classNames({
const nameClasses = classNames({
'mx_RoomTile_name': true,
'mx_RoomTile_invite': this.props.isInvite,
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
});
if (this.state.selected) {
let nameSelected = <EmojiText>{name}</EmojiText>;
const nameSelected = <EmojiText>{ name }</EmojiText>;
label = <div title={ name } className={ nameClasses } dir="auto">{ nameSelected }</div>;
label = <div title={name} className={nameClasses} dir="auto">{ nameSelected }</div>;
} else {
label = <EmojiText element="div" title={ name } className={ nameClasses } dir="auto">{name}</EmojiText>;
label = <EmojiText element="div" title={name} className={nameClasses} dir="auto">{ name }</EmojiText>;
}
} else if (this.state.hover) {
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
tooltip = <RoomTooltip className="mx_RoomTile_tooltip" room={this.props.room} dir="auto" />;
}
@ -250,34 +249,34 @@ module.exports = React.createClass({
// incomingCallBox = <IncomingCallBox incomingCall={ this.props.incomingCall }/>;
//}
var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
var directMessageIndicator;
let directMessageIndicator;
if (this._isDirectMessageRoom(this.props.room.roomId)) {
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm"/>;
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
}
// These props are injected by React DnD,
// as defined by your `collect` function above:
var isDragging = this.props.isDragging;
var connectDragSource = this.props.connectDragSource;
var connectDropTarget = this.props.connectDropTarget;
const isDragging = this.props.isDragging;
const connectDragSource = this.props.connectDragSource;
const connectDropTarget = this.props.connectDropTarget;
let ret = (
<div> { /* Only native elements can be wrapped in a DnD object. */}
<div> { /* Only native elements can be wrapped in a DnD object. */ }
<AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<div className={avatarClasses}>
<div className="mx_RoomTile_avatar_container">
<RoomAvatar room={this.props.room} width={24} height={24} />
{directMessageIndicator}
{ directMessageIndicator }
</div>
</div>
<div className="mx_RoomTile_nameContainer">
{ label }
{ badge }
</div>
{/* { incomingCallBox } */}
{ /* { incomingCallBox } */ }
{ tooltip }
</AccessibleButton>
</div>
@ -287,5 +286,5 @@ module.exports = React.createClass({
if (connectDragSource) ret = connectDragSource(ret);
return ret;
}
},
});

View file

@ -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');
import { _t } from "../../../languageHandler";
module.exports = React.createClass({
@ -28,8 +28,8 @@ module.exports = React.createClass({
},
componentWillMount: function() {
var room = this.props.room;
var topic = room.currentState.getStateEvents('m.room.topic', '');
const room = this.props.room;
const topic = room.currentState.getStateEvents('m.room.topic', '');
this._initialTopic = topic ? topic.getContent().topic : '';
},
@ -38,15 +38,15 @@ module.exports = React.createClass({
},
render: function() {
var EditableText = sdk.getComponent("elements.EditableText");
const EditableText = sdk.getComponent("elements.EditableText");
return (
<EditableText ref="editor"
className="mx_RoomHeader_topic mx_RoomHeader_editable"
placeholderClassName="mx_RoomHeader_placeholder"
placeholder={_t("Add a topic")}
blurToCancel={ false }
initialValue={ this._initialTopic }
blurToCancel={false}
initialValue={this._initialTopic}
dir="auto" />
);
},

View file

@ -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: 'SearchResult',
@ -36,20 +36,20 @@ module.exports = React.createClass({
},
render: function() {
var DateSeparator = sdk.getComponent('messages.DateSeparator');
var EventTile = sdk.getComponent('rooms.EventTile');
var result = this.props.searchResult;
var mxEv = result.context.getEvent();
var eventId = mxEv.getId();
const DateSeparator = sdk.getComponent('messages.DateSeparator');
const EventTile = sdk.getComponent('rooms.EventTile');
const result = this.props.searchResult;
const mxEv = result.context.getEvent();
const eventId = mxEv.getId();
var ts1 = mxEv.getTs();
var ret = [<DateSeparator key={ts1 + "-search"} ts={ts1}/>];
const ts1 = mxEv.getTs();
const ret = [<DateSeparator key={ts1 + "-search"} ts={ts1} />];
var timeline = result.context.getTimeline();
const timeline = result.context.getTimeline();
for (var j = 0; j < timeline.length; j++) {
var ev = timeline[j];
const ev = timeline[j];
var highlights;
var contextual = (j != result.context.getOurEventIndex());
const contextual = (j != result.context.getOurEventIndex());
if (!contextual) {
highlights = this.props.searchHighlights;
}
@ -61,7 +61,7 @@ module.exports = React.createClass({
}
return (
<li data-scroll-tokens={eventId+"+"+j}>
{ret}
{ ret }
</li>);
},
});

View file

@ -13,16 +13,16 @@ 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 MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var sdk = require("../../../index");
const React = require('react');
const MatrixClientPeg = require("../../../MatrixClientPeg");
const Modal = require("../../../Modal");
const sdk = require("../../../index");
import { _t } from '../../../languageHandler';
var GeminiScrollbar = require('react-gemini-scrollbar');
const GeminiScrollbar = require('react-gemini-scrollbar');
// A list capable of displaying entities which conform to the SearchableEntity
// interface which is an object containing getJsx(): Jsx and matches(query: string): boolean
var SearchableEntityList = React.createClass({
const SearchableEntityList = React.createClass({
displayName: 'SearchableEntityList',
propTypes: {
@ -31,7 +31,7 @@ var SearchableEntityList = React.createClass({
onQueryChanged: React.PropTypes.func, // fn(inputText)
onSubmit: React.PropTypes.func, // fn(inputText)
entities: React.PropTypes.array,
truncateAt: React.PropTypes.number
truncateAt: React.PropTypes.number,
},
getDefaultProps: function() {
@ -40,7 +40,7 @@ var SearchableEntityList = React.createClass({
entities: [],
emptyQueryShowsAll: false,
onSubmit: function() {},
onQueryChanged: function(input) {}
onQueryChanged: function(input) {},
};
},
@ -49,14 +49,14 @@ var SearchableEntityList = React.createClass({
query: "",
focused: false,
truncateAt: this.props.truncateAt,
results: this.getSearchResults("", this.props.entities)
results: this.getSearchResults("", this.props.entities),
};
},
componentWillReceiveProps: function(newProps) {
// recalculate the search results in case we got new entities
this.setState({
results: this.getSearchResults(this.state.query, newProps.entities)
results: this.getSearchResults(this.state.query, newProps.entities),
});
},
@ -73,17 +73,17 @@ var SearchableEntityList = React.createClass({
setQuery: function(input) {
this.setState({
query: input,
results: this.getSearchResults(input, this.props.entities)
results: this.getSearchResults(input, this.props.entities),
});
},
onQueryChanged: function(ev) {
var q = ev.target.value;
const q = ev.target.value;
this.setState({
query: q,
// reset truncation if they back out the entire text
truncateAt: (q.length === 0 ? this.props.truncateAt : this.state.truncateAt),
results: this.getSearchResults(q, this.props.entities)
results: this.getSearchResults(q, this.props.entities),
}, () => {
// invoke the callback AFTER we've flushed the new state. We need to
// do this because onQueryChanged can result in new props being passed
@ -110,13 +110,13 @@ var SearchableEntityList = React.createClass({
_showAll: function() {
this.setState({
truncateAt: -1
truncateAt: -1,
});
},
_createOverflowEntity: function(overflowCount, totalCount) {
var EntityTile = sdk.getComponent("rooms.EntityTile");
var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const EntityTile = sdk.getComponent("rooms.EntityTile");
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
@ -127,7 +127,7 @@ var SearchableEntityList = React.createClass({
},
render: function() {
var inputBox;
let inputBox;
if (this.props.showInputBox) {
inputBox = (
@ -136,31 +136,30 @@ var SearchableEntityList = React.createClass({
onChange={this.onQueryChanged} value={this.state.query}
onFocus= {() => { this.setState({ focused: true }); }}
onBlur= {() => { this.setState({ focused: false }); }}
placeholder={ _t("Search") } />
placeholder={_t("Search")} />
</form>
);
}
var list;
let list;
if (this.state.results.length > 1 || this.state.focused) {
if (this.props.truncateAt) { // caller wants list truncated
var TruncatedList = sdk.getComponent("elements.TruncatedList");
const TruncatedList = sdk.getComponent("elements.TruncatedList");
list = (
<TruncatedList className="mx_SearchableEntityList_list"
truncateAt={this.state.truncateAt} // use state truncation as it may be expanded
createOverflowElement={this._createOverflowEntity}>
{this.state.results.map((entity) => {
{ this.state.results.map((entity) => {
return entity.getJsx();
})}
}) }
</TruncatedList>
);
}
else {
} else {
list = (
<div className="mx_SearchableEntityList_list">
{this.state.results.map((entity) => {
{ this.state.results.map((entity) => {
return entity.getJsx();
})}
}) }
</div>
);
}
@ -173,13 +172,13 @@ var SearchableEntityList = React.createClass({
}
return (
<div className={ "mx_SearchableEntityList " + (list ? "mx_SearchableEntityList_expanded" : "") }>
<div className={"mx_SearchableEntityList " + (list ? "mx_SearchableEntityList_expanded" : "")}>
{ inputBox }
{ list }
{ list ? <div className="mx_SearchableEntityList_hrWrapper"><hr/></div> : '' }
{ list ? <div className="mx_SearchableEntityList_hrWrapper"><hr /></div> : '' }
</div>
);
}
},
});
module.exports = SearchableEntityList;

View file

@ -17,9 +17,9 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
const sdk = require('../../../index');
module.exports = React.createClass({
displayName: 'TopUnreadMessagesBar',
@ -35,8 +35,8 @@ module.exports = React.createClass({
<div className="mx_TopUnreadMessagesBar_scrollUp"
onClick={this.props.onScrollUpClick}>
<img src="img/scrollto.svg" width="24" height="24"
alt={ _t('Scroll to unread messages') }
title={ _t('Scroll to unread messages') }/>
alt={_t('Scroll to unread messages')}
title={_t('Scroll to unread messages')} />
{ _t("Jump to first unread message.") }
</div>
<img className="mx_TopUnreadMessagesBar_close mx_filterFlipColor"

View file

@ -16,41 +16,41 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
var Avatar = require("../../../Avatar");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require('../../../index');
var dis = require('../../../dispatcher');
var Modal = require("../../../Modal");
const Avatar = require("../../../Avatar");
const MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require('../../../index');
const dis = require('../../../dispatcher');
const Modal = require("../../../Modal");
module.exports = React.createClass({
displayName: 'UserTile',
propTypes: {
user: React.PropTypes.any.isRequired // User
user: React.PropTypes.any.isRequired, // User
},
render: function() {
var EntityTile = sdk.getComponent("rooms.EntityTile");
var user = this.props.user;
var name = user.displayName || user.userId;
var active = -1;
const EntityTile = sdk.getComponent("rooms.EntityTile");
const user = this.props.user;
const name = user.displayName || user.userId;
let active = -1;
// FIXME: make presence data update whenever User.presence changes...
active = user.lastActiveAgo ?
(Date.now() - (user.lastPresenceTs - user.lastActiveAgo)) : -1;
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
var avatarJsx = (
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const avatarJsx = (
<BaseAvatar width={36} height={36} name={name} idName={user.userId}
url={ Avatar.avatarUrlForUser(user, 36, 36, "crop") } />
url={Avatar.avatarUrlForUser(user, 36, 36, "crop")} />
);
return (
<EntityTile {...this.props} presenceState={user.presence} presenceActiveAgo={active}
presenceCurrentlyActive={ user.currentlyActive }
presenceCurrentlyActive={user.currentlyActive}
name={name} title={user.userId} avatarJsx={avatarJsx} />
);
}
},
});

View file

@ -81,7 +81,7 @@ export default withMatrixClient(React.createClass({
this._promptForMsisdnVerificationCode(resp.msisdn);
}).catch((err) => {
console.error("Unable to add phone number: " + err);
let msg = err.message;
const msg = err.message;
Modal.createTrackedDialog('Add Phone Number Error', '', ErrorDialog, {
title: _t("Error"),
description: msg,
@ -94,22 +94,22 @@ export default withMatrixClient(React.createClass({
this.setState({msisdn_add_pending: true});
},
_promptForMsisdnVerificationCode:function (msisdn, err) {
_promptForMsisdnVerificationCode: function(msisdn, err) {
if (this._unmounted) return;
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
let msgElements = [
<div key="_static" >{ _t("A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", { msisdn: msisdn} ) }</div>
const msgElements = [
<div key="_static" >{ _t("A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", { msisdn: msisdn} ) }</div>,
];
if (err) {
let msg = err.error;
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
msg = _t("Incorrect verification code");
}
msgElements.push(<div key="_error" className="error">{msg}</div>);
msgElements.push(<div key="_error" className="error">{ msg }</div>);
}
Modal.createTrackedDialog('Prompt for MSISDN Verification Code', '', TextInputDialog, {
title: _t("Enter Code"),
description: <div>{msgElements}</div>,
description: <div>{ msgElements }</div>,
button: _t("Submit"),
onFinished: (should_verify, token) => {
if (!should_verify) {
@ -128,7 +128,7 @@ export default withMatrixClient(React.createClass({
if (this._unmounted) return;
this.setState({msisdn_add_pending: false});
}).done();
}
},
});
},
@ -146,7 +146,7 @@ export default withMatrixClient(React.createClass({
return (
<form className="mx_UserSettings_profileTableRow" onSubmit={this._onAddMsisdnSubmit}>
<div className="mx_UserSettings_profileLabelCell">
<label>{_t('Phone')}</label>
<label>{ _t('Phone') }</label>
</div>
<div className="mx_UserSettings_profileInputCell">
<div className="mx_UserSettings_phoneSection">
@ -158,7 +158,7 @@ export default withMatrixClient(React.createClass({
<input type="text"
ref={this._collectAddMsisdnInput}
className="mx_UserSettings_phoneNumberField"
placeholder={ _t('Add phone number') }
placeholder={_t('Add phone number')}
value={this.state.phoneNumber}
onChange={this._onPhoneNumberChange}
/>
@ -169,5 +169,5 @@ export default withMatrixClient(React.createClass({
</div>
</form>
);
}
}))
},
}));

View file

@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var sdk = require('../../../index');
const React = require('react');
const MatrixClientPeg = require("../../../MatrixClientPeg");
const sdk = require('../../../index');
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@ -28,7 +28,7 @@ module.exports = React.createClass({
showUploadSection: React.PropTypes.bool,
width: React.PropTypes.number,
height: React.PropTypes.number,
className: React.PropTypes.string
className: React.PropTypes.string,
},
Phases: {
@ -53,31 +53,57 @@ module.exports = React.createClass({
};
},
componentWillMount: function() {
MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents);
},
componentWillReceiveProps: function(newProps) {
if (this.avatarSet) {
// don't clobber what the user has just set
return;
}
this.setState({
avatarUrl: newProps.initialAvatarUrl
avatarUrl: newProps.initialAvatarUrl,
});
},
componentWillUnmount: function() {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
}
},
onRoomStateEvents: function(ev) {
if (!this.props.room) {
return;
}
if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'm.room.avatar'
|| ev.getSender() !== MatrixClientPeg.get().getUserId()) {
return;
}
if (!ev.getContent().url) {
this.avatarSet = false;
this.setState({}); // force update
}
},
setAvatarFromFile: function(file) {
var newUrl = null;
let newUrl = null;
this.setState({
phase: this.Phases.Uploading
phase: this.Phases.Uploading,
});
var self = this;
var httpPromise = MatrixClientPeg.get().uploadContent(file).then(function(url) {
const self = this;
const httpPromise = MatrixClientPeg.get().uploadContent(file).then(function(url) {
newUrl = url;
if (self.props.room) {
return MatrixClientPeg.get().sendStateEvent(
self.props.room.roomId,
'm.room.avatar',
{url: url},
''
'',
);
} else {
return MatrixClientPeg.get().setAvatarUrl(url);
@ -87,11 +113,11 @@ module.exports = React.createClass({
httpPromise.done(function() {
self.setState({
phase: self.Phases.Display,
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl)
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl),
});
}, function(error) {
self.setState({
phase: self.Phases.Error
phase: self.Phases.Error,
});
self.onError(error);
});
@ -106,31 +132,31 @@ module.exports = React.createClass({
onError: function(error) {
this.setState({
errorText: _t("Failed to upload profile picture!")
errorText: _t("Failed to upload profile picture!"),
});
},
render: function() {
var avatarImg;
let avatarImg;
// Having just set an avatar we just display that since it will take a little
// time to propagate through to the RoomAvatar.
if (this.props.room && !this.avatarSet) {
var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
avatarImg = <RoomAvatar room={this.props.room} width={ this.props.width } height={ this.props.height } resizeMethod='crop' />;
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
avatarImg = <RoomAvatar room={this.props.room} width={this.props.width} height={this.props.height} resizeMethod='crop' />;
} else {
var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
// XXX: FIXME: once we track in the JS what our own displayname is(!) then use it here rather than ?
avatarImg = <BaseAvatar width={this.props.width} height={this.props.height} resizeMethod='crop'
name='?' idName={ MatrixClientPeg.get().getUserIdLocalpart() } url={this.state.avatarUrl} />;
name='?' idName={MatrixClientPeg.get().getUserIdLocalpart()} url={this.state.avatarUrl} />;
}
var uploadSection;
let uploadSection;
if (this.props.showUploadSection) {
uploadSection = (
<div className={this.props.className}>
{_t("Upload new:")}
<input type="file" accept="image/*" onChange={this.onFileSelected}/>
{this.state.errorText}
{ _t("Upload new:") }
<input type="file" accept="image/*" onChange={this.onFileSelected} />
{ this.state.errorText }
</div>
);
}
@ -141,9 +167,9 @@ module.exports = React.createClass({
return (
<div>
<div className={this.props.className}>
{avatarImg}
{ avatarImg }
</div>
{uploadSection}
{ uploadSection }
</div>
);
case this.Phases.Uploading:
@ -152,5 +178,5 @@ module.exports = React.createClass({
<Loader />
);
}
}
},
});

View file

@ -15,23 +15,22 @@ limitations under the License.
*/
'use strict';
var React = require('react');
var sdk = require('../../../index');
var MatrixClientPeg = require("../../../MatrixClientPeg");
const React = require('react');
const sdk = require('../../../index');
const MatrixClientPeg = require("../../../MatrixClientPeg");
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'ChangeDisplayName',
_getDisplayName: function() {
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
return cli.getProfileInfo(cli.credentials.userId).then(function(result) {
var displayname = result.displayname;
let displayname = result.displayname;
if (!displayname) {
if (MatrixClientPeg.get().isGuest()) {
displayname = "Guest " + MatrixClientPeg.get().getUserIdLocalpart();
}
else {
} else {
displayname = MatrixClientPeg.get().getUserIdLocalpart();
}
}
@ -42,14 +41,14 @@ module.exports = React.createClass({
},
_changeDisplayName: function(new_displayname) {
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
return cli.setDisplayName(new_displayname).catch(function(e) {
throw new Error("Failed to set display name");
});
},
render: function() {
var EditableTextContainer = sdk.getComponent('elements.EditableTextContainer');
const EditableTextContainer = sdk.getComponent('elements.EditableTextContainer');
return (
<EditableTextContainer
getInitialValue={this._getDisplayName}
@ -57,5 +56,5 @@ module.exports = React.createClass({
blurToSubmit={true}
onSubmit={this._changeDisplayName} />
);
}
},
});

View file

@ -16,10 +16,10 @@ limitations under the License.
'use strict';
var React = require('react');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var sdk = require("../../../index");
const React = require('react');
const MatrixClientPeg = require("../../../MatrixClientPeg");
const Modal = require("../../../Modal");
const sdk = require("../../../index");
import Promise from 'bluebird';
import AccessibleButton from '../elements/AccessibleButton';
@ -45,7 +45,7 @@ module.exports = React.createClass({
Phases: {
Edit: "edit",
Uploading: "uploading",
Error: "error"
Error: "error",
},
getDefaultProps: function() {
@ -55,11 +55,11 @@ module.exports = React.createClass({
onCheckPassword: function(oldPass, newPass, confirmPass) {
if (newPass !== confirmPass) {
return {
error: _t("New passwords don't match")
error: _t("New passwords don't match"),
};
} else if (!newPass || newPass.length === 0) {
return {
error: _t("Passwords can't be empty")
error: _t("Passwords can't be empty"),
};
}
},
@ -112,7 +112,7 @@ module.exports = React.createClass({
'Changing password will currently reset any 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.'
'In future this will be improved.',
) } (<a href="https://github.com/vector-im/riot-web/issues/2671">https://github.com/vector-im/riot-web/issues/2671</a>)
</div>,
button: _t("Continue"),
@ -120,7 +120,7 @@ module.exports = React.createClass({
<button className="mx_Dialog_primary"
onClick={this._onExportE2eKeysClicked}>
{ _t('Export E2E room keys') }
</button>
</button>,
],
onFinished: (confirmed) => {
if (confirmed) {
@ -254,5 +254,5 @@ module.exports = React.createClass({
</div>
);
}
}
},
});

View file

@ -52,7 +52,7 @@ export default class DevicesPanel extends React.Component {
},
(error) => {
if (this._unmounted) { return; }
var errtxt;
let errtxt;
if (error.httpStatus == 404) {
// 404 probably means the HS doesn't yet support the API.
errtxt = _t("Your home server does not support device management.");
@ -61,7 +61,7 @@ export default class DevicesPanel extends React.Component {
errtxt = _t("Unable to load device list");
}
this.setState({deviceLoadError: errtxt});
}
},
);
}
@ -89,14 +89,14 @@ export default class DevicesPanel extends React.Component {
const removed_id = device.device_id;
this.setState((state, props) => {
const newDevices = state.devices.filter(
d => { return d.device_id != removed_id; }
(d) => { return d.device_id != removed_id; },
);
return { devices: newDevices };
});
}
_renderDevice(device) {
var DevicesPanelEntry = sdk.getComponent('settings.DevicesPanelEntry');
const DevicesPanelEntry = sdk.getComponent('settings.DevicesPanelEntry');
return (
<DevicesPanelEntry key={device.device_id} device={device}
onDeleted={()=>{this._onDeviceDeleted(device);}} />
@ -110,7 +110,7 @@ export default class DevicesPanel extends React.Component {
const classes = classNames(this.props.className, "error");
return (
<div className={classes}>
{this.state.deviceLoadError}
{ this.state.deviceLoadError }
</div>
);
}
@ -119,7 +119,7 @@ export default class DevicesPanel extends React.Component {
if (devices === undefined) {
// still loading
const classes = this.props.className;
return <Spinner className={classes}/>;
return <Spinner className={classes} />;
}
devices.sort(this._deviceCompare);
@ -128,12 +128,12 @@ export default class DevicesPanel extends React.Component {
return (
<div className={classes}>
<div className="mx_DevicesPanel_header">
<div className="mx_DevicesPanel_deviceId">{_t("Device ID")}</div>
<div className="mx_DevicesPanel_deviceName">{_t("Device Name")}</div>
<div className="mx_DevicesPanel_deviceLastSeen">{_t("Last seen")}</div>
<div className="mx_DevicesPanel_deviceId">{ _t("Device ID") }</div>
<div className="mx_DevicesPanel_deviceName">{ _t("Device Name") }</div>
<div className="mx_DevicesPanel_deviceLastSeen">{ _t("Last seen") }</div>
<div className="mx_DevicesPanel_deviceButtons"></div>
</div>
{devices.map(this._renderDevice)}
{ devices.map(this._renderDevice) }
</div>
);
}

View file

@ -69,7 +69,7 @@ export default class DevicesPanelEntry extends React.Component {
}
// pop up an interactive auth dialog
var InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
Modal.createTrackedDialog('Delete Device Dialog', '', InteractiveAuthDialog, {
title: _t("Authentication"),
@ -101,7 +101,7 @@ export default class DevicesPanelEntry extends React.Component {
this.props.onDeleted();
if (this._unmounted) { return; }
this.setState({ deleting: false });
}
},
);
}
@ -129,7 +129,7 @@ export default class DevicesPanelEntry extends React.Component {
let deleteButton;
if (this.state.deleteError) {
deleteButton = <div className="error">{this.state.deleteError}</div>;
deleteButton = <div className="error">{ this.state.deleteError }</div>;
} else {
deleteButton = (
<div className="mx_textButton"
@ -139,15 +139,15 @@ export default class DevicesPanelEntry extends React.Component {
);
}
var myDeviceClass = '';
let myDeviceClass = '';
if (device.device_id === MatrixClientPeg.get().getDeviceId()) {
myDeviceClass = " mx_DevicesPanel_myDevice";
}
return (
<div className={ "mx_DevicesPanel_device" + myDeviceClass }>
<div className={"mx_DevicesPanel_device" + myDeviceClass}>
<div className="mx_DevicesPanel_deviceId">
{device.device_id}
{ device.device_id }
</div>
<div className="mx_DevicesPanel_deviceName">
<EditableTextContainer initialValue={device.display_name}
@ -156,10 +156,10 @@ export default class DevicesPanelEntry extends React.Component {
/>
</div>
<div className="mx_DevicesPanel_lastSeen">
{lastSeen}
{ lastSeen }
</div>
<div className="mx_DevicesPanel_deviceButtons">
{deleteButton}
{ deleteButton }
</div>
</div>
);

View file

@ -15,9 +15,9 @@ limitations under the License.
*/
'use strict';
var React = require("react");
var Notifier = require("../../../Notifier");
var dis = require("../../../dispatcher");
const React = require("react");
const Notifier = require("../../../Notifier");
const dis = require("../../../dispatcher");
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@ -43,7 +43,7 @@ module.exports = React.createClass({
},
onClick: function() {
var self = this;
const self = this;
if (!Notifier.supportsDesktopNotifications()) {
return;
}
@ -61,15 +61,15 @@ module.exports = React.createClass({
if (this.enabled()) {
return (
<button className="mx_EnableNotificationsButton" onClick={this.onClick}>
{_t("Disable Notifications")}
{ _t("Disable Notifications") }
</button>
);
} else {
return (
<button className="mx_EnableNotificationsButton" onClick={this.onClick}>
{_t("Enable Notifications")}
{ _t("Enable Notifications") }
</button>
);
}
}
},
});