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

@ -17,9 +17,9 @@ limitations under the License.
'use strict';
var classNames = require('classnames');
var React = require('react');
var ReactDOM = require('react-dom');
const classNames = require('classnames');
const React = require('react');
const ReactDOM = require('react-dom');
// Shamelessly ripped off Modal.js. There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and
@ -36,7 +36,7 @@ module.exports = {
},
getOrCreateContainer: function() {
var container = document.getElementById(this.ContextualMenuContainerId);
let container = document.getElementById(this.ContextualMenuContainerId);
if (!container) {
container = document.createElement("div");
@ -48,9 +48,9 @@ module.exports = {
},
createMenu: function(Element, props) {
var self = this;
const self = this;
var closeMenu = function() {
const closeMenu = function() {
ReactDOM.unmountComponentAtNode(self.getOrCreateContainer());
if (props && props.onFinished) {
@ -58,17 +58,17 @@ module.exports = {
}
};
var position = {
const position = {
top: props.top,
};
var chevronOffset = {};
const chevronOffset = {};
if (props.chevronOffset) {
chevronOffset.top = props.chevronOffset;
}
// To override the default chevron colour, if it's been set
var chevronCSS = "";
let chevronCSS = "";
if (props.menuColour) {
chevronCSS = `
.mx_ContextualMenu_chevron_left:after {
@ -81,7 +81,7 @@ module.exports = {
`;
}
var chevron = null;
let chevron = null;
if (props.left) {
chevron = <div style={chevronOffset} className="mx_ContextualMenu_chevron_left"></div>;
position.left = props.left;
@ -90,15 +90,15 @@ module.exports = {
position.right = props.right;
}
var className = 'mx_ContextualMenu_wrapper';
const className = 'mx_ContextualMenu_wrapper';
var menuClasses = classNames({
const menuClasses = classNames({
'mx_ContextualMenu': true,
'mx_ContextualMenu_left': props.left,
'mx_ContextualMenu_right': !props.left,
});
var menuStyle = {};
const menuStyle = {};
if (props.menuWidth) {
menuStyle.width = props.menuWidth;
}
@ -113,14 +113,14 @@ module.exports = {
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
// property set here so you can't close the menu from a button click!
var menu = (
const menu = (
<div className={className} style={position}>
<div className={menuClasses} style={menuStyle}>
{chevron}
<Element {...props} onFinished={closeMenu}/>
{ chevron }
<Element {...props} onFinished={closeMenu} />
</div>
<div className="mx_ContextualMenu_background" onClick={closeMenu}></div>
<style>{chevronCSS}</style>
<style>{ chevronCSS }</style>
</div>
);

View file

@ -61,7 +61,7 @@ module.exports = React.createClass({
},
onCreateRoom: function() {
var options = {};
const options = {};
if (this.state.room_name) {
options.name = this.state.room_name;
@ -79,14 +79,14 @@ module.exports = React.createClass({
{
type: "m.room.join_rules",
content: {
"join_rule": this.state.is_private ? "invite" : "public"
}
"join_rule": this.state.is_private ? "invite" : "public",
},
},
{
type: "m.room.history_visibility",
content: {
"history_visibility": this.state.share_history ? "shared" : "invited"
}
"history_visibility": this.state.share_history ? "shared" : "invited",
},
},
];
}
@ -94,19 +94,19 @@ module.exports = React.createClass({
options.invite = this.state.invited_users;
var alias = this.getAliasLocalpart();
const alias = this.getAliasLocalpart();
if (alias) {
options.room_alias_name = alias;
}
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
if (!cli) {
// TODO: Error.
console.error("Cannot create room: No matrix client.");
return;
}
var deferred = cli.createRoom(options);
const deferred = cli.createRoom(options);
if (this.state.encrypt) {
// TODO
@ -116,7 +116,7 @@ module.exports = React.createClass({
phase: this.phases.CREATING,
});
var self = this;
const self = this;
deferred.then(function(resp) {
self.setState({
@ -209,7 +209,7 @@ module.exports = React.createClass({
onAliasChanged: function(alias) {
this.setState({
alias: alias
alias: alias,
});
},
@ -220,64 +220,64 @@ module.exports = React.createClass({
},
render: function() {
var curr_phase = this.state.phase;
const curr_phase = this.state.phase;
if (curr_phase == this.phases.CREATING) {
var Loader = sdk.getComponent("elements.Spinner");
const Loader = sdk.getComponent("elements.Spinner");
return (
<Loader/>
<Loader />
);
} else {
var error_box = "";
let error_box = "";
if (curr_phase == this.phases.ERROR) {
error_box = (
<div className="mx_Error">
{_t('An error occurred: %(error_string)s', {error_string: this.state.error_string})}
{ _t('An error occurred: %(error_string)s', {error_string: this.state.error_string}) }
</div>
);
}
var CreateRoomButton = sdk.getComponent("create_room.CreateRoomButton");
var RoomAlias = sdk.getComponent("create_room.RoomAlias");
var Presets = sdk.getComponent("create_room.Presets");
var UserSelector = sdk.getComponent("elements.UserSelector");
var SimpleRoomHeader = sdk.getComponent("rooms.SimpleRoomHeader");
const CreateRoomButton = sdk.getComponent("create_room.CreateRoomButton");
const RoomAlias = sdk.getComponent("create_room.RoomAlias");
const Presets = sdk.getComponent("create_room.Presets");
const UserSelector = sdk.getComponent("elements.UserSelector");
const SimpleRoomHeader = sdk.getComponent("rooms.SimpleRoomHeader");
var domain = MatrixClientPeg.get().getDomain();
const domain = MatrixClientPeg.get().getDomain();
return (
<div className="mx_CreateRoom">
<SimpleRoomHeader title={_t("Create Room")} collapsedRhs={ this.props.collapsedRhs }/>
<SimpleRoomHeader title={_t("Create Room")} collapsedRhs={this.props.collapsedRhs} />
<div className="mx_CreateRoom_body">
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder={_t('Name')}/> <br />
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder={_t('Topic')}/> <br />
<RoomAlias ref="alias" alias={this.state.alias} homeserver={ domain } onChange={this.onAliasChanged}/> <br />
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br />
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br />
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder={_t('Name')} /> <br />
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder={_t('Topic')} /> <br />
<RoomAlias ref="alias" alias={this.state.alias} homeserver={domain} onChange={this.onAliasChanged} /> <br />
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged} /> <br />
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset} /> <br />
<div>
<label>
<input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/>
{_t('Make this room private')}
<input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged} />
{ _t('Make this room private') }
</label>
</div>
<div>
<label>
<input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/>
{_t('Share message history with new users')}
<input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged} />
{ _t('Share message history with new users') }
</label>
</div>
<div className="mx_CreateRoom_encrypt">
<label>
<input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/>
{_t('Encrypt room')}
<input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged} />
{ _t('Encrypt room') }
</label>
</div>
<div>
<CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br />
</div>
{error_box}
{ error_box }
</div>
</div>
);
}
}
},
});

View file

@ -24,7 +24,7 @@ import { _t, _tJsx } from '../../languageHandler';
/*
* Component which shows the filtered file using a TimelinePanel
*/
var FilePanel = React.createClass({
const FilePanel = React.createClass({
displayName: 'FilePanel',
propTypes: {
@ -55,33 +55,33 @@ var FilePanel = React.createClass({
},
updateTimelineSet: function(roomId) {
var client = MatrixClientPeg.get();
var room = client.getRoom(roomId);
const client = MatrixClientPeg.get();
const room = client.getRoom(roomId);
this.noRoom = !room;
if (room) {
var filter = new Matrix.Filter(client.credentials.userId);
const filter = new Matrix.Filter(client.credentials.userId);
filter.setDefinition(
{
"room": {
"timeline": {
"contains_url": true
"contains_url": true,
},
}
}
},
},
);
// FIXME: we shouldn't be doing this every time we change room - see comment above.
client.getOrCreateFilter("FILTER_FILES_" + client.credentials.userId, filter).then(
(filterId)=>{
filter.filterId = filterId;
var timelineSet = room.getOrCreateFilteredTimelineSet(filter);
const timelineSet = room.getOrCreateFilteredTimelineSet(filter);
this.setState({ timelineSet: timelineSet });
},
(error)=>{
console.error("Failed to get or create file panel filter", error);
}
},
);
} else {
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
@ -92,18 +92,18 @@ var FilePanel = React.createClass({
if (MatrixClientPeg.get().isGuest()) {
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
<div className="mx_RoomView_empty">
{_tJsx("You must <a>register</a> to use this functionality", /<a>(.*?)<\/a>/, (sub) => <a href="#/register" key="sub">{sub}</a>)}
{ _tJsx("You must <a>register</a> to use this functionality", /<a>(.*?)<\/a>/, (sub) => <a href="#/register" key="sub">{ sub }</a>) }
</div>
</div>;
} else if (this.noRoom) {
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
<div className="mx_RoomView_empty">{_t("You must join the room to see its files")}</div>
<div className="mx_RoomView_empty">{ _t("You must join the room to see its files") }</div>
</div>;
}
// wrap a TimelinePanel with the jump-to-event bits turned off.
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
var Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
if (this.state.timelineSet) {
// console.log("rendering TimelinePanel for timelineSet " + this.state.timelineSet.room.roomId + " " +
@ -114,17 +114,16 @@ var FilePanel = React.createClass({
manageReadReceipts={false}
manageReadMarkers={false}
timelineSet={this.state.timelineSet}
showUrlPreview = { false }
showUrlPreview = {false}
tileShape="file_grid"
opacity={ this.props.opacity }
opacity={this.props.opacity}
empty={_t('There are no visible files in this room')}
/>
);
}
else {
} else {
return (
<div className="mx_FilePanel">
<Loader/>
<Loader />
</div>
);
}

View file

@ -27,7 +27,8 @@ import AccessibleButton from '../views/elements/AccessibleButton';
import Modal from '../../Modal';
import classnames from 'classnames';
import GroupSummaryStore from '../../stores/GroupSummaryStore';
import GroupStoreCache from '../../stores/GroupStoreCache';
import GroupStore from '../../stores/GroupStore';
const RoomSummaryType = PropTypes.shape({
room_id: PropTypes.string.isRequired,
@ -78,7 +79,7 @@ const CategoryRoomList = React.createClass({
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
return this.context.groupSummaryStore
return this.context.groupStore
.addRoomToGroupSummary(addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
@ -157,7 +158,7 @@ const FeaturedRoom = React.createClass({
onDeleteClicked: function(e) {
e.preventDefault();
e.stopPropagation();
this.context.groupSummaryStore.removeRoomFromGroupSummary(
this.context.groupStore.removeRoomFromGroupSummary(
this.props.summaryInfo.room_id,
).catch((err) => {
console.error('Error whilst removing room from group summary', err);
@ -252,7 +253,7 @@ const RoleUserList = React.createClass({
if (!success) return;
const errorList = [];
Promise.all(addrs.map((addr) => {
return this.context.groupSummaryStore
return this.context.groupStore
.addUserToGroupSummary(addr.address)
.catch(() => { errorList.push(addr.address); })
.reflect();
@ -327,7 +328,7 @@ const FeaturedUser = React.createClass({
onDeleteClicked: function(e) {
e.preventDefault();
e.stopPropagation();
this.context.groupSummaryStore.removeUserFromGroupSummary(
this.context.groupStore.removeUserFromGroupSummary(
this.props.summaryInfo.user_id,
).catch((err) => {
console.error('Error whilst removing user from group summary', err);
@ -373,14 +374,14 @@ const FeaturedUser = React.createClass({
},
});
const GroupSummaryContext = {
groupSummaryStore: React.PropTypes.instanceOf(GroupSummaryStore).isRequired,
const GroupContext = {
groupStore: React.PropTypes.instanceOf(GroupStore).isRequired,
};
CategoryRoomList.contextTypes = GroupSummaryContext;
FeaturedRoom.contextTypes = GroupSummaryContext;
RoleUserList.contextTypes = GroupSummaryContext;
FeaturedUser.contextTypes = GroupSummaryContext;
CategoryRoomList.contextTypes = GroupContext;
FeaturedRoom.contextTypes = GroupContext;
RoleUserList.contextTypes = GroupContext;
FeaturedUser.contextTypes = GroupContext;
export default React.createClass({
displayName: 'GroupView',
@ -390,12 +391,12 @@ export default React.createClass({
},
childContextTypes: {
groupSummaryStore: React.PropTypes.instanceOf(GroupSummaryStore),
groupStore: React.PropTypes.instanceOf(GroupStore),
},
getChildContext: function() {
return {
groupSummaryStore: this._groupSummaryStore,
groupStore: this._groupStore,
};
},
@ -413,14 +414,14 @@ export default React.createClass({
componentWillMount: function() {
this._changeAvatarComponent = null;
this._initGroupSummaryStore(this.props.groupId);
this._initGroupStore(this.props.groupId);
MatrixClientPeg.get().on("Group.myMembership", this._onGroupMyMembership);
},
componentWillUnmount: function() {
MatrixClientPeg.get().removeListener("Group.myMembership", this._onGroupMyMembership);
this._groupSummaryStore.removeAllListeners();
this._groupStore.removeAllListeners();
},
componentWillReceiveProps: function(newProps) {
@ -429,7 +430,7 @@ export default React.createClass({
summary: null,
error: null,
}, () => {
this._initGroupSummaryStore(newProps.groupId);
this._initGroupStore(newProps.groupId);
});
}
},
@ -440,17 +441,15 @@ export default React.createClass({
this.setState({membershipBusy: false});
},
_initGroupSummaryStore: function(groupId) {
this._groupSummaryStore = new GroupSummaryStore(
MatrixClientPeg.get(), this.props.groupId,
);
this._groupSummaryStore.on('update', () => {
_initGroupStore: function(groupId) {
this._groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
this._groupStore.on('update', () => {
this.setState({
summary: this._groupSummaryStore.getSummary(),
summary: this._groupStore.getSummary(),
error: null,
});
});
this._groupSummaryStore.on('error', (err) => {
this._groupStore.on('error', (err) => {
this.setState({
summary: null,
error: err,
@ -527,7 +526,7 @@ export default React.createClass({
editing: false,
summary: null,
});
this._initGroupSummaryStore(this.props.groupId);
this._initGroupStore(this.props.groupId);
}).catch((e) => {
this.setState({
saving: false,
@ -606,7 +605,7 @@ export default React.createClass({
this.setState({
publicityBusy: true,
});
this._groupSummaryStore.setGroupPublicity(publicity).then(() => {
this._groupStore.setGroupPublicity(publicity).then(() => {
this.setState({
publicityBusy: false,
});

View file

@ -107,7 +107,7 @@ export default React.createClass({
const msg = error.message || error.toString();
this.setState({
errorText: msg
errorText: msg,
});
}).done();
@ -207,7 +207,7 @@ export default React.createClass({
if (this.state.errorText) {
error = (
<div className="error">
{this.state.errorText}
{ this.state.errorText }
</div>
);
}
@ -215,8 +215,8 @@ export default React.createClass({
return (
<div>
<div>
{this._renderCurrentStage()}
{error}
{ this._renderCurrentStage() }
{ error }
</div>
</div>
);

View file

@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -164,7 +165,7 @@ export default React.createClass({
case KeyCode.UP:
case KeyCode.DOWN:
if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) {
let action = ev.keyCode == KeyCode.UP ?
const action = ev.keyCode == KeyCode.UP ?
'view_prev_room' : 'view_next_room';
dis.dispatch({action: action});
handled = true;
@ -206,8 +207,7 @@ export default React.createClass({
_onScrollKeyPressed: function(ev) {
if (this.refs.roomView) {
this.refs.roomView.handleScrollKey(ev);
}
else if (this.refs.roomDirectory) {
} else if (this.refs.roomDirectory) {
this.refs.roomDirectory.handleScrollKey(ev);
}
},
@ -251,11 +251,10 @@ export default React.createClass({
page_element = <UserSettings
onClose={this.props.onUserSettingsClose}
brand={this.props.config.brand}
enableLabs={this.props.config.enableLabs}
referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken}
/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity} />;
break;
case PageTypes.MyGroups:
@ -267,7 +266,7 @@ export default React.createClass({
onRoomCreated={this.props.onRoomCreated}
collapsedRhs={this.props.collapseRhs}
/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity}/>;
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity} />;
break;
case PageTypes.RoomDirectory:
@ -320,7 +319,7 @@ export default React.createClass({
topBar = <MatrixToolbar />;
}
var bodyClasses = 'mx_MatrixChat';
let bodyClasses = 'mx_MatrixChat';
if (topBar) {
bodyClasses += ' mx_MatrixChat_toolbarShowing';
}
@ -330,7 +329,7 @@ export default React.createClass({
return (
<div className='mx_MatrixChat_wrapper'>
{topBar}
{ topBar }
<div className={bodyClasses}>
<LeftPanel
selectedRoom={this.props.currentRoomId}
@ -338,9 +337,9 @@ export default React.createClass({
opacity={this.props.leftOpacity}
/>
<main className='mx_MatrixChat_middlePanel'>
{page_element}
{ page_element }
</main>
{right_panel}
{ right_panel }
</div>
</div>
);

View file

@ -773,15 +773,13 @@ module.exports = React.createClass({
dis.dispatch({action: 'view_set_mxid'});
return;
}
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createTrackedDialog('Create Room', '', TextInputDialog, {
title: _t('Create Room'),
description: _t('Room name (optional)'),
button: _t('Create Room'),
onFinished: (shouldCreate, name) => {
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, {
onFinished: (shouldCreate, name, noFederate) => {
if (shouldCreate) {
const createOpts = {};
if (name) createOpts.name = name;
if (noFederate) createOpts.creation_content = {'m.federate': false};
createRoom({createOpts}).done();
}
},

View file

@ -154,15 +154,15 @@ module.exports = React.createClass({
// 0: read marker is within the window
// +1: read marker is below the window
getReadMarkerPosition: function() {
var readMarker = this.refs.readMarkerNode;
var messageWrapper = this.refs.scrollPanel;
const readMarker = this.refs.readMarkerNode;
const messageWrapper = this.refs.scrollPanel;
if (!readMarker || !messageWrapper) {
return null;
}
var wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect();
var readMarkerRect = readMarker.getBoundingClientRect();
const wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect();
const readMarkerRect = readMarker.getBoundingClientRect();
// the read-marker pretends to have zero height when it is actually
// two pixels high; +2 here to account for that.
@ -262,7 +262,7 @@ module.exports = React.createClass({
this.eventNodes = {};
var i;
let i;
// first figure out which is the last event in the list which we're
// actually going to show; this allows us to behave slightly
@ -272,9 +272,9 @@ module.exports = React.createClass({
// a local echo, to manage the read-marker.
let lastShownEvent;
var lastShownNonLocalEchoIndex = -1;
let lastShownNonLocalEchoIndex = -1;
for (i = this.props.events.length-1; i >= 0; i--) {
var mxEv = this.props.events[i];
const mxEv = this.props.events[i];
if (!this._shouldShowEvent(mxEv)) {
continue;
}
@ -292,12 +292,12 @@ module.exports = React.createClass({
break;
}
var ret = [];
const ret = [];
var prevEvent = null; // the last event we showed
let prevEvent = null; // the last event we showed
// assume there is no read marker until proven otherwise
var readMarkerVisible = false;
let readMarkerVisible = false;
// if the readmarker has moved, cancel any active ghost.
if (this.currentReadMarkerEventId && this.props.readMarkerEventId &&
@ -309,16 +309,16 @@ module.exports = React.createClass({
const isMembershipChange = (e) => e.getType() === 'm.room.member';
for (i = 0; i < this.props.events.length; i++) {
let mxEv = this.props.events[i];
let eventId = mxEv.getId();
let last = (mxEv === lastShownEvent);
const mxEv = this.props.events[i];
const eventId = mxEv.getId();
const last = (mxEv === lastShownEvent);
const wantTile = this._shouldShowEvent(mxEv);
// Wrap consecutive member events in a ListSummary, ignore if redacted
if (isMembershipChange(mxEv) && wantTile) {
let readMarkerInMels = false;
let ts1 = mxEv.getTs();
const ts1 = mxEv.getTs();
// Ensure that the key of the MemberEventListSummary does not change with new
// member events. This will prevent it from being re-created unnecessarily, and
// instead will allow new props to be provided. In turn, the shouldComponentUpdate
@ -330,7 +330,7 @@ module.exports = React.createClass({
const key = "membereventlistsummary-" + (prevEvent ? mxEv.getId() : "initial");
if (this._wantsDateSeparator(prevEvent, mxEv.getDate())) {
let dateSeparator = <li key={ts1+'~'}><DateSeparator key={ts1+'~'} ts={ts1} showTwelveHour={this.props.isTwelveHour}/></li>;
const dateSeparator = <li key={ts1+'~'}><DateSeparator key={ts1+'~'} ts={ts1} showTwelveHour={this.props.isTwelveHour} /></li>;
ret.push(dateSeparator);
}
@ -339,7 +339,7 @@ module.exports = React.createClass({
readMarkerInMels = true;
}
let summarisedEvents = [mxEv];
const summarisedEvents = [mxEv];
for (;i + 1 < this.props.events.length; i++) {
const collapsedMxEv = this.props.events[i + 1];
@ -390,7 +390,7 @@ module.exports = React.createClass({
onToggle={this._onWidgetLoad} // Update scroll state
startExpanded={highlightInMels}
>
{eventTiles}
{ eventTiles }
</MemberEventListSummary>);
if (readMarkerInMels) {
@ -408,7 +408,7 @@ module.exports = React.createClass({
prevEvent = mxEv;
}
var isVisibleReadMarker = false;
let isVisibleReadMarker = false;
if (eventId == this.props.readMarkerEventId) {
var visible = this.props.readMarkerVisible;
@ -448,10 +448,10 @@ module.exports = React.createClass({
_getTilesForEvent: function(prevEvent, mxEv, last) {
const EventTile = sdk.getComponent('rooms.EventTile');
const DateSeparator = sdk.getComponent('messages.DateSeparator');
var ret = [];
const ret = [];
// is this a continuation of the previous message?
var continuation = false;
let continuation = false;
if (prevEvent !== null
&& prevEvent.sender && mxEv.sender
@ -476,8 +476,8 @@ module.exports = React.createClass({
// local echoes have a fake date, which could even be yesterday. Treat them
// as 'today' for the date separators.
var ts1 = mxEv.getTs();
var eventDate = mxEv.getDate();
let ts1 = mxEv.getTs();
let eventDate = mxEv.getDate();
if (mxEv.status) {
eventDate = new Date();
ts1 = eventDate.getTime();
@ -485,19 +485,19 @@ module.exports = React.createClass({
// do we need a date separator since the last event?
if (this._wantsDateSeparator(prevEvent, eventDate)) {
var dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} showTwelveHour={this.props.isTwelveHour}/></li>;
const dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} showTwelveHour={this.props.isTwelveHour} /></li>;
ret.push(dateSeparator);
continuation = false;
}
var eventId = mxEv.getId();
var highlight = (eventId == this.props.highlightedEventId);
const eventId = mxEv.getId();
const highlight = (eventId == this.props.highlightedEventId);
// we can't use local echoes as scroll tokens, because their event IDs change.
// Local echos have a send "status".
var scrollToken = mxEv.status ? undefined : eventId;
const scrollToken = mxEv.status ? undefined : eventId;
var readReceipts;
let readReceipts;
if (this.props.showReadReceipts) {
readReceipts = this._getReadReceiptsForEvent(mxEv);
}
@ -515,8 +515,8 @@ module.exports = React.createClass({
eventSendStatus={mxEv.status}
tileShape={this.props.tileShape}
isTwelveHour={this.props.isTwelveHour}
last={last} isSelectedEvent={highlight}/>
</li>
last={last} isSelectedEvent={highlight} />
</li>,
);
return ret;
@ -551,7 +551,7 @@ module.exports = React.createClass({
if (!room) {
return null;
}
let receipts = [];
const receipts = [];
room.getReceiptsForEvent(event).forEach((r) => {
if (!r.userId || r.type !== "m.read" || r.userId === myUserId) {
return; // ignore non-read receipts and receipts from self.
@ -559,7 +559,7 @@ module.exports = React.createClass({
if (MatrixClientPeg.get().isUserIgnored(r.userId)) {
return; // ignore ignored users
}
let member = room.getMember(r.userId);
const member = room.getMember(r.userId);
if (!member) {
return; // ignore unknown user IDs
}
@ -575,7 +575,7 @@ module.exports = React.createClass({
},
_getReadMarkerTile: function(visible) {
var hr;
let hr;
if (visible) {
hr = <hr className="mx_RoomView_myReadMarker"
style={{opacity: 1, width: '99%'}}
@ -585,7 +585,7 @@ module.exports = React.createClass({
return (
<li key="_readupto" ref="readMarkerNode"
className="mx_RoomView_myReadMarker_container">
{hr}
{ hr }
</li>
);
},
@ -604,7 +604,7 @@ module.exports = React.createClass({
},
_getReadMarkerGhostTile: function() {
var hr = <hr className="mx_RoomView_myReadMarker"
const hr = <hr className="mx_RoomView_myReadMarker"
style={{opacity: 1, width: '99%'}}
ref={this._startAnimation}
/>;
@ -615,7 +615,7 @@ module.exports = React.createClass({
return (
<li key={"_readuptoghost_"+this.currentGhostEventId}
className="mx_RoomView_myReadMarker_container">
{hr}
{ hr }
</li>
);
},
@ -627,7 +627,7 @@ module.exports = React.createClass({
// once dynamic content in the events load, make the scrollPanel check the
// scroll offsets.
_onWidgetLoad: function() {
var scrollPanel = this.refs.scrollPanel;
const scrollPanel = this.refs.scrollPanel;
if (scrollPanel) {
scrollPanel.forceUpdate();
}
@ -638,9 +638,9 @@ module.exports = React.createClass({
},
render: function() {
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
var Spinner = sdk.getComponent("elements.Spinner");
var topSpinner, bottomSpinner;
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
const Spinner = sdk.getComponent("elements.Spinner");
let topSpinner, bottomSpinner;
if (this.props.backPaginating) {
topSpinner = <li key="_topSpinner"><Spinner /></li>;
}
@ -648,25 +648,25 @@ module.exports = React.createClass({
bottomSpinner = <li key="_bottomSpinner"><Spinner /></li>;
}
var style = this.props.hidden ? { display: 'none' } : {};
const style = this.props.hidden ? { display: 'none' } : {};
style.opacity = this.props.opacity;
var className = this.props.className + " mx_fadable";
let className = this.props.className + " mx_fadable";
if (this.props.alwaysShowTimestamps) {
className += " mx_MessagePanel_alwaysShowTimestamps";
}
return (
<ScrollPanel ref="scrollPanel" className={ className }
onScroll={ this.props.onScroll }
onResize={ this.onResize }
onFillRequest={ this.props.onFillRequest }
onUnfillRequest={ this.props.onUnfillRequest }
style={ style }
stickyBottom={ this.props.stickyBottom }>
{topSpinner}
{this._getEventTiles()}
{bottomSpinner}
<ScrollPanel ref="scrollPanel" className={className}
onScroll={this.props.onScroll}
onResize={this.onResize}
onFillRequest={this.props.onFillRequest}
onUnfillRequest={this.props.onUnfillRequest}
style={style}
stickyBottom={this.props.stickyBottom}>
{ topSpinner }
{ this._getEventTiles() }
{ bottomSpinner }
</ScrollPanel>
);
},

View file

@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ReactDOM = require("react-dom");
const React = require('react');
const ReactDOM = require("react-dom");
import { _t } from '../../languageHandler';
var Matrix = require("matrix-js-sdk");
var sdk = require('../../index');
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
const Matrix = require("matrix-js-sdk");
const sdk = require('../../index');
const MatrixClientPeg = require("../../MatrixClientPeg");
const dis = require("../../dispatcher");
/*
* Component which shows the global notification list using a TimelinePanel
*/
var NotificationPanel = React.createClass({
const NotificationPanel = React.createClass({
displayName: 'NotificationPanel',
propTypes: {
@ -33,10 +33,10 @@ var NotificationPanel = React.createClass({
render: function() {
// wrap a TimelinePanel with the jump-to-event bits turned off.
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
var Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
var timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
const timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
if (timelineSet) {
return (
<TimelinePanel key={"NotificationPanel_" + this.props.roomId}
@ -44,18 +44,17 @@ var NotificationPanel = React.createClass({
manageReadReceipts={false}
manageReadMarkers={false}
timelineSet={timelineSet}
showUrlPreview = { false }
opacity={ this.props.opacity }
showUrlPreview = {false}
opacity={this.props.opacity}
tileShape="notif"
empty={ _t('You have no visible notifications') }
empty={_t('You have no visible notifications')}
/>
);
}
else {
} else {
console.error("No notifTimelineSet available!");
return (
<div className="mx_NotificationPanel">
<Loader/>
<Loader />
</div>
);
}

View file

@ -43,6 +43,10 @@ module.exports = React.createClass({
// the end of the live timeline.
atEndOfLiveTimeline: React.PropTypes.bool,
// This is true when the user is alone in the room, but has also sent a message.
// Used to suggest to the user to invite someone
sentMessageAndIsAlone: React.PropTypes.bool,
// true if there is an active call in this room (means we show
// the 'Active Call' text in the status bar if there is nothing
// more interesting)
@ -60,6 +64,14 @@ module.exports = React.createClass({
// 'unsent messages' bar
onCancelAllClick: React.PropTypes.func,
// callback for when the user clicks on the 'invite others' button in the
// 'you are alone' bar
onInviteClick: React.PropTypes.func,
// callback for when the user clicks on the 'stop warning me' button in the
// 'you are alone' bar
onStopWarningClick: React.PropTypes.func,
// callback for when the user clicks on the 'scroll to bottom' button
onScrollToBottomClick: React.PropTypes.func,
@ -103,7 +115,7 @@ module.exports = React.createClass({
componentWillUnmount: function() {
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("sync", this.onSyncStateChange);
client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
@ -115,7 +127,7 @@ module.exports = React.createClass({
return;
}
this.setState({
syncState: state
syncState: state,
});
},
@ -126,7 +138,7 @@ module.exports = React.createClass({
},
// Check whether current size is greater than 0, if yes call props.onVisible
_checkSize: function () {
_checkSize: function() {
if (this.props.onVisible && this._getSize()) {
this.props.onVisible();
}
@ -140,7 +152,8 @@ module.exports = React.createClass({
(this.state.usersTyping.length > 0) ||
this.props.numUnreadMessages ||
!this.props.atEndOfLiveTimeline ||
this.props.hasActiveCall
this.props.hasActiveCall ||
this.props.sentMessageAndIsAlone
) {
return STATUS_BAR_EXPANDED;
} else if (this.props.unsentMessageError) {
@ -157,9 +170,9 @@ module.exports = React.createClass({
if (this.props.numUnreadMessages) {
return (
<div className="mx_RoomStatusBar_scrollDownIndicator"
onClick={ this.props.onScrollToBottomClick }>
onClick={this.props.onScrollToBottomClick}>
<img src="img/newmessages.svg" width="24" height="24"
alt=""/>
alt="" />
</div>
);
}
@ -167,18 +180,18 @@ module.exports = React.createClass({
if (!this.props.atEndOfLiveTimeline) {
return (
<div className="mx_RoomStatusBar_scrollDownIndicator"
onClick={ this.props.onScrollToBottomClick }>
onClick={this.props.onScrollToBottomClick}>
<img src="img/scrolldown.svg" width="24" height="24"
alt={ _t("Scroll to bottom of page") }
title={ _t("Scroll to bottom of page") }/>
alt={_t("Scroll to bottom of page")}
title={_t("Scroll to bottom of page")} />
</div>
);
}
if (this.props.hasActiveCall) {
var TintableSvg = sdk.getComponent("elements.TintableSvg");
const TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
<TintableSvg src="img/sound-indicator.svg" width="23" height="20"/>
<TintableSvg src="img/sound-indicator.svg" width="23" height="20" />
);
}
@ -189,7 +202,7 @@ module.exports = React.createClass({
if (wantPlaceholder) {
return (
<div className="mx_RoomStatusBar_typingIndicatorAvatars">
{this._renderTypingIndicatorAvatars(this.props.whoIsTypingLimit)}
{ this._renderTypingIndicatorAvatars(this.props.whoIsTypingLimit) }
</div>
);
}
@ -221,8 +234,8 @@ module.exports = React.createClass({
if (othersCount > 0) {
avatars.push(
<span className="mx_RoomStatusBar_typingIndicatorRemaining" key="others">
+{othersCount}
</span>
+{ othersCount }
</span>,
);
}
@ -240,12 +253,12 @@ module.exports = React.createClass({
if (this.state.syncState === "ERROR") {
return (
<div className="mx_RoomStatusBar_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ " />
<div className="mx_RoomStatusBar_connectionLostBar_title">
{_t('Connectivity to the server has been lost.')}
{ _t('Connectivity to the server has been lost.') }
</div>
<div className="mx_RoomStatusBar_connectionLostBar_desc">
{_t('Sent messages will be stored until your connection has returned.')}
{ _t('Sent messages will be stored until your connection has returned.') }
</div>
</div>
);
@ -254,18 +267,18 @@ module.exports = React.createClass({
if (this.props.unsentMessageError) {
return (
<div className="mx_RoomStatusBar_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ " />
<div className="mx_RoomStatusBar_connectionLostBar_title">
{ this.props.unsentMessageError }
</div>
<div className="mx_RoomStatusBar_connectionLostBar_desc">
{_tJsx("<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.",
{ _tJsx("<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.",
[/<a>(.*?)<\/a>/, /<a>(.*?)<\/a>/],
[
(sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={ this.props.onResendAllClick }>{sub}</a>,
(sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={ this.props.onCancelAllClick }>{sub}</a>,
]
)}
(sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this.props.onResendAllClick}>{ sub }</a>,
(sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this.props.onCancelAllClick}>{ sub }</a>,
],
) }
</div>
</div>
);
@ -275,24 +288,24 @@ module.exports = React.createClass({
// set when you've scrolled up
if (this.props.numUnreadMessages) {
// MUST use var name "count" for pluralization to kick in
var unreadMsgs = _t("%(count)s new messages", {count: this.props.numUnreadMessages});
const unreadMsgs = _t("%(count)s new messages", {count: this.props.numUnreadMessages});
return (
<div className="mx_RoomStatusBar_unreadMessagesBar"
onClick={ this.props.onScrollToBottomClick }>
{unreadMsgs}
onClick={this.props.onScrollToBottomClick}>
{ unreadMsgs }
</div>
);
}
const typingString = WhoIsTyping.whoIsTypingString(
this.state.usersTyping,
this.props.whoIsTypingLimit
this.props.whoIsTypingLimit,
);
if (typingString) {
return (
<div className="mx_RoomStatusBar_typingBar">
<EmojiText>{typingString}</EmojiText>
<EmojiText>{ typingString }</EmojiText>
</div>
);
}
@ -300,7 +313,22 @@ module.exports = React.createClass({
if (this.props.hasActiveCall) {
return (
<div className="mx_RoomStatusBar_callBar">
<b>{_t('Active call')}</b>
<b>{ _t('Active call') }</b>
</div>
);
}
// If you're alone in the room, and have sent a message, suggest to invite someone
if (this.props.sentMessageAndIsAlone) {
return (
<div className="mx_RoomStatusBar_isAlone">
{ _tJsx("There's no one else here! Would you like to <a>invite others</a> or <a>stop warning about the empty room</a>?",
[/<a>(.*?)<\/a>/, /<a>(.*?)<\/a>/],
[
(sub) => <a className="mx_RoomStatusBar_resend_link" key="invite" onClick={this.props.onInviteClick}>{ sub }</a>,
(sub) => <a className="mx_RoomStatusBar_resend_link" key="nowarn" onClick={this.props.onStopWarningClick}>{ sub }</a>,
],
) }
</div>
);
}
@ -310,15 +338,15 @@ module.exports = React.createClass({
render: function() {
var content = this._getContent();
var indicator = this._getIndicator(this.state.usersTyping.length > 0);
const content = this._getContent();
const indicator = this._getIndicator(this.state.usersTyping.length > 0);
return (
<div className="mx_RoomStatusBar">
<div className="mx_RoomStatusBar_indicator">
{indicator}
{ indicator }
</div>
{content}
{ content }
</div>
);
},

View file

@ -22,25 +22,25 @@ limitations under the License.
import shouldHideEvent from "../../shouldHideEvent";
var React = require("react");
var ReactDOM = require("react-dom");
const React = require("react");
const ReactDOM = require("react-dom");
import Promise from 'bluebird';
var classNames = require("classnames");
var Matrix = require("matrix-js-sdk");
const classNames = require("classnames");
const Matrix = require("matrix-js-sdk");
import { _t } from '../../languageHandler';
var UserSettingsStore = require('../../UserSettingsStore');
var MatrixClientPeg = require("../../MatrixClientPeg");
var ContentMessages = require("../../ContentMessages");
var Modal = require("../../Modal");
var sdk = require('../../index');
var CallHandler = require('../../CallHandler');
var Resend = require("../../Resend");
var dis = require("../../dispatcher");
var Tinter = require("../../Tinter");
var rate_limited_func = require('../../ratelimitedfunc');
var ObjectUtils = require('../../ObjectUtils');
var Rooms = require('../../Rooms');
const UserSettingsStore = require('../../UserSettingsStore');
const MatrixClientPeg = require("../../MatrixClientPeg");
const ContentMessages = require("../../ContentMessages");
const Modal = require("../../Modal");
const sdk = require('../../index');
const CallHandler = require('../../CallHandler');
const Resend = require("../../Resend");
const dis = require("../../dispatcher");
const Tinter = require("../../Tinter");
const rate_limited_func = require('../../ratelimitedfunc');
const ObjectUtils = require('../../ObjectUtils');
const Rooms = require('../../Rooms');
import KeyCode from '../../KeyCode';
@ -49,7 +49,7 @@ import UserProvider from '../../autocomplete/UserProvider';
import RoomViewStore from '../../stores/RoomViewStore';
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
let DEBUG = false;
const DEBUG = false;
let debuglog = function() {};
const BROWSER_SUPPORTS_SANDBOX = 'sandbox' in document.createElement('iframe');
@ -117,6 +117,8 @@ module.exports = React.createClass({
guestsCanJoin: false,
canPeek: false,
showApps: false,
isAlone: false,
isPeeking: false,
// error object, as from the matrix client/server API
// If we failed to load information about the room,
@ -266,6 +268,7 @@ module.exports = React.createClass({
console.log("Attempting to peek into room %s", roomId);
this.setState({
peekLoading: true,
isPeeking: true, // this will change to false if peeking fails
});
MatrixClientPeg.get().peekInRoom(roomId).then((room) => {
this.setState({
@ -274,6 +277,11 @@ module.exports = React.createClass({
});
this._onRoomLoaded(room);
}, (err) => {
// Stop peeking if anything went wrong
this.setState({
isPeeking: false,
});
// This won't necessarily be a MatrixError, but we duck-type
// here and say if it's got an 'errcode' key with the right value,
// it means we can't peek.
@ -290,6 +298,7 @@ module.exports = React.createClass({
} else if (room) {
// Stop peeking because we have joined this room previously
MatrixClientPeg.get().stopPeeking();
this.setState({isPeeking: false});
}
},
@ -307,10 +316,10 @@ module.exports = React.createClass({
},
componentDidMount: function() {
var call = this._getCallForRoom();
var callState = call ? call.call_state : "ended";
const call = this._getCallForRoom();
const callState = call ? call.call_state : "ended";
this.setState({
callState: callState
callState: callState,
});
this._updateConfCallNotification();
@ -327,9 +336,8 @@ module.exports = React.createClass({
this.state.room.getJoinedMembers().length == 1 &&
this.state.room.getLiveTimeline() &&
this.state.room.getLiveTimeline().getEvents() &&
this.state.room.getLiveTimeline().getEvents().length <= 6)
{
var inviteBox = document.getElementById("mx_SearchableEntityList_query");
this.state.room.getLiveTimeline().getEvents().length <= 6) {
const inviteBox = document.getElementById("mx_SearchableEntityList_query");
setTimeout(function() {
if (inviteBox) {
inviteBox.focus();
@ -345,7 +353,7 @@ module.exports = React.createClass({
componentDidUpdate: function() {
if (this.refs.roomView) {
var roomView = ReactDOM.findDOMNode(this.refs.roomView);
const roomView = ReactDOM.findDOMNode(this.refs.roomView);
if (!roomView.ondrop) {
roomView.addEventListener('drop', this.onDrop);
roomView.addEventListener('dragover', this.onDragOver);
@ -372,7 +380,7 @@ module.exports = React.createClass({
// is really just for hygiene - we're going to be
// deleted anyway, so it doesn't matter if the event listeners
// don't get cleaned up.
var roomView = ReactDOM.findDOMNode(this.refs.roomView);
const roomView = ReactDOM.findDOMNode(this.refs.roomView);
roomView.removeEventListener('drop', this.onDrop);
roomView.removeEventListener('dragover', this.onDragOver);
roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd);
@ -454,6 +462,8 @@ module.exports = React.createClass({
switch (payload.action) {
case 'message_send_failed':
case 'message_sent':
this._checkIfAlone(this.state.room);
// no break; to intentionally fall through
case 'message_send_cancelled':
this.setState({
unsentMessageError: this._getUnsentMessageError(this.state.room),
@ -478,8 +488,7 @@ module.exports = React.createClass({
if (call) {
callState = call.call_state;
}
else {
} else {
callState = "ended";
}
@ -591,17 +600,17 @@ module.exports = React.createClass({
},
_calculatePeekRules: function(room) {
var guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
this.setState({
guestsCanJoin: true
guestsCanJoin: true,
});
}
var historyVisibility = room.currentState.getStateEvents("m.room.history_visibility", "");
const historyVisibility = room.currentState.getStateEvents("m.room.history_visibility", "");
if (historyVisibility && historyVisibility.getContent().history_visibility === "world_readable") {
this.setState({
canPeek: true
canPeek: true,
});
}
},
@ -610,35 +619,35 @@ module.exports = React.createClass({
// console.log("_updatePreviewUrlVisibility");
// check our per-room overrides
var roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls");
const roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls");
if (roomPreviewUrls && roomPreviewUrls.getContent().disable !== undefined) {
this.setState({
showUrlPreview: !roomPreviewUrls.getContent().disable
showUrlPreview: !roomPreviewUrls.getContent().disable,
});
return;
}
// check our global disable override
var userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
const userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
if (userRoomPreviewUrls && userRoomPreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false
showUrlPreview: false,
});
return;
}
// check the room state event
var roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
const roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
if (roomStatePreviewUrls && roomStatePreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false
showUrlPreview: false,
});
return;
}
// otherwise, we assume they're on.
this.setState({
showUrlPreview: true
showUrlPreview: true,
});
},
@ -654,11 +663,11 @@ module.exports = React.createClass({
},
updateTint: function() {
var room = this.state.room;
const room = this.state.room;
if (!room) return;
var color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
var color_scheme = {};
const color_scheme_event = room.getAccountData("org.matrix.room.color_scheme");
let color_scheme = {};
if (color_scheme_event) {
color_scheme = color_scheme_event.getContent();
// XXX: we should validate the event
@ -676,12 +685,11 @@ module.exports = React.createClass({
onRoomAccountData: function(event, room) {
if (room.roomId == this.state.roomId) {
if (event.getType() === "org.matrix.room.color_scheme") {
var color_scheme = event.getContent();
const color_scheme = event.getContent();
// XXX: we should validate the event
console.log("Tinter.tint from onRoomAccountData");
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
}
else if (event.getType() === "org.matrix.room.preview_urls") {
} else if (event.getType() === "org.matrix.room.preview_urls") {
this._updatePreviewUrlVisibility(room);
}
}
@ -720,7 +728,7 @@ module.exports = React.createClass({
// if we are now a member of the room, where we were not before, that
// means we have finished joining a room we were previously peeking
// into.
var me = MatrixClientPeg.get().credentials.userId;
const me = MatrixClientPeg.get().credentials.userId;
if (this.state.joining && this.state.room.hasMembershipState(me, "join")) {
// Having just joined a room, check to see if it looks like a DM room, and if so,
// mark it as one. This is to work around the fact that some clients don't support
@ -735,9 +743,34 @@ module.exports = React.createClass({
}
}, 500),
_checkIfAlone: function(room) {
let warnedAboutLonelyRoom = false;
if (localStorage) {
warnedAboutLonelyRoom = localStorage.getItem('mx_user_alone_warned_' + this.state.room.roomId);
}
if (warnedAboutLonelyRoom) {
if (this.state.isAlone) this.setState({isAlone: false});
return;
}
const joinedMembers = room.currentState.getMembers().filter(m => m.membership === "join" || m.membership === "invite");
this.setState({isAlone: joinedMembers.length === 1});
},
_getUnsentMessageError: function(room) {
const unsentMessages = this._getUnsentMessages(room);
if (!unsentMessages.length) return "";
if (
unsentMessages.length === 1 &&
unsentMessages[0].error &&
unsentMessages[0].error.data &&
unsentMessages[0].error.data.error &&
unsentMessages[0].error.name !== "UnknownDeviceError"
) {
return unsentMessages[0].error.data.error;
}
for (const event of unsentMessages) {
if (!event.error || event.error.name !== "UnknownDeviceError") {
return _t("Some of your messages have not been sent.");
@ -754,18 +787,18 @@ module.exports = React.createClass({
},
_updateConfCallNotification: function() {
var room = this.state.room;
const room = this.state.room;
if (!room || !this.props.ConferenceHandler) {
return;
}
var confMember = room.getMember(
this.props.ConferenceHandler.getConferenceUserIdForRoom(room.roomId)
const confMember = room.getMember(
this.props.ConferenceHandler.getConferenceUserIdForRoom(room.roomId),
);
if (!confMember) {
return;
}
var confCall = this.props.ConferenceHandler.getConferenceCallForRoom(confMember.roomId);
const confCall = this.props.ConferenceHandler.getConferenceCallForRoom(confMember.roomId);
// A conf call notification should be displayed if there is an ongoing
// conf call but this cilent isn't a part of it.
@ -773,7 +806,7 @@ module.exports = React.createClass({
displayConfCallNotification: (
(!confCall || confCall.call_state === "ended") &&
confMember.membership === "join"
)
),
});
},
@ -788,7 +821,7 @@ module.exports = React.createClass({
if (this.state.searchResults.next_batch) {
debuglog("requesting more search results");
var searchPromise = MatrixClientPeg.get().backPaginateRoomEventsSearch(
const searchPromise = MatrixClientPeg.get().backPaginateRoomEventsSearch(
this.state.searchResults);
return this._handleSearchResult(searchPromise);
} else {
@ -805,6 +838,22 @@ module.exports = React.createClass({
Resend.cancelUnsentEvents(this.state.room);
},
onInviteButtonClick: function() {
// call AddressPickerDialog
dis.dispatch({
action: 'view_invite',
roomId: this.state.room.roomId,
});
this.setState({isAlone: false}); // there's a good chance they'll invite someone
},
onStopAloneWarningClick: function() {
if (localStorage) {
localStorage.setItem('mx_user_alone_warned_' + this.state.room.roomId, true);
}
this.setState({isAlone: false});
},
onJoinButtonClicked: function(ev) {
const cli = MatrixClientPeg.get();
@ -883,8 +932,7 @@ module.exports = React.createClass({
numUnreadMessages: 0,
atEndOfLiveTimeline: true,
});
}
else {
} else {
this.setState({
atEndOfLiveTimeline: false,
});
@ -898,10 +946,10 @@ module.exports = React.createClass({
ev.dataTransfer.dropEffect = 'none';
var items = ev.dataTransfer.items;
const items = ev.dataTransfer.items;
if (items.length == 1) {
if (items[0].kind == 'file') {
this.setState({ draggingFile : true });
this.setState({ draggingFile: true });
ev.dataTransfer.dropEffect = 'copy';
}
}
@ -910,8 +958,8 @@ module.exports = React.createClass({
onDrop: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
var files = ev.dataTransfer.files;
this.setState({ draggingFile: false });
const files = ev.dataTransfer.files;
if (files.length == 1) {
this.uploadFile(files[0]);
}
@ -920,7 +968,7 @@ module.exports = React.createClass({
onDragLeaveOrEnd: function(ev) {
ev.stopPropagation();
ev.preventDefault();
this.setState({ draggingFile : false });
this.setState({ draggingFile: false });
},
uploadFile: function(file) {
@ -930,7 +978,7 @@ module.exports = React.createClass({
}
ContentMessages.sendContentToRoom(
file, this.state.room.roomId, MatrixClientPeg.get()
file, this.state.room.roomId, MatrixClientPeg.get(),
).done(undefined, (error) => {
if (error.name === "UnknownDeviceError") {
dis.dispatch({
@ -969,19 +1017,19 @@ module.exports = React.createClass({
// todo: should cancel any previous search requests.
this.searchId = new Date().getTime();
var filter;
let filter;
if (scope === "Room") {
filter = {
// XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
rooms: [
this.state.room.roomId
]
this.state.room.roomId,
],
};
}
debuglog("sending search request");
var searchPromise = MatrixClientPeg.get().searchRoomEvents({
const searchPromise = MatrixClientPeg.get().searchRoomEvents({
filter: filter,
term: term,
});
@ -989,11 +1037,11 @@ module.exports = React.createClass({
},
_handleSearchResult: function(searchPromise) {
var self = this;
const self = this;
// keep a record of the current search id, so that if the search terms
// change before we get a response, we can ignore the results.
var localSearchId = this.searchId;
const localSearchId = this.searchId;
this.setState({
searchInProgress: true,
@ -1012,7 +1060,7 @@ module.exports = React.createClass({
// In either case, we want to highlight the literal search term
// whether it was used by the search engine or not.
var highlights = results.highlights;
let highlights = results.highlights;
if (highlights.indexOf(self.state.searchTerm) < 0) {
highlights = highlights.concat(self.state.searchTerm);
}
@ -1020,14 +1068,15 @@ module.exports = React.createClass({
// For overlapping highlights,
// favour longer (more specific) terms first
highlights = highlights.sort(function(a, b) {
return b.length - a.length; });
return b.length - a.length;
});
self.setState({
searchHighlights: highlights,
searchResults: results,
});
}, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Search failed: " + error);
Modal.createTrackedDialog('Search failed', '', ErrorDialog, {
title: _t("Search failed"),
@ -1035,17 +1084,17 @@ module.exports = React.createClass({
});
}).finally(function() {
self.setState({
searchInProgress: false
searchInProgress: false,
});
});
},
getSearchResultTiles: function() {
var EventTile = sdk.getComponent('rooms.EventTile');
var SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
var Spinner = sdk.getComponent("elements.Spinner");
const EventTile = sdk.getComponent('rooms.EventTile');
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
const Spinner = sdk.getComponent("elements.Spinner");
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
// XXX: todo: merge overlapping results somehow?
// XXX: why doesn't searching on name work?
@ -1055,7 +1104,7 @@ module.exports = React.createClass({
return [];
}
var ret = [];
const ret = [];
if (this.state.searchInProgress) {
ret.push(<li key="search-spinner">
@ -1067,32 +1116,32 @@ module.exports = React.createClass({
if (this.state.searchResults.results.length == 0) {
ret.push(<li key="search-top-marker">
<h2 className="mx_RoomView_topMarker">{ _t("No results") }</h2>
</li>
</li>,
);
} else {
ret.push(<li key="search-top-marker">
<h2 className="mx_RoomView_topMarker">{ _t("No more results") }</h2>
</li>
</li>,
);
}
}
// once dynamic content in the search results load, make the scrollPanel check
// the scroll offsets.
var onWidgetLoad = () => {
var scrollPanel = this.refs.searchResultsPanel;
const onWidgetLoad = () => {
const scrollPanel = this.refs.searchResultsPanel;
if (scrollPanel) {
scrollPanel.checkScroll();
}
};
var lastRoomId;
let lastRoomId;
for (var i = this.state.searchResults.results.length - 1; i >= 0; i--) {
var result = this.state.searchResults.results[i];
for (let i = this.state.searchResults.results.length - 1; i >= 0; i--) {
const result = this.state.searchResults.results[i];
var mxEv = result.context.getEvent();
var roomId = mxEv.getRoomId();
const mxEv = result.context.getEvent();
const roomId = mxEv.getRoomId();
if (!EventTile.haveTileForEvent(mxEv)) {
// XXX: can this ever happen? It will make the result count
@ -1102,13 +1151,13 @@ module.exports = React.createClass({
if (this.state.searchScope === 'All') {
if(roomId != lastRoomId) {
var room = cli.getRoom(roomId);
const room = cli.getRoom(roomId);
// XXX: if we've left the room, we might not know about
// it. We should tell the js sdk to go and find out about
// it. But that's not an issue currently, as synapse only
// returns results for rooms we're joined to.
var roomName = room ? room.name : _t("Unknown room %(roomId)s", { roomId: roomId });
const roomName = room ? room.name : _t("Unknown room %(roomId)s", { roomId: roomId });
ret.push(<li key={mxEv.getId() + "-room"}>
<h1>{ _t("Room") }: { roomName }</h1>
@ -1117,13 +1166,13 @@ module.exports = React.createClass({
}
}
var resultLink = "#/room/"+roomId+"/"+mxEv.getId();
const resultLink = "#/room/"+roomId+"/"+mxEv.getId();
ret.push(<SearchResultTile key={mxEv.getId()}
searchResult={result}
searchHighlights={this.state.searchHighlights}
resultLink={resultLink}
onWidgetLoad={onWidgetLoad}/>);
onWidgetLoad={onWidgetLoad} />);
}
return ret;
},
@ -1143,38 +1192,37 @@ module.exports = React.createClass({
uploadingRoomSettings: true,
});
var newName = this.refs.header.getEditedName();
const newName = this.refs.header.getEditedName();
if (newName !== undefined) {
this.refs.room_settings.setName(newName);
}
var newTopic = this.refs.header.getEditedTopic();
const newTopic = this.refs.header.getEditedTopic();
if (newTopic !== undefined) {
this.refs.room_settings.setTopic(newTopic);
}
this.refs.room_settings.save().then((results) => {
var fails = results.filter(function(result) { return result.state !== "fulfilled"; });
const fails = results.filter(function(result) { return result.state !== "fulfilled"; });
console.log("Settings saved with %s errors", fails.length);
if (fails.length) {
fails.forEach(function(result) {
console.error(result.reason);
});
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to save room settings', '', ErrorDialog, {
title: _t("Failed to save settings"),
description: fails.map(function(result) { return result.reason; }).join("\n"),
});
// still editing room settings
}
else {
} else {
this.setState({
editingRoomSettings: false
editingRoomSettings: false,
});
}
}).finally(() => {
this.setState({
uploadingRoomSettings: false,
editingRoomSettings: false
editingRoomSettings: false,
});
}).done();
},
@ -1205,8 +1253,8 @@ module.exports = React.createClass({
MatrixClientPeg.get().forget(this.state.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 }),
@ -1215,20 +1263,20 @@ module.exports = React.createClass({
},
onRejectButtonClicked: function(ev) {
var self = this;
const self = this;
this.setState({
rejecting: true
rejecting: true,
});
MatrixClientPeg.get().leave(this.state.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
self.setState({
rejecting: false
rejecting: false,
});
}, function(error) {
console.error("Failed to reject invite: %s", error);
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const msg = error.message ? error.message : JSON.stringify(error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
title: _t("Failed to reject invite"),
description: msg,
@ -1236,7 +1284,7 @@ module.exports = React.createClass({
self.setState({
rejecting: false,
rejectError: error
rejectError: error,
});
});
},
@ -1296,7 +1344,7 @@ module.exports = React.createClass({
// restored when we switch back to it.
//
_getScrollState: function() {
var messagePanel = this.refs.messagePanel;
const messagePanel = this.refs.messagePanel;
if (!messagePanel) return null;
// if we're following the live timeline, we want to return null; that
@ -1311,7 +1359,7 @@ module.exports = React.createClass({
return null;
}
var scrollState = messagePanel.getScrollState();
const scrollState = messagePanel.getScrollState();
if (scrollState.stuckAtBottom) {
// we don't really expect to be in this state, but it will
@ -1338,7 +1386,7 @@ module.exports = React.createClass({
// a maxHeight on the underlying remote video tag.
// header + footer + status + give us at least 120px of scrollback at all times.
var auxPanelMaxHeight = window.innerHeight -
let auxPanelMaxHeight = window.innerHeight -
(83 + // height of RoomHeader
36 + // height of the status area
72 + // minimum height of the message compmoser
@ -1357,26 +1405,26 @@ module.exports = React.createClass({
onFullscreenClick: function() {
dis.dispatch({
action: 'video_fullscreen',
fullscreen: true
fullscreen: true,
}, true);
},
onMuteAudioClick: function() {
var call = this._getCallForRoom();
const call = this._getCallForRoom();
if (!call) {
return;
}
var newState = !call.isMicrophoneMuted();
const newState = !call.isMicrophoneMuted();
call.setMicrophoneMuted(newState);
this.forceUpdate(); // TODO: just update the voip buttons
},
onMuteVideoClick: function() {
var call = this._getCallForRoom();
const call = this._getCallForRoom();
if (!call) {
return;
}
var newState = !call.isLocalVideoMuted();
const newState = !call.isLocalVideoMuted();
call.setLocalVideoMuted(newState);
this.forceUpdate(); // TODO: just update the voip buttons
},
@ -1412,7 +1460,7 @@ module.exports = React.createClass({
* We pass it down to the scroll panel.
*/
handleScrollKey: function(ev) {
var panel;
let panel;
if(this.refs.searchResultsPanel) {
panel = this.refs.searchResultsPanel;
} else if(this.refs.messagePanel) {
@ -1483,13 +1531,13 @@ module.exports = React.createClass({
<RoomHeader ref="header"
room={this.state.room}
oobData={this.props.oobData}
collapsedRhs={ this.props.collapsedRhs }
collapsedRhs={this.props.collapsedRhs}
/>
<div className="mx_RoomView_auxPanel">
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
onForgetClick={ this.onForgetClick }
onRejectClick={ this.onRejectThreepidInviteButtonClicked }
canPreview={ false } error={ this.state.roomLoadError }
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
onForgetClick={this.onForgetClick}
onRejectClick={this.onRejectThreepidInviteButtonClicked}
canPreview={false} error={this.state.roomLoadError}
roomAlias={roomAlias}
spinner={this.state.joining}
inviterName={inviterName}
@ -1503,8 +1551,8 @@ module.exports = React.createClass({
}
}
var myUserId = MatrixClientPeg.get().credentials.userId;
var myMember = this.state.room.getMember(myUserId);
const myUserId = MatrixClientPeg.get().credentials.userId;
const myMember = this.state.room.getMember(myUserId);
if (myMember && myMember.membership == 'invite') {
if (this.state.joining || this.state.rejecting) {
return (
@ -1513,7 +1561,7 @@ module.exports = React.createClass({
</div>
);
} else {
var inviteEvent = myMember.events.member;
const inviteEvent = myMember.events.member;
var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender();
// We deliberately don't try to peek into invites, even if we have permission to peek
@ -1526,14 +1574,14 @@ module.exports = React.createClass({
<RoomHeader
ref="header"
room={this.state.room}
collapsedRhs={ this.props.collapsedRhs }
collapsedRhs={this.props.collapsedRhs}
/>
<div className="mx_RoomView_auxPanel">
<RoomPreviewBar onJoinClick={ this.onJoinButtonClicked }
onForgetClick={ this.onForgetClick }
onRejectClick={ this.onRejectButtonClicked }
inviterName={ inviterName }
canPreview={ false }
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
onForgetClick={this.onForgetClick}
onRejectClick={this.onRejectButtonClicked}
inviterName={inviterName}
canPreview={false}
spinner={this.state.joining}
room={this.state.room}
/>
@ -1547,33 +1595,36 @@ module.exports = React.createClass({
// We have successfully loaded this room, and are not previewing.
// Display the "normal" room view.
var call = this._getCallForRoom();
var inCall = false;
const call = this._getCallForRoom();
let inCall = false;
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
inCall = true;
}
var scrollheader_classes = classNames({
const scrollheader_classes = classNames({
mx_RoomView_scrollheader: true,
});
var statusBar;
let statusBar;
let isStatusAreaExpanded = true;
if (ContentMessages.getCurrentUploads().length > 0) {
var UploadBar = sdk.getComponent('structures.UploadBar');
const UploadBar = sdk.getComponent('structures.UploadBar');
statusBar = <UploadBar room={this.state.room} />;
} else if (!this.state.searchResults) {
var RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
const RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
isStatusAreaExpanded = this.state.statusBarVisible;
statusBar = <RoomStatusBar
room={this.state.room}
numUnreadMessages={this.state.numUnreadMessages}
unsentMessageError={this.state.unsentMessageError}
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
sentMessageAndIsAlone={this.state.isAlone}
hasActiveCall={inCall}
onResendAllClick={this.onResendAllClick}
onCancelAllClick={this.onCancelAllClick}
onInviteClick={this.onInviteButtonClick}
onStopWarningClick={this.onStopAloneWarningClick}
onScrollToBottomClick={this.jumpToLiveTimeline}
onResize={this.onChildResize}
onVisible={this.onStatusBarVisible}
@ -1587,13 +1638,12 @@ module.exports = React.createClass({
if (this.state.editingRoomSettings) {
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSettingsSaveClick} onCancelClick={this.onCancelClick} room={this.state.room} />;
} else if (this.state.uploadingRoomSettings) {
aux = <Loader/>;
aux = <Loader />;
} else if (this.state.forwardingEvent !== null) {
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
} else if (this.state.searching) {
hideCancel = true; // has own cancel
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress}
onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />;
} else if (this.state.showingPinned) {
hideCancel = true; // has own cancel
aux = <PinnedEventsPanel room={this.state.room} onCancelClick={this.onPinnedClick} />;
@ -1611,7 +1661,7 @@ module.exports = React.createClass({
hideCancel = true;
aux = (
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
onForgetClick={ this.onForgetClick }
onForgetClick={this.onForgetClick}
onRejectClick={this.onRejectThreepidInviteButtonClicked}
spinner={this.state.joining}
inviterName={inviterName}
@ -1622,7 +1672,7 @@ module.exports = React.createClass({
);
}
var auxPanel = (
const auxPanel = (
<AuxPanel ref="auxPanel" room={this.state.room}
userId={MatrixClientPeg.get().credentials.userId}
conferenceHandler={this.props.ConferenceHandler}
@ -1635,8 +1685,8 @@ module.exports = React.createClass({
</AuxPanel>
);
var messageComposer, searchInfo;
var canSpeak = (
let messageComposer, searchInfo;
const canSpeak = (
// joined and not showing search results
myMember && (myMember.membership == 'join') && !this.state.searchResults
);
@ -1647,8 +1697,8 @@ module.exports = React.createClass({
onResize={this.onChildResize}
uploadFile={this.uploadFile}
callState={this.state.callState}
opacity={ this.props.opacity }
showApps={ this.state.showApps }
opacity={this.props.opacity}
showApps={this.state.showApps}
/>;
}
@ -1656,19 +1706,19 @@ module.exports = React.createClass({
// in this.state if this is what RoomHeader desires?
if (this.state.searchResults) {
searchInfo = {
searchTerm : this.state.searchTerm,
searchScope : this.state.searchScope,
searchCount : this.state.searchResults.count,
searchTerm: this.state.searchTerm,
searchScope: this.state.searchScope,
searchCount: this.state.searchResults.count,
};
}
if (inCall) {
var zoomButton, voiceMuteButton, videoMuteButton;
let zoomButton, voiceMuteButton, videoMuteButton;
if (call.type === "video") {
zoomButton = (
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title={ _t("Fill screen") }>
<TintableSvg src="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }}/>
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title={_t("Fill screen")}>
<TintableSvg src="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }} />
</div>
);
@ -1676,14 +1726,14 @@ module.exports = React.createClass({
<div className="mx_RoomView_voipButton" onClick={this.onMuteVideoClick}>
<TintableSvg src={call.isLocalVideoMuted() ? "img/video-unmute.svg" : "img/video-mute.svg"}
alt={call.isLocalVideoMuted() ? _t("Click to unmute video") : _t("Click to mute video")}
width="31" height="27"/>
width="31" height="27" />
</div>;
}
voiceMuteButton =
<div className="mx_RoomView_voipButton" onClick={this.onMuteAudioClick}>
<TintableSvg src={call.isMicrophoneMuted() ? "img/voice-unmute.svg" : "img/voice-mute.svg"}
alt={call.isMicrophoneMuted() ? _t("Click to unmute audio") : _t("Click to mute audio")}
width="21" height="26"/>
width="21" height="26" />
</div>;
// wrap the existing status bar into a 'callStatusBar' which adds more knobs.
@ -1693,25 +1743,25 @@ module.exports = React.createClass({
{ videoMuteButton }
{ zoomButton }
{ statusBar }
<TintableSvg className="mx_RoomView_voipChevron" src="img/voip-chevron.svg" width="22" height="17"/>
<TintableSvg className="mx_RoomView_voipChevron" src="img/voip-chevron.svg" width="22" height="17" />
</div>;
}
// if we have search results, we keep the messagepanel (so that it preserves its
// scroll state), but hide it.
var searchResultsPanel;
var hideMessagePanel = false;
let searchResultsPanel;
let hideMessagePanel = false;
if (this.state.searchResults) {
searchResultsPanel = (
<ScrollPanel ref="searchResultsPanel"
className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel"
onFillRequest={ this.onSearchResultsFillRequest }
onResize={ this.onSearchResultsResize }
onFillRequest={this.onSearchResultsFillRequest}
onResize={this.onSearchResultsResize}
style={{ opacity: this.props.opacity }}
>
<li className={scrollheader_classes}></li>
{this.getSearchResultTiles()}
{ this.getSearchResultTiles() }
</ScrollPanel>
);
hideMessagePanel = true;
@ -1726,26 +1776,26 @@ module.exports = React.createClass({
}
// console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
var messagePanel = (
const messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef}
timelineSet={this.state.room.getUnfilteredTimelineSet()}
showReadReceipts={!UserSettingsStore.getSyncedSetting('hideReadReceipts', false)}
manageReadReceipts={true}
manageReadMarkers={true}
manageReadReceipts={!this.state.isPeeking}
manageReadMarkers={!this.state.isPeeking}
hidden={hideMessagePanel}
highlightedEventId={highlightedEventId}
eventId={this.state.initialEventId}
eventPixelOffset={this.state.initialEventPixelOffset}
onScroll={ this.onMessageListScroll }
onReadMarkerUpdated={ this._updateTopUnreadMessagesBar }
showUrlPreview = { this.state.showUrlPreview }
opacity={ this.props.opacity }
onScroll={this.onMessageListScroll}
onReadMarkerUpdated={this._updateTopUnreadMessagesBar}
showUrlPreview = {this.state.showUrlPreview}
opacity={this.props.opacity}
className="mx_RoomView_messagePanel"
/>);
var topUnreadMessagesBar = null;
let topUnreadMessagesBar = null;
if (this.state.showTopUnreadMessagesBar) {
var TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
topUnreadMessagesBar = (
<div className="mx_RoomView_topUnreadMessagesBar mx_fadable" style={{ opacity: this.props.opacity }}>
<TopUnreadMessagesBar
@ -1761,13 +1811,13 @@ module.exports = React.createClass({
}
return (
<div className={ "mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "") } ref="roomView">
<div className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
oobData={this.props.oobData}
editing={this.state.editingRoomSettings}
saving={this.state.uploadingRoomSettings}
inRoom={myMember && myMember.membership === 'join'}
collapsedRhs={ this.props.collapsedRhs }
collapsedRhs={this.props.collapsedRhs}
onSearchClick={this.onSearchClick}
onSettingsClick={this.onSettingsClick}
onPinnedClick={this.onPinnedClick}

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require("react");
var ReactDOM = require("react-dom");
var GeminiScrollbar = require('react-gemini-scrollbar');
const React = require("react");
const ReactDOM = require("react-dom");
const GeminiScrollbar = require('react-gemini-scrollbar');
import Promise from 'bluebird';
var KeyCode = require('../../KeyCode');
const KeyCode = require('../../KeyCode');
var DEBUG_SCROLL = false;
const DEBUG_SCROLL = false;
// var DEBUG_SCROLL = true;
// The amount of extra scroll distance to allow prior to unfilling.
@ -178,7 +178,7 @@ module.exports = React.createClass({
},
onScroll: function(ev) {
var sn = this._getScrollNode();
const sn = this._getScrollNode();
debuglog("Scroll event: offset now:", sn.scrollTop,
"_lastSetScroll:", this._lastSetScroll);
@ -238,7 +238,7 @@ module.exports = React.createClass({
// about whether the the content is scrolled down right now, irrespective of
// whether it will stay that way when the children update.
isAtBottom: function() {
var sn = this._getScrollNode();
const sn = this._getScrollNode();
// there seems to be some bug with flexbox/gemini/chrome/richvdh's
// understanding of the box model, wherein the scrollNode ends up 2
@ -281,7 +281,7 @@ module.exports = React.createClass({
// |#########| |
// `---------' -
_getExcessHeight: function(backwards) {
var sn = this._getScrollNode();
const sn = this._getScrollNode();
if (backwards) {
return sn.scrollTop - sn.clientHeight - UNPAGINATION_PADDING;
} else {
@ -295,7 +295,7 @@ module.exports = React.createClass({
return;
}
var sn = this._getScrollNode();
const sn = this._getScrollNode();
// if there is less than a screenful of messages above or below the
// viewport, try to get some more messages.
@ -377,7 +377,7 @@ module.exports = React.createClass({
// check if there is already a pending fill request. If not, set one off.
_maybeFill: function(backwards) {
var dir = backwards ? 'b' : 'f';
const dir = backwards ? 'b' : 'f';
if (this._pendingFillRequests[dir]) {
debuglog("ScrollPanel: Already a "+dir+" fill in progress - not starting another");
return;
@ -470,8 +470,8 @@ module.exports = React.createClass({
* mult: -1 to page up, +1 to page down
*/
scrollRelative: function(mult) {
var scrollNode = this._getScrollNode();
var delta = mult * scrollNode.clientHeight * 0.5;
const scrollNode = this._getScrollNode();
const delta = mult * scrollNode.clientHeight * 0.5;
this._setScrollTop(scrollNode.scrollTop + delta);
this._saveScrollState();
},
@ -535,7 +535,7 @@ module.exports = React.createClass({
this.scrollState = {
stuckAtBottom: false,
trackedScrollToken: scrollToken,
pixelOffset: pixelOffset
pixelOffset: pixelOffset,
};
// ... then make it so.
@ -546,10 +546,10 @@ module.exports = React.createClass({
// given offset in the window. A helper for _restoreSavedScrollState.
_scrollToToken: function(scrollToken, pixelOffset) {
/* find the dom node with the right scrolltoken */
var node;
var messages = this.refs.itemlist.children;
for (var i = messages.length-1; i >= 0; --i) {
var m = messages[i];
let node;
const messages = this.refs.itemlist.children;
for (let i = messages.length-1; i >= 0; --i) {
const m = messages[i];
// 'data-scroll-tokens' is a DOMString of comma-separated scroll tokens
// There might only be one scroll token
if (m.dataset.scrollTokens &&
@ -564,10 +564,10 @@ module.exports = React.createClass({
return;
}
var scrollNode = this._getScrollNode();
var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
const scrollNode = this._getScrollNode();
const wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
const boundingRect = node.getBoundingClientRect();
const scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")");
@ -575,7 +575,6 @@ module.exports = React.createClass({
if(scrollDelta != 0) {
this._setScrollTop(scrollNode.scrollTop + scrollDelta);
}
},
_saveScrollState: function() {
@ -585,16 +584,16 @@ module.exports = React.createClass({
return;
}
var itemlist = this.refs.itemlist;
var wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
var messages = itemlist.children;
const itemlist = this.refs.itemlist;
const wrapperRect = ReactDOM.findDOMNode(this).getBoundingClientRect();
const messages = itemlist.children;
let newScrollState = null;
for (var i = messages.length-1; i >= 0; --i) {
var node = messages[i];
for (let i = messages.length-1; i >= 0; --i) {
const node = messages[i];
if (!node.dataset.scrollTokens) continue;
var boundingRect = node.getBoundingClientRect();
const boundingRect = node.getBoundingClientRect();
newScrollState = {
stuckAtBottom: false,
trackedScrollToken: node.dataset.scrollTokens.split(',')[0],
@ -619,8 +618,8 @@ module.exports = React.createClass({
},
_restoreSavedScrollState: function() {
var scrollState = this.scrollState;
var scrollNode = this._getScrollNode();
const scrollState = this.scrollState;
const scrollNode = this._getScrollNode();
if (scrollState.stuckAtBottom) {
this._setScrollTop(Number.MAX_VALUE);
@ -631,9 +630,9 @@ module.exports = React.createClass({
},
_setScrollTop: function(scrollTop) {
var scrollNode = this._getScrollNode();
const scrollNode = this._getScrollNode();
var prevScroll = scrollNode.scrollTop;
const prevScroll = scrollNode.scrollTop;
// FF ignores attempts to set scrollTop to very large numbers
scrollNode.scrollTop = Math.min(scrollTop, scrollNode.scrollHeight);
@ -676,7 +675,7 @@ module.exports = React.createClass({
className={this.props.className} style={this.props.style}>
<div className="mx_RoomView_messageListWrapper">
<ol ref="itemlist" className="mx_RoomView_MessageList" aria-live="polite">
{this.props.children}
{ this.props.children }
</ol>
</div>
</GeminiScrollbar>

View file

@ -15,27 +15,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ReactDOM = require("react-dom");
const React = require('react');
const ReactDOM = require("react-dom");
import Promise from 'bluebird';
var Matrix = require("matrix-js-sdk");
var EventTimeline = Matrix.EventTimeline;
const Matrix = require("matrix-js-sdk");
const EventTimeline = Matrix.EventTimeline;
var sdk = require('../../index');
const sdk = require('../../index');
import { _t } from '../../languageHandler';
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
var ObjectUtils = require('../../ObjectUtils');
var Modal = require("../../Modal");
var UserActivity = require("../../UserActivity");
var KeyCode = require('../../KeyCode');
const MatrixClientPeg = require("../../MatrixClientPeg");
const dis = require("../../dispatcher");
const ObjectUtils = require('../../ObjectUtils');
const Modal = require("../../Modal");
const UserActivity = require("../../UserActivity");
const KeyCode = require('../../KeyCode');
import UserSettingsStore from '../../UserSettingsStore';
var PAGINATE_SIZE = 20;
var INITIAL_SIZE = 20;
const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20;
var DEBUG = false;
const DEBUG = false;
if (DEBUG) {
// using bind means that we get to keep useful line numbers in the console
@ -260,7 +260,7 @@ var TimelinePanel = React.createClass({
dis.unregister(this.dispatcherRef);
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
if (client) {
client.removeListener("Room.timeline", this.onRoomTimeline);
client.removeListener("Room.timelineReset", this.onRoomTimelineReset);
@ -275,20 +275,20 @@ var TimelinePanel = React.createClass({
onMessageListUnfillRequest: function(backwards, scrollToken) {
// If backwards, unpaginate from the back (i.e. the start of the timeline)
let dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
debuglog("TimelinePanel: unpaginating events in direction", dir);
// All tiles are inserted by MessagePanel to have a scrollToken === eventId, and
// this particular event should be the first or last to be unpaginated.
let eventId = scrollToken;
const eventId = scrollToken;
let marker = this.state.events.findIndex(
const marker = this.state.events.findIndex(
(ev) => {
return ev.getId() === eventId;
}
},
);
let count = backwards ? marker + 1 : this.state.events.length - marker;
const count = backwards ? marker + 1 : this.state.events.length - marker;
if (count > 0) {
debuglog("TimelinePanel: Unpaginating", count, "in direction", dir);
@ -305,9 +305,9 @@ var TimelinePanel = React.createClass({
// set off a pagination request.
onMessageListFillRequest: function(backwards) {
var dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
var canPaginateKey = backwards ? 'canBackPaginate' : 'canForwardPaginate';
var paginatingKey = backwards ? 'backPaginating' : 'forwardPaginating';
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const canPaginateKey = backwards ? 'canBackPaginate' : 'canForwardPaginate';
const paginatingKey = backwards ? 'backPaginating' : 'forwardPaginating';
if (!this.state[canPaginateKey]) {
debuglog("TimelinePanel: have given up", dir, "paginating this timeline");
@ -328,7 +328,7 @@ var TimelinePanel = React.createClass({
debuglog("TimelinePanel: paginate complete backwards:"+backwards+"; success:"+r);
var newState = {
const newState = {
[paginatingKey]: false,
[canPaginateKey]: r,
events: this._getEvents(),
@ -336,8 +336,8 @@ var TimelinePanel = React.createClass({
// moving the window in this direction may mean that we can now
// paginate in the other where we previously could not.
var otherDirection = backwards ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
var canPaginateOtherWayKey = backwards ? 'canForwardPaginate' : 'canBackPaginate';
const otherDirection = backwards ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
const canPaginateOtherWayKey = backwards ? 'canForwardPaginate' : 'canBackPaginate';
if (!this.state[canPaginateOtherWayKey] &&
this._timelineWindow.canPaginate(otherDirection)) {
debuglog('TimelinePanel: can now', otherDirection, 'paginate again');
@ -420,15 +420,15 @@ var TimelinePanel = React.createClass({
this._timelineWindow.paginate(EventTimeline.FORWARDS, 1, false).done(() => {
if (this.unmounted) { return; }
var events = this._timelineWindow.getEvents();
var lastEv = events[events.length-1];
const events = this._timelineWindow.getEvents();
const lastEv = events[events.length-1];
// if we're at the end of the live timeline, append the pending events
if (this.props.timelineSet.room && !this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
events.push(...this.props.timelineSet.room.getPendingEvents());
}
var updatedState = {events: events};
const updatedState = {events: events};
if (this.props.manageReadMarkers) {
// when a new event arrives when the user is not watching the
@ -439,8 +439,8 @@ var TimelinePanel = React.createClass({
// read-marker when a remote echo of an event we have just sent takes
// more than the timeout on userCurrentlyActive.
//
var myUserId = MatrixClientPeg.get().credentials.userId;
var sender = ev.sender ? ev.sender.userId : null;
const myUserId = MatrixClientPeg.get().credentials.userId;
const sender = ev.sender ? ev.sender.userId : null;
var callback = null;
if (sender != myUserId && !UserActivity.userCurrentlyActive()) {
updatedState.readMarkerVisible = true;
@ -646,7 +646,7 @@ var TimelinePanel = React.createClass({
// and we'll get confused when their ID changes and we can't figure out
// where the RM is pointing to. The read marker will be invisible for
// now anyway, so this doesn't really matter.
var lastDisplayedIndex = this._getLastDisplayedEventIndex({
const lastDisplayedIndex = this._getLastDisplayedEventIndex({
allowPartial: true,
ignoreEchoes: true,
});
@ -655,7 +655,7 @@ var TimelinePanel = React.createClass({
return;
}
var lastDisplayedEvent = this.state.events[lastDisplayedIndex];
const lastDisplayedEvent = this.state.events[lastDisplayedIndex];
this._setReadMarker(lastDisplayedEvent.getId(),
lastDisplayedEvent.getTs());
@ -676,7 +676,7 @@ var TimelinePanel = React.createClass({
// we call _timelineWindow.getEvents() rather than using
// this.state.events, because react batches the update to the latter, so it
// may not have been updated yet.
var events = this._timelineWindow.getEvents();
const events = this._timelineWindow.getEvents();
// first find where the current RM is
for (var i = 0; i < events.length; i++) {
@ -689,7 +689,7 @@ var TimelinePanel = React.createClass({
}
// now think about advancing it
var myUserId = MatrixClientPeg.get().credentials.userId;
const myUserId = MatrixClientPeg.get().credentials.userId;
for (i++; i < events.length; i++) {
var ev = events[i];
if (!ev.sender || ev.sender.userId != myUserId) {
@ -734,7 +734,7 @@ var TimelinePanel = React.createClass({
//
// a quick way to figure out if we've loaded the relevant event is
// simply to check if the messagepanel knows where the read-marker is.
var ret = this.refs.messagePanel.getReadMarkerPosition();
const ret = this.refs.messagePanel.getReadMarkerPosition();
if (ret !== null) {
// The messagepanel knows where the RM is, so we must have loaded
// the relevant event.
@ -755,13 +755,13 @@ var TimelinePanel = React.createClass({
forgetReadMarker: function() {
if (!this.props.manageReadMarkers) return;
var rmId = this._getCurrentReadReceipt();
const rmId = this._getCurrentReadReceipt();
// see if we know the timestamp for the rr event
var tl = this.props.timelineSet.getTimelineForEvent(rmId);
var rmTs;
const tl = this.props.timelineSet.getTimelineForEvent(rmId);
let rmTs;
if (tl) {
var event = tl.getEvents().find((e) => { return e.getId() == rmId; });
const event = tl.getEvents().find((e) => { return e.getId() == rmId; });
if (event) {
rmTs = event.getTs();
}
@ -801,7 +801,7 @@ var TimelinePanel = React.createClass({
if (!this.props.manageReadMarkers) return null;
if (!this.refs.messagePanel) return null;
var ret = this.refs.messagePanel.getReadMarkerPosition();
const ret = this.refs.messagePanel.getReadMarkerPosition();
if (ret !== null) {
return ret;
}
@ -844,8 +844,7 @@ var TimelinePanel = React.createClass({
// jump to the live timeline on ctrl-end, rather than the end of the
// timeline window.
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey &&
ev.keyCode == KeyCode.END)
{
ev.keyCode == KeyCode.END) {
this.jumpToLiveTimeline();
} else {
this.refs.messagePanel.handleScrollKey(ev);
@ -853,12 +852,12 @@ var TimelinePanel = React.createClass({
},
_initTimeline: function(props) {
var initialEvent = props.eventId;
var pixelOffset = props.eventPixelOffset;
const initialEvent = props.eventId;
const pixelOffset = props.eventPixelOffset;
// if a pixelOffset is given, it is relative to the bottom of the
// container. If not, put the event in the middle of the container.
var offsetBase = 1;
let offsetBase = 1;
if (pixelOffset == null) {
offsetBase = 0.5;
}
@ -887,7 +886,7 @@ var TimelinePanel = React.createClass({
MatrixClientPeg.get(), this.props.timelineSet,
{windowLimit: this.props.timelineCap});
var onLoaded = () => {
const onLoaded = () => {
this._reloadEvents();
// If we switched away from the room while there were pending
@ -922,15 +921,15 @@ var TimelinePanel = React.createClass({
});
};
var onError = (error) => {
const onError = (error) => {
this.setState({timelineLoading: false});
console.error(
`Error loading timeline panel at ${eventId}: ${error}`,
);
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const msg = error.message ? error.message : JSON.stringify(error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
var onFinished;
let onFinished;
// if we were given an event ID, then when the user closes the
// dialog, let's jump to the end of the timeline. If we weren't,
@ -945,7 +944,7 @@ var TimelinePanel = React.createClass({
});
};
}
var message = (error.errcode == 'M_FORBIDDEN')
const message = (error.errcode == 'M_FORBIDDEN')
? _t("Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.")
: _t("Tried to load a specific point in this room's timeline, but was unable to find it.");
Modal.createTrackedDialog('Failed to load timeline position', '', ErrorDialog, {
@ -955,7 +954,7 @@ var TimelinePanel = React.createClass({
});
};
var prom = this._timelineWindow.load(eventId, INITIAL_SIZE);
let prom = this._timelineWindow.load(eventId, INITIAL_SIZE);
// if we already have the event in question, TimelineWindow.load
// returns a resolved promise.
@ -996,7 +995,7 @@ var TimelinePanel = React.createClass({
// get the list of events from the timeline window and the pending event list
_getEvents: function() {
var events = this._timelineWindow.getEvents();
const events = this._timelineWindow.getEvents();
// if we're at the end of the live timeline, append the pending events
if (!this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
@ -1007,7 +1006,7 @@ var TimelinePanel = React.createClass({
},
_indexForEventId: function(evId) {
for (var i = 0; i < this.state.events.length; ++i) {
for (let i = 0; i < this.state.events.length; ++i) {
if (evId == this.state.events[i].getId()) {
return i;
}
@ -1017,18 +1016,18 @@ var TimelinePanel = React.createClass({
_getLastDisplayedEventIndex: function(opts) {
opts = opts || {};
var ignoreOwn = opts.ignoreOwn || false;
var ignoreEchoes = opts.ignoreEchoes || false;
var allowPartial = opts.allowPartial || false;
const ignoreOwn = opts.ignoreOwn || false;
const ignoreEchoes = opts.ignoreEchoes || false;
const allowPartial = opts.allowPartial || false;
var messagePanel = this.refs.messagePanel;
const messagePanel = this.refs.messagePanel;
if (messagePanel === undefined) return null;
var wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect();
var myUserId = MatrixClientPeg.get().credentials.userId;
const wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect();
const myUserId = MatrixClientPeg.get().credentials.userId;
for (var i = this.state.events.length-1; i >= 0; --i) {
var ev = this.state.events[i];
for (let i = this.state.events.length-1; i >= 0; --i) {
const ev = this.state.events[i];
if (ignoreOwn && ev.sender && ev.sender.userId == myUserId) {
continue;
@ -1039,10 +1038,10 @@ var TimelinePanel = React.createClass({
continue;
}
var node = messagePanel.getNodeForEventId(ev.getId());
const node = messagePanel.getNodeForEventId(ev.getId());
if (!node) continue;
var boundingRect = node.getBoundingClientRect();
const boundingRect = node.getBoundingClientRect();
if ((allowPartial && boundingRect.top < wrapperRect.bottom) ||
(!allowPartial && boundingRect.bottom < wrapperRect.bottom)) {
return i;
@ -1060,18 +1059,18 @@ var TimelinePanel = React.createClass({
* SDK.
*/
_getCurrentReadReceipt: function(ignoreSynthesized) {
var client = MatrixClientPeg.get();
const client = MatrixClientPeg.get();
// the client can be null on logout
if (client == null) {
return null;
}
var myUserId = client.credentials.userId;
const myUserId = client.credentials.userId;
return this.props.timelineSet.room.getEventReadUpTo(myUserId, ignoreSynthesized);
},
_setReadMarker: function(eventId, eventTs, inhibitSetState) {
var roomId = this.props.timelineSet.room.roomId;
const roomId = this.props.timelineSet.room.roomId;
// don't update the state (and cause a re-render) if there is
// no change to the RM.
@ -1096,8 +1095,8 @@ var TimelinePanel = React.createClass({
},
render: function() {
var MessagePanel = sdk.getComponent("structures.MessagePanel");
var Loader = sdk.getComponent("elements.Spinner");
const MessagePanel = sdk.getComponent("structures.MessagePanel");
const Loader = sdk.getComponent("elements.Spinner");
// just show a spinner while the timeline loads.
//
@ -1112,7 +1111,7 @@ var TimelinePanel = React.createClass({
// exist.
if (this.state.timelineLoading) {
return (
<div className={ this.props.className + " mx_RoomView_messageListWrapper" }>
<div className={this.props.className + " mx_RoomView_messageListWrapper"}>
<Loader />
</div>
);
@ -1120,7 +1119,7 @@ var TimelinePanel = React.createClass({
if (this.state.events.length == 0 && !this.state.canBackPaginate && this.props.empty) {
return (
<div className={ this.props.className + " mx_RoomView_messageListWrapper" }>
<div className={this.props.className + " mx_RoomView_messageListWrapper"}>
<div className="mx_RoomView_empty">{ this.props.empty }</div>
</div>
);
@ -1134,7 +1133,7 @@ var TimelinePanel = React.createClass({
// forwards, otherwise if somebody hits the bottom of the loaded
// events when viewing historical messages, we get stuck in a loop
// of paginating our way through the entire history of the room.
var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
const stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
// If the state is PREPARED, we're still waiting for the js-sdk to sync with
// the HS and fetch the latest events, so we are effectively forward paginating.
@ -1143,26 +1142,26 @@ var TimelinePanel = React.createClass({
);
return (
<MessagePanel ref="messagePanel"
hidden={ this.props.hidden }
backPaginating={ this.state.backPaginating }
forwardPaginating={ forwardPaginating }
events={ this.state.events }
highlightedEventId={ this.props.highlightedEventId }
readMarkerEventId={ this.state.readMarkerEventId }
readMarkerVisible={ this.state.readMarkerVisible }
suppressFirstDateSeparator={ this.state.canBackPaginate }
showUrlPreview={ this.props.showUrlPreview }
showReadReceipts={ this.props.showReadReceipts }
ourUserId={ MatrixClientPeg.get().credentials.userId }
stickyBottom={ stickyBottom }
onScroll={ this.onMessageListScroll }
onFillRequest={ this.onMessageListFillRequest }
onUnfillRequest={ this.onMessageListUnfillRequest }
opacity={ this.props.opacity }
isTwelveHour={ this.state.isTwelveHour }
alwaysShowTimestamps={ this.state.alwaysShowTimestamps }
className={ this.props.className }
tileShape={ this.props.tileShape }
hidden={this.props.hidden}
backPaginating={this.state.backPaginating}
forwardPaginating={forwardPaginating}
events={this.state.events}
highlightedEventId={this.props.highlightedEventId}
readMarkerEventId={this.state.readMarkerEventId}
readMarkerVisible={this.state.readMarkerVisible}
suppressFirstDateSeparator={this.state.canBackPaginate}
showUrlPreview={this.props.showUrlPreview}
showReadReceipts={this.props.showReadReceipts}
ourUserId={MatrixClientPeg.get().credentials.userId}
stickyBottom={stickyBottom}
onScroll={this.onMessageListScroll}
onFillRequest={this.onMessageListFillRequest}
onUnfillRequest={this.onMessageListUnfillRequest}
opacity={this.props.opacity}
isTwelveHour={this.state.isTwelveHour}
alwaysShowTimestamps={this.state.alwaysShowTimestamps}
className={this.props.className}
tileShape={this.props.tileShape}
/>
);
},

View file

@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ContentMessages = require('../../ContentMessages');
var dis = require('../../dispatcher');
var filesize = require('filesize');
const React = require('react');
const ContentMessages = require('../../ContentMessages');
const dis = require('../../dispatcher');
const filesize = require('filesize');
import { _t } from '../../languageHandler';
module.exports = React.createClass({displayName: 'UploadBar',
propTypes: {
room: React.PropTypes.object
room: React.PropTypes.object,
},
componentDidMount: function() {
@ -46,7 +46,7 @@ module.exports = React.createClass({displayName: 'UploadBar',
},
render: function() {
var uploads = ContentMessages.getCurrentUploads();
const uploads = ContentMessages.getCurrentUploads();
// for testing UI... - also fix up the ContentMessages.getCurrentUploads().length
// check in RoomView
@ -62,8 +62,8 @@ module.exports = React.createClass({displayName: 'UploadBar',
return <div />;
}
var upload;
for (var i = 0; i < uploads.length; ++i) {
let upload;
for (let i = 0; i < uploads.length; ++i) {
if (uploads[i].roomId == this.props.room.roomId) {
upload = uploads[i];
break;
@ -73,32 +73,32 @@ module.exports = React.createClass({displayName: 'UploadBar',
return <div />;
}
var innerProgressStyle = {
width: ((upload.loaded / (upload.total || 1)) * 100) + '%'
const innerProgressStyle = {
width: ((upload.loaded / (upload.total || 1)) * 100) + '%',
};
var uploadedSize = filesize(upload.loaded);
var totalSize = filesize(upload.total);
let uploadedSize = filesize(upload.loaded);
const totalSize = filesize(upload.total);
if (uploadedSize.replace(/^.* /, '') === totalSize.replace(/^.* /, '')) {
uploadedSize = uploadedSize.replace(/ .*/, '');
}
// MUST use var name 'count' for pluralization to kick in
var uploadText = _t("Uploading %(filename)s and %(count)s others", {filename: upload.fileName, count: (uploads.length - 1)});
const uploadText = _t("Uploading %(filename)s and %(count)s others", {filename: upload.fileName, count: (uploads.length - 1)});
return (
<div className="mx_UploadBar">
<div className="mx_UploadBar_uploadProgressOuter">
<div className="mx_UploadBar_uploadProgressInner" style={innerProgressStyle}></div>
</div>
<img className="mx_UploadBar_uploadIcon mx_filterFlipColor" src="img/fileicon.png" width="17" height="22"/>
<img className="mx_UploadBar_uploadIcon mx_filterFlipColor" src="img/fileicon.png" width="17" height="22" />
<img className="mx_UploadBar_uploadCancel mx_filterFlipColor" src="img/cancel.svg" width="18" height="18"
onClick={function() { ContentMessages.cancelUpload(upload.promise); }}
/>
<div className="mx_UploadBar_uploadBytes">
{ uploadedSize } / { totalSize }
</div>
<div className="mx_UploadBar_uploadFilename">{uploadText}</div>
<div className="mx_UploadBar_uploadFilename">{ uploadText }</div>
</div>
);
}
},
});

View file

@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -32,7 +33,7 @@ const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import Analytics from '../../Analytics';
import AccessibleButton from '../views/elements/AccessibleButton';
import { _t } from '../../languageHandler';
import { _t, _td } from '../../languageHandler';
import * as languageHandler from '../../languageHandler';
import * as FormattingUtils from '../../utils/FormattingUtils';
@ -63,55 +64,59 @@ const gHVersionLabel = function(repo, token='') {
const SETTINGS_LABELS = [
{
id: 'autoplayGifsAndVideos',
label: 'Autoplay GIFs and videos',
label: _td('Autoplay GIFs and videos'),
},
{
id: 'hideReadReceipts',
label: 'Hide read receipts',
label: _td('Hide read receipts'),
},
{
id: 'dontSendTypingNotifications',
label: "Don't send typing notifications",
label: _td("Don't send typing notifications"),
},
{
id: 'alwaysShowTimestamps',
label: 'Always show message timestamps',
label: _td('Always show message timestamps'),
},
{
id: 'showTwelveHourTimestamps',
label: 'Show timestamps in 12 hour format (e.g. 2:30pm)',
label: _td('Show timestamps in 12 hour format (e.g. 2:30pm)'),
},
{
id: 'hideJoinLeaves',
label: 'Hide join/leave messages (invites/kicks/bans unaffected)',
label: _td('Hide join/leave messages (invites/kicks/bans unaffected)'),
},
{
id: 'hideAvatarDisplaynameChanges',
label: 'Hide avatar and display name changes',
label: _td('Hide avatar and display name changes'),
},
{
id: 'useCompactLayout',
label: 'Use compact timeline layout',
label: _td('Use compact timeline layout'),
},
{
id: 'hideRedactions',
label: 'Hide removed messages',
label: _td('Hide removed messages'),
},
{
id: 'enableSyntaxHighlightLanguageDetection',
label: 'Enable automatic language detection for syntax highlighting',
label: _td('Enable automatic language detection for syntax highlighting'),
},
{
id: 'MessageComposerInput.autoReplaceEmoji',
label: 'Automatically replace plain text Emoji',
label: _td('Automatically replace plain text Emoji'),
},
{
id: 'MessageComposerInput.dontSuggestEmoji',
label: 'Disable Emoji suggestions while typing',
label: _td('Disable Emoji suggestions while typing'),
},
{
id: 'Pill.shouldHidePillAvatar',
label: 'Hide avatars in user and room mentions',
label: _td('Hide avatars in user and room mentions'),
},
{
id: 'TextualBody.disableBigEmoji',
label: _td('Disable big emoji in chat'),
},
/*
{
@ -124,7 +129,7 @@ const SETTINGS_LABELS = [
const ANALYTICS_SETTINGS_LABELS = [
{
id: 'analyticsOptOut',
label: 'Opt out of analytics',
label: _td('Opt out of analytics'),
fn: function(checked) {
Analytics[checked ? 'disable' : 'enable']();
},
@ -134,7 +139,7 @@ const ANALYTICS_SETTINGS_LABELS = [
const WEBRTC_SETTINGS_LABELS = [
{
id: 'webRtcForceTURN',
label: 'Disable Peer-to-Peer for 1:1 calls',
label: _td('Disable Peer-to-Peer for 1:1 calls'),
},
];
@ -143,7 +148,7 @@ const WEBRTC_SETTINGS_LABELS = [
const CRYPTO_SETTINGS_LABELS = [
{
id: 'blacklistUnverifiedDevices',
label: 'Never send encrypted messages to unverified devices from this device',
label: _td('Never send encrypted messages to unverified devices from this device'),
fn: function(checked) {
MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked);
},
@ -166,12 +171,12 @@ const CRYPTO_SETTINGS_LABELS = [
const THEMES = [
{
id: 'theme',
label: 'Light theme',
label: _td('Light theme'),
value: 'light',
},
{
id: 'theme',
label: 'Dark theme',
label: _td('Dark theme'),
value: 'dark',
},
];
@ -212,9 +217,6 @@ module.exports = React.createClass({
// The brand string given when creating email pushers
brand: React.PropTypes.string,
// True to show the 'labs' section of experimental features
enableLabs: React.PropTypes.bool,
// The base URL to use in the referral link. Defaults to window.location.origin.
referralBaseUrl: React.PropTypes.string,
@ -226,7 +228,6 @@ module.exports = React.createClass({
getDefaultProps: function() {
return {
onClose: function() {},
enableLabs: true,
};
},
@ -426,6 +427,11 @@ module.exports = React.createClass({
});
},
onAvatarRemoveClick: function() {
MatrixClientPeg.get().setAvatarUrl(null);
this.setState({avatarUrl: null}); // the avatar update will complete async for us
},
onLogoutClicked: function(ev) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Logout E2E Export', '', QuestionDialog, {
@ -793,7 +799,7 @@ module.exports = React.createClass({
onChange={onChange}
/>
<label htmlFor={setting.id + "_" + setting.value}>
{ setting.label }
{ _t(setting.label) }
</label>
</div>;
},
@ -923,34 +929,25 @@ module.exports = React.createClass({
},
_renderLabs: function() {
// default to enabled if undefined
if (this.props.enableLabs === false) return null;
UserSettingsStore.doTranslations();
const features = [];
UserSettingsStore.LABS_FEATURES.forEach((feature) => {
// This feature has an override and will be set to the default, so do not
// show it here.
if (feature.override) {
return;
}
UserSettingsStore.getLabsFeatures().forEach((featureId) => {
// TODO: this ought to be a separate component so that we don't need
// to rebind the onChange each time we render
const onChange = (e) => {
UserSettingsStore.setFeatureEnabled(feature.id, e.target.checked);
UserSettingsStore.setFeatureEnabled(featureId, e.target.checked);
this.forceUpdate();
};
features.push(
<div key={feature.id} className="mx_UserSettings_toggle">
<div key={featureId} className="mx_UserSettings_toggle">
<input
type="checkbox"
id={feature.id}
name={feature.id}
defaultChecked={UserSettingsStore.isFeatureEnabled(feature.id)}
id={featureId}
name={featureId}
defaultChecked={UserSettingsStore.isFeatureEnabled(featureId)}
onChange={onChange}
/>
<label htmlFor={feature.id}>{ feature.name }</label>
<label htmlFor={featureId}>{ UserSettingsStore.translatedNameForFeature(featureId) }</label>
</div>);
});
@ -1330,7 +1327,11 @@ module.exports = React.createClass({
</div>
<div className="mx_UserSettings_avatarPicker">
<div onClick={this.onAvatarPickerClick}>
<div className="mx_UserSettings_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
<img src="img/cancel.svg" width="15" height="15"
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
</div>
<div onClick={this.onAvatarPickerClick} className="mx_UserSettings_avatarPicker_imgContainer">
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
showUploadSection={false} className="mx_UserSettings_avatarPicker_img" />
</div>

View file

@ -17,13 +17,13 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
var Modal = require("../../../Modal");
var MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require('../../../index');
const Modal = require("../../../Modal");
const MatrixClientPeg = require('../../../MatrixClientPeg');
var PasswordReset = require("../../../PasswordReset");
const PasswordReset = require("../../../PasswordReset");
module.exports = React.createClass({
displayName: 'ForgotPassword',
@ -35,30 +35,30 @@ module.exports = React.createClass({
customIsUrl: React.PropTypes.string,
onLoginClick: React.PropTypes.func,
onRegisterClick: React.PropTypes.func,
onComplete: React.PropTypes.func.isRequired
onComplete: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
progress: null
progress: null,
};
},
submitPasswordReset: function(hsUrl, identityUrl, email, password) {
this.setState({
progress: "sending_email"
progress: "sending_email",
});
this.reset = new PasswordReset(hsUrl, identityUrl);
this.reset.resetPassword(email, password).done(() => {
this.setState({
progress: "sent_email"
progress: "sent_email",
});
}, (err) => {
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
this.setState({
progress: null
progress: null,
});
});
},
@ -81,15 +81,12 @@ module.exports = React.createClass({
if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
}
else if (!this.state.password || !this.state.password2) {
} else if (!this.state.password || !this.state.password2) {
this.showErrorDialog(_t('A new password must be entered.'));
}
else if (this.state.password !== this.state.password2) {
} else if (this.state.password !== this.state.password2) {
this.showErrorDialog(_t('New passwords must match each other.'));
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
} else {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
title: _t('Warning!'),
description:
@ -99,7 +96,7 @@ module.exports = React.createClass({
'end-to-end encryption keys on all devices, ' +
'making encrypted chat history unreadable, ' +
'unless you first export your room keys and re-import ' +
'them afterwards. In future this will be improved.'
'them afterwards. In future this will be improved.',
) }
</div>,
button: _t('Continue'),
@ -107,13 +104,13 @@ module.exports = React.createClass({
<button className="mx_Dialog_primary"
onClick={this._onExportE2eKeysClicked}>
{ _t('Export E2E room keys') }
</button>
</button>,
],
onFinished: (confirmed) => {
if (confirmed) {
this.submitPasswordReset(
this.state.enteredHomeserverUrl, this.state.enteredIdentityServerUrl,
this.state.email, this.state.password
this.state.email, this.state.password,
);
}
},
@ -133,7 +130,7 @@ module.exports = React.createClass({
onInputChanged: function(stateKey, ev) {
this.setState({
[stateKey]: ev.target.value
[stateKey]: ev.target.value,
});
},
@ -149,7 +146,7 @@ module.exports = React.createClass({
},
showErrorDialog: function(body, title) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
title: title,
description: body,
@ -157,37 +154,34 @@ module.exports = React.createClass({
},
render: function() {
var LoginHeader = sdk.getComponent("login.LoginHeader");
var LoginFooter = sdk.getComponent("login.LoginFooter");
var ServerConfig = sdk.getComponent("login.ServerConfig");
var Spinner = sdk.getComponent("elements.Spinner");
const LoginHeader = sdk.getComponent("login.LoginHeader");
const LoginFooter = sdk.getComponent("login.LoginFooter");
const ServerConfig = sdk.getComponent("login.ServerConfig");
const Spinner = sdk.getComponent("elements.Spinner");
var resetPasswordJsx;
let resetPasswordJsx;
if (this.state.progress === "sending_email") {
resetPasswordJsx = <Spinner />;
}
else if (this.state.progress === "sent_email") {
} else if (this.state.progress === "sent_email") {
resetPasswordJsx = (
<div>
{ _t('An email has been sent to') } {this.state.email}. { _t("Once you've followed the link it contains, click below") }.
{ _t('An email has been sent to') } { this.state.email }. { _t("Once you've followed the link it contains, click below") }.
<br />
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
value={ _t('I have verified my email address') } />
value={_t('I have verified my email address')} />
</div>
);
}
else if (this.state.progress === "complete") {
} else if (this.state.progress === "complete") {
resetPasswordJsx = (
<div>
<p>{ _t('Your password has been reset') }.</p>
<p>{ _t('You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device') }.</p>
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
value={ _t('Return to login screen') } />
value={_t('Return to login screen')} />
</div>
);
}
else {
} else {
resetPasswordJsx = (
<div>
<div className="mx_Login_prompt">
@ -199,21 +193,21 @@ module.exports = React.createClass({
name="reset_email" // define a name so browser's password autofill gets less confused
value={this.state.email}
onChange={this.onInputChanged.bind(this, "email")}
placeholder={ _t('Email address') } autoFocus />
placeholder={_t('Email address')} autoFocus />
<br />
<input className="mx_Login_field" ref="pass" type="password"
name="reset_password"
value={this.state.password}
onChange={this.onInputChanged.bind(this, "password")}
placeholder={ _t('New password') } />
placeholder={_t('New password')} />
<br />
<input className="mx_Login_field" ref="pass" type="password"
name="reset_password_confirm"
value={this.state.password2}
onChange={this.onInputChanged.bind(this, "password2")}
placeholder={ _t('Confirm your new password') } />
placeholder={_t('Confirm your new password')} />
<br />
<input className="mx_Login_submit" type="submit" value={ _t('Send Reset Email') } />
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
</form>
<ServerConfig ref="serverConfig"
withToggleButton={true}
@ -222,11 +216,11 @@ module.exports = React.createClass({
customHsUrl={this.props.customHsUrl}
customIsUrl={this.props.customIsUrl}
onServerConfigChange={this.onServerConfigChange}
delayTimeMs={0}/>
delayTimeMs={0} />
<div className="mx_Login_error">
</div>
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
{_t('Return to login screen')}
{ _t('Return to login screen') }
</a>
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
{ _t('Create an account') }
@ -242,9 +236,9 @@ module.exports = React.createClass({
<div className="mx_Login">
<div className="mx_Login_box">
<LoginHeader />
{resetPasswordJsx}
{ resetPasswordJsx }
</div>
</div>
);
}
},
});

View file

@ -134,7 +134,7 @@ module.exports = React.createClass({
},
_onLoginAsGuestClick: function() {
var self = this;
const self = this;
self.setState({
busy: true,
errorText: null,
@ -156,7 +156,7 @@ module.exports = React.createClass({
});
}).finally(function() {
self.setState({
busy: false
busy: false,
});
}).done();
},
@ -183,8 +183,8 @@ module.exports = React.createClass({
},
onServerConfigChange: function(config) {
var self = this;
let newState = {
const self = this;
const newState = {
errorText: null, // reset err messages
};
if (config.hsUrl !== undefined) {
@ -199,13 +199,13 @@ module.exports = React.createClass({
},
_initLoginLogic: function(hsUrl, isUrl) {
var self = this;
const self = this;
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
isUrl = isUrl || this.state.enteredIdentityServerUrl;
var fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
const fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
var loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
});
this._loginLogic = loginLogic;
@ -259,15 +259,15 @@ module.exports = React.createClass({
{ _tJsx("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
"Either use HTTPS or <a>enable unsafe scripts</a>.",
/<a>(.*?)<\/a>/,
(sub) => { return <a href="https://www.google.com/search?&q=enable%20unsafe%20scripts">{ sub }</a>; }
)}
(sub) => { return <a href="https://www.google.com/search?&q=enable%20unsafe%20scripts">{ sub }</a>; },
) }
</span>;
} else {
errorText = <span>
{ _tJsx("Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.",
/<a>(.*?)<\/a>/,
(sub) => { return <a href={this.state.enteredHomeserverUrl}>{ sub }</a>; }
)}
(sub) => { return <a href={this.state.enteredHomeserverUrl}>{ sub }</a>; },
) }
</span>;
}
}
@ -290,6 +290,7 @@ module.exports = React.createClass({
onPhoneNumberChanged={this.onPhoneNumberChanged}
onForgotPasswordClick={this.props.onForgotPasswordClick}
loginIncorrect={this.state.loginIncorrect}
hsUrl={this.state.enteredHomeserverUrl}
/>
);
case 'm.login.cas':
@ -303,7 +304,7 @@ module.exports = React.createClass({
}
return (
<div>
{ _t('Sorry, this homeserver is using a login which is not recognised ')}({step})
{ _t('Sorry, this homeserver is using a login which is not recognised ') }({ step })
</div>
);
}
@ -333,19 +334,19 @@ module.exports = React.createClass({
const ServerConfig = sdk.getComponent("login.ServerConfig");
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
var loginAsGuestJsx;
let loginAsGuestJsx;
if (this.props.enableGuest) {
loginAsGuestJsx =
<a className="mx_Login_create" onClick={this._onLoginAsGuestClick} href="#">
{ _t('Login as guest')}
{ _t('Login as guest') }
</a>;
}
var returnToAppJsx;
let returnToAppJsx;
if (this.props.onCancelClick) {
returnToAppJsx =
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
{ _t('Return to app')}
{ _t('Return to app') }
</a>;
}
@ -354,7 +355,7 @@ module.exports = React.createClass({
<div className="mx_Login_box">
<LoginHeader />
<div>
<h2>{ _t('Sign in')}
<h2>{ _t('Sign in') }
{ loader }
</h2>
{ this.componentForStep(this.state.currentFlow) }
@ -365,12 +366,12 @@ module.exports = React.createClass({
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange}
delayTimeMs={1000}/>
delayTimeMs={1000} />
<div className="mx_Login_error">
{ this.state.errorText }
</div>
<a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#">
{ _t('Create an account')}
{ _t('Create an account') }
</a>
{ loginAsGuestJsx }
{ returnToAppJsx }
@ -380,5 +381,5 @@ module.exports = React.createClass({
</div>
</div>
);
}
},
});

View file

@ -25,14 +25,14 @@ module.exports = React.createClass({
displayName: 'PostRegistration',
propTypes: {
onComplete: React.PropTypes.func.isRequired
onComplete: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
avatarUrl: null,
errorString: null,
busy: false
busy: false,
};
},
@ -40,26 +40,26 @@ module.exports = React.createClass({
// There is some assymetry between ChangeDisplayName and ChangeAvatar,
// as ChangeDisplayName will auto-get the name but ChangeAvatar expects
// the URL to be passed to you (because it's also used for room avatars).
var cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
this.setState({busy: true});
var self = this;
const self = this;
cli.getProfileInfo(cli.credentials.userId).done(function(result) {
self.setState({
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(result.avatar_url),
busy: false
busy: false,
});
}, function(error) {
self.setState({
errorString: _t("Failed to fetch avatar URL"),
busy: false
busy: false,
});
});
},
render: function() {
var ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
var LoginHeader = sdk.getComponent('login.LoginHeader');
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
const LoginHeader = sdk.getComponent('login.LoginHeader');
return (
<div className="mx_Login">
<div className="mx_Login_box">
@ -71,10 +71,10 @@ module.exports = React.createClass({
<ChangeAvatar
initialAvatarUrl={this.state.avatarUrl} />
<button onClick={this.props.onComplete}>{ _t('Continue') }</button>
{this.state.errorString}
{ this.state.errorString }
</div>
</div>
</div>
);
}
},
});

View file

@ -57,7 +57,7 @@ module.exports = React.createClass({
// registration shouldn't know or care how login is done.
onLoginClick: React.PropTypes.func.isRequired,
onCancelClick: React.PropTypes.func
onCancelClick: React.PropTypes.func,
},
getInitialState: function() {
@ -121,7 +121,7 @@ module.exports = React.createClass({
},
onServerConfigChange: function(config) {
let newState = {};
const newState = {};
if (config.hsUrl !== undefined) {
newState.hsUrl = config.hsUrl;
}
@ -195,7 +195,7 @@ module.exports = React.createClass({
this._rtsClient.getTeam(teamToken).then((team) => {
console.log(
`User successfully registered with team ${team.name}`
`User successfully registered with team ${team.name}`,
);
if (!team.rooms) {
return;
@ -223,7 +223,7 @@ module.exports = React.createClass({
deviceId: response.device_id,
homeserverUrl: this._matrixClient.getHomeserverUrl(),
identityServerUrl: this._matrixClient.getIdentityServerUrl(),
accessToken: response.access_token
accessToken: response.access_token,
}, teamToken);
}).then((cli) => {
return this._setupPushers(cli);
@ -253,7 +253,7 @@ module.exports = React.createClass({
},
onFormValidationFailed: function(errCode) {
var errMsg;
let errMsg;
switch (errCode) {
case "RegistrationForm.ERR_PASSWORD_MISSING":
errMsg = _t('Missing password.');
@ -282,7 +282,7 @@ module.exports = React.createClass({
break;
}
this.setState({
errorText: errMsg
errorText: errMsg,
});
},
@ -316,7 +316,7 @@ module.exports = React.createClass({
emailAddress: this.state.formVals.email,
phoneCountry: this.state.formVals.phoneCountry,
phoneNumber: this.state.formVals.phoneNumber,
}
};
},
render: function() {
@ -346,7 +346,7 @@ module.exports = React.createClass({
} else {
let errorSection;
if (this.state.errorText) {
errorSection = <div className="mx_Login_error">{this.state.errorText}</div>;
errorSection = <div className="mx_Login_error">{ this.state.errorText }</div>;
}
registerBody = (
<div>
@ -362,7 +362,7 @@ module.exports = React.createClass({
onRegisterClick={this.onFormSubmit}
onTeamSelected={this.onTeamSelected}
/>
{errorSection}
{ errorSection }
<ServerConfig ref="serverConfig"
withToggleButton={true}
customHsUrl={this.props.customHsUrl}
@ -380,7 +380,7 @@ module.exports = React.createClass({
if (this.props.onCancelClick) {
returnToAppJsx = (
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
{_t('Return to app')}
{ _t('Return to app') }
</a>
);
}
@ -393,15 +393,15 @@ module.exports = React.createClass({
this.state.teamSelected.domain + "/icon.png" :
null}
/>
<h2>{_t('Create an account')}</h2>
{registerBody}
<h2>{ _t('Create an account') }</h2>
{ registerBody }
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
{_t('I already have an account')}
{ _t('I already have an account') }
</a>
{returnToAppJsx}
{ returnToAppJsx }
<LoginFooter />
</div>
</div>
);
}
},
});