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

@ -233,7 +233,7 @@ export default class Autocomplete extends React.Component {
const componentPosition = position;
position++;
const onMouseOver = () => this.setSelection(componentPosition);
const onMouseMove = () => this.setSelection(componentPosition);
const onClick = () => {
this.setSelection(componentPosition);
this.onCompletionClicked();
@ -243,7 +243,7 @@ export default class Autocomplete extends React.Component {
key: i,
ref: `completion${position - 1}`,
className,
onMouseOver,
onMouseMove,
onClick,
});
});
@ -251,15 +251,15 @@ export default class Autocomplete extends React.Component {
return completions.length > 0 ? (
<div key={i} className="mx_Autocomplete_ProviderSection">
<EmojiText element="div" className="mx_Autocomplete_provider_name">{completionResult.provider.getName()}</EmojiText>
{completionResult.provider.renderCompletions(completions)}
<EmojiText element="div" className="mx_Autocomplete_provider_name">{ completionResult.provider.getName() }</EmojiText>
{ completionResult.provider.renderCompletions(completions) }
</div>
) : null;
}).filter((completion) => !!completion);
return !this.state.hide && renderedCompletions.length > 0 ? (
<div className="mx_Autocomplete" ref={(e) => this.container = e}>
{renderedCompletions}
{ renderedCompletions }
</div>
) : null;
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -83,9 +84,9 @@ module.exports = React.createClass({
<div className="mx_RoomView_fileDropTarget">
<div className="mx_RoomView_fileDropTargetLabel"
title={_t("Drop File Here")}>
<TintableSvg src="img/upload-big.svg" width="45" height="59"/>
<br/>
{_t("Drop file here to upload")}
<TintableSvg src="img/upload-big.svg" width="45" height="59" />
<br />
{ _t("Drop file here to upload") }
</div>
</div>
);
@ -99,23 +100,23 @@ module.exports = React.createClass({
supportedText = _t(" (unsupported)");
} else {
joinNode = (<span>
{_tJsx(
{ _tJsx(
"Join as <voiceText>voice</voiceText> or <videoText>video</videoText>.",
[/<voiceText>(.*?)<\/voiceText>/, /<videoText>(.*?)<\/videoText>/],
[
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub}</a>,
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'video');}} href="#">{sub}</a>,
]
)}
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'voice');}} href="#">{ sub }</a>,
(sub) => <a onClick={(event)=>{ this.onConferenceNotificationClick(event, 'video');}} href="#">{ sub }</a>,
],
) }
</span>);
}
// XXX: the translation here isn't great: appending ' (unsupported)' is likely to not make sense in many languages,
// but there are translations for this in the languages we do have so I'm leaving it for now.
conferenceCallNotification = (
<div className="mx_RoomView_ongoingConfCallNotification">
{_t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText})}
{ _t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText}) }
&nbsp;
{joinNode}
{ joinNode }
</div>
);
}
@ -128,15 +129,12 @@ module.exports = React.createClass({
/>
);
let appsDrawer = null;
if(UserSettingsStore.isFeatureEnabled('matrix_apps')) {
appsDrawer = <AppsDrawer ref="appsDrawer"
room={this.props.room}
userId={this.props.userId}
maxHeight={this.props.maxHeight}
showApps={this.props.showApps}
/>;
}
const appsDrawer = <AppsDrawer ref="appsDrawer"
room={this.props.room}
userId={this.props.userId}
maxHeight={this.props.maxHeight}
showApps={this.props.showApps}
/>;
return (
<div className="mx_RoomView_auxPanel" style={{maxHeight: this.props.maxHeight}} >

View file

@ -16,18 +16,18 @@ limitations under the License.
'use strict';
var React = require('react');
const React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require('../../../index');
const MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require('../../../index');
import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler';
var PRESENCE_CLASS = {
const PRESENCE_CLASS = {
"offline": "mx_EntityTile_offline",
"online": "mx_EntityTile_online",
"unavailable": "mx_EntityTile_unavailable"
"unavailable": "mx_EntityTile_unavailable",
};
@ -62,7 +62,7 @@ module.exports = React.createClass({
showInviteButton: React.PropTypes.bool,
shouldComponentUpdate: React.PropTypes.func,
onClick: React.PropTypes.func,
suppressOnHover: React.PropTypes.bool
suppressOnHover: React.PropTypes.bool,
},
getDefaultProps: function() {
@ -73,13 +73,13 @@ module.exports = React.createClass({
presenceLastActiveAgo: 0,
presenceLastTs: 0,
showInviteButton: false,
suppressOnHover: false
suppressOnHover: false,
};
},
getInitialState: function() {
return {
hover: false
hover: false,
};
},
@ -98,38 +98,37 @@ module.exports = React.createClass({
render: function() {
const presenceClass = presenceClassForMember(
this.props.presenceState, this.props.presenceLastActiveAgo
this.props.presenceState, this.props.presenceLastActiveAgo,
);
var mainClassName = "mx_EntityTile ";
let mainClassName = "mx_EntityTile ";
mainClassName += presenceClass + (this.props.className ? (" " + this.props.className) : "");
var nameEl;
let nameEl;
const {name} = this.props;
const EmojiText = sdk.getComponent('elements.EmojiText');
if (this.state.hover && !this.props.suppressOnHover) {
var activeAgo = this.props.presenceLastActiveAgo ?
const activeAgo = this.props.presenceLastActiveAgo ?
(Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)) : -1;
mainClassName += " mx_EntityTile_hover";
var PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
const PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
nameEl = (
<div className="mx_EntityTile_details">
<img className="mx_EntityTile_chevron" src="img/member_chevron.png" width="8" height="12"/>
<EmojiText element="div" className="mx_EntityTile_name_hover" dir="auto">{name}</EmojiText>
<PresenceLabel activeAgo={ activeAgo }
<img className="mx_EntityTile_chevron" src="img/member_chevron.png" width="8" height="12" />
<EmojiText element="div" className="mx_EntityTile_name_hover" dir="auto">{ name }</EmojiText>
<PresenceLabel activeAgo={activeAgo}
currentlyActive={this.props.presenceCurrentlyActive}
presenceState={this.props.presenceState} />
</div>
);
}
else {
} else {
nameEl = (
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">{name}</EmojiText>
<EmojiText element="div" className="mx_EntityTile_name" dir="auto">{ name }</EmojiText>
);
}
var inviteButton;
let inviteButton;
if (this.props.showInviteButton) {
inviteButton = (
<div className="mx_EntityTile_invite">
@ -138,25 +137,25 @@ module.exports = React.createClass({
);
}
var power;
var powerLevel = this.props.powerLevel;
let power;
const powerLevel = this.props.powerLevel;
if (powerLevel >= 50 && powerLevel < 99) {
power = <img src="img/mod.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Moderator")}/>;
power = <img src="img/mod.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Moderator")} />;
}
if (powerLevel >= 99) {
power = <img src="img/admin.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Admin")}/>;
power = <img src="img/admin.svg" className="mx_EntityTile_power" width="16" height="17" alt={_t("Admin")} />;
}
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
var av = this.props.avatarJsx || <BaseAvatar name={this.props.name} width={36} height={36} />;
const av = this.props.avatarJsx || <BaseAvatar name={this.props.name} width={36} height={36} />;
return (
<AccessibleButton className={mainClassName} title={ this.props.title }
onClick={ this.props.onClick } onMouseEnter={ this.mouseEnter }
onMouseLeave={ this.mouseLeave }>
<AccessibleButton className={mainClassName} title={this.props.title}
onClick={this.props.onClick} onMouseEnter={this.mouseEnter}
onMouseLeave={this.mouseLeave}>
<div className="mx_EntityTile_avatar">
{ av }
{ power }
@ -165,5 +164,5 @@ module.exports = React.createClass({
{ inviteButton }
</AccessibleButton>
);
}
},
});

View file

@ -17,39 +17,39 @@ limitations under the License.
'use strict';
var React = require('react');
var classNames = require("classnames");
const React = require('react');
const classNames = require("classnames");
import { _t } from '../../../languageHandler';
var Modal = require('../../../Modal');
const Modal = require('../../../Modal');
var sdk = require('../../../index');
var TextForEvent = require('../../../TextForEvent');
const sdk = require('../../../index');
const TextForEvent = require('../../../TextForEvent');
import withMatrixClient from '../../../wrappers/withMatrixClient';
var ContextualMenu = require('../../structures/ContextualMenu');
const ContextualMenu = require('../../structures/ContextualMenu');
import dis from '../../../dispatcher';
var ObjectUtils = require('../../../ObjectUtils');
const ObjectUtils = require('../../../ObjectUtils');
var eventTileTypes = {
const eventTileTypes = {
'm.room.message': 'messages.MessageEvent',
'm.room.member' : 'messages.TextualEvent',
'm.call.invite' : 'messages.TextualEvent',
'm.call.answer' : 'messages.TextualEvent',
'm.call.hangup' : 'messages.TextualEvent',
'm.room.name' : 'messages.TextualEvent',
'm.room.avatar' : 'messages.RoomAvatarEvent',
'm.room.topic' : 'messages.TextualEvent',
'm.room.third_party_invite' : 'messages.TextualEvent',
'm.room.history_visibility' : 'messages.TextualEvent',
'm.room.encryption' : 'messages.TextualEvent',
'm.room.power_levels' : 'messages.TextualEvent',
'm.room.member': 'messages.TextualEvent',
'm.call.invite': 'messages.TextualEvent',
'm.call.answer': 'messages.TextualEvent',
'm.call.hangup': 'messages.TextualEvent',
'm.room.name': 'messages.TextualEvent',
'm.room.avatar': 'messages.RoomAvatarEvent',
'm.room.topic': 'messages.TextualEvent',
'm.room.third_party_invite': 'messages.TextualEvent',
'm.room.history_visibility': 'messages.TextualEvent',
'm.room.encryption': 'messages.TextualEvent',
'm.room.power_levels': 'messages.TextualEvent',
'm.room.pinned_events' : 'messages.TextualEvent',
'im.vector.modular.widgets': 'messages.TextualEvent',
};
var MAX_READ_AVATARS = 5;
const MAX_READ_AVATARS = 5;
// Our component structure for EventTiles on the timeline is:
//
@ -178,7 +178,7 @@ module.exports = withMatrixClient(React.createClass({
},
componentWillUnmount: function() {
var client = this.props.matrixClient;
const client = this.props.matrixClient;
client.removeListener("deviceVerificationChanged",
this.onDeviceVerificationChanged);
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
@ -205,20 +205,20 @@ module.exports = withMatrixClient(React.createClass({
const verified = await this.props.matrixClient.isEventSenderVerified(mxEvent);
this.setState({
verified: verified
verified: verified,
});
},
_propsEqual: function(objA, objB) {
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
for (var i = 0; i < keysA.length; i++) {
var key = keysA[i];
for (let i = 0; i < keysA.length; i++) {
const key = keysA[i];
if (!objB.hasOwnProperty(key)) {
return false;
@ -226,8 +226,8 @@ module.exports = withMatrixClient(React.createClass({
// need to deep-compare readReceipts
if (key == 'readReceipts') {
var rA = objA[key];
var rB = objB[key];
const rA = objA[key];
const rB = objB[key];
if (rA === rB) {
continue;
}
@ -239,7 +239,7 @@ module.exports = withMatrixClient(React.createClass({
if (rA.length !== rB.length) {
return false;
}
for (var j = 0; j < rA.length; j++) {
for (let j = 0; j < rA.length; j++) {
if (rA[j].roomMember.userId !== rB[j].roomMember.userId) {
return false;
}
@ -254,12 +254,11 @@ module.exports = withMatrixClient(React.createClass({
},
shouldHighlight: function() {
var actions = this.props.matrixClient.getPushActionsForEvent(this.props.mxEvent);
const actions = this.props.matrixClient.getPushActionsForEvent(this.props.mxEvent);
if (!actions || !actions.tweaks) { return false; }
// don't show self-highlights from another of our clients
if (this.props.mxEvent.getSender() === this.props.matrixClient.credentials.userId)
{
if (this.props.mxEvent.getSender() === this.props.matrixClient.credentials.userId) {
return false;
}
@ -267,13 +266,13 @@ module.exports = withMatrixClient(React.createClass({
},
onEditClicked: function(e) {
var MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
var buttonRect = e.target.getBoundingClientRect();
const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
const buttonRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
var x = buttonRect.right + window.pageXOffset;
var y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
var self = this;
const x = buttonRect.right + window.pageXOffset;
const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
const self = this;
ContextualMenu.createMenu(MessageContextMenu, {
chevronOffset: 10,
mxEvent: this.props.mxEvent,
@ -282,19 +281,18 @@ module.exports = withMatrixClient(React.createClass({
eventTileOps: this.refs.tile && this.refs.tile.getEventTileOps ? this.refs.tile.getEventTileOps() : undefined,
onFinished: function() {
self.setState({menu: false});
}
},
});
this.setState({menu: true});
},
toggleAllReadAvatars: function() {
this.setState({
allReadAvatars: !this.state.allReadAvatars
allReadAvatars: !this.state.allReadAvatars,
});
},
getReadAvatars: function() {
// return early if there are no read receipts
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
return (<span className="mx_EventTile_readAvatars"></span>);
@ -305,11 +303,11 @@ module.exports = withMatrixClient(React.createClass({
const receiptOffset = 15;
let left = 0;
var receipts = this.props.readReceipts || [];
for (var i = 0; i < receipts.length; ++i) {
var receipt = receipts[i];
const receipts = this.props.readReceipts || [];
for (let i = 0; i < receipts.length; ++i) {
const receipt = receipts[i];
var hidden = true;
let hidden = true;
if ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) {
hidden = false;
}
@ -320,7 +318,7 @@ module.exports = withMatrixClient(React.createClass({
// else set it proportional to index
left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset;
var userId = receipt.roomMember.userId;
const userId = receipt.roomMember.userId;
var readReceiptInfo;
if (this.props.readReceiptMap) {
@ -341,12 +339,12 @@ module.exports = withMatrixClient(React.createClass({
onClick={this.toggleAllReadAvatars}
timestamp={receipt.ts}
showTwelveHour={this.props.isTwelveHour}
/>
/>,
);
}
var remText;
let remText;
if (!this.state.allReadAvatars) {
var remainder = receipts.length - MAX_READ_AVATARS;
const remainder = receipts.length - MAX_READ_AVATARS;
if (remainder > 0) {
remText = <span className="mx_EventTile_readAvatarRemainder"
onClick={this.toggleAllReadAvatars}
@ -370,7 +368,7 @@ module.exports = withMatrixClient(React.createClass({
},
onCryptoClicked: function(e) {
var event = this.props.mxEvent;
const event = this.props.mxEvent;
Modal.createTrackedDialogAsync('Encrypted Event Dialog', '', (cb) => {
require(['../../../async-components/views/dialogs/EncryptedEventDialog'], cb);
@ -397,12 +395,12 @@ module.exports = withMatrixClient(React.createClass({
if (ev.getContent().msgtype === 'm.bad.encrypted') {
return <E2ePadlockUndecryptable {...props}/>;
return <E2ePadlockUndecryptable {...props} />;
} else if (ev.isEncrypted()) {
if (this.state.verified) {
return <E2ePadlockVerified {...props}/>;
return <E2ePadlockVerified {...props} />;
} else {
return <E2ePadlockUnverified {...props}/>;
return <E2ePadlockUnverified {...props} />;
}
} else {
// XXX: if the event is being encrypted (ie eventSendStatus ===
@ -413,7 +411,7 @@ module.exports = withMatrixClient(React.createClass({
// open padlock
const e2eEnabled = this.props.matrixClient.isRoomEncrypted(ev.getRoomId());
if (e2eEnabled) {
return <E2ePadlockUnencrypted {...props}/>;
return <E2ePadlockUnencrypted {...props} />;
}
}
@ -422,27 +420,27 @@ module.exports = withMatrixClient(React.createClass({
},
render: function() {
var MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
var SenderProfile = sdk.getComponent('messages.SenderProfile');
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
const SenderProfile = sdk.getComponent('messages.SenderProfile');
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
//console.log("EventTile showUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
var content = this.props.mxEvent.getContent();
var msgtype = content.msgtype;
var eventType = this.props.mxEvent.getType();
const content = this.props.mxEvent.getContent();
const msgtype = content.msgtype;
const eventType = this.props.mxEvent.getType();
// Info messages are basically information about commands processed on a room
var isInfoMessage = (eventType !== 'm.room.message');
const isInfoMessage = (eventType !== 'm.room.message');
var EventTileType = sdk.getComponent(eventTileTypes[eventType]);
const EventTileType = sdk.getComponent(eventTileTypes[eventType]);
// This shouldn't happen: the caller should check we support this type
// before trying to instantiate us
if (!EventTileType) {
throw new Error("Event type not supported");
}
var isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted;
const classes = classNames({
@ -469,9 +467,9 @@ module.exports = withMatrixClient(React.createClass({
this.props.mxEvent.getRoomId() + "/" +
this.props.mxEvent.getId();
var readAvatars = this.getReadAvatars();
const readAvatars = this.getReadAvatars();
var avatar, sender;
let avatar, sender;
let avatarSize;
let needsSenderProfile;
@ -509,15 +507,14 @@ module.exports = withMatrixClient(React.createClass({
if (msgtype === 'm.image') aux = _t('sent an image');
else if (msgtype === 'm.video') aux = _t('sent a video');
else if (msgtype === 'm.file') aux = _t('uploaded a file');
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} enableFlair={!aux} aux={aux} />;
}
else {
sender = <SenderProfile onClick={this.onSenderProfileClick} mxEvent={this.props.mxEvent} enableFlair={!aux} aux={aux} />;
} else {
sender = <SenderProfile mxEvent={this.props.mxEvent} enableFlair={true} />;
}
}
const editButton = (
<span className="mx_EventTile_editButton" title={ _t("Options") } onClick={this.onEditClicked} />
<span className="mx_EventTile_editButton" title={_t("Options")} onClick={this.onEditClicked} />
);
const timestamp = this.props.mxEvent.getTs() ?
@ -528,13 +525,13 @@ module.exports = withMatrixClient(React.createClass({
return (
<div className={classes}>
<div className="mx_EventTile_roomName">
<a href={ permalink } onClick={this.onPermalinkClicked}>
<a href={permalink} onClick={this.onPermalinkClicked}>
{ room ? room.name : '' }
</a>
</div>
<div className="mx_EventTile_senderDetails">
{ avatar }
<a href={ permalink } onClick={this.onPermalinkClicked}>
<a href={permalink} onClick={this.onPermalinkClicked}>
{ sender }
{ timestamp }
</a>
@ -549,8 +546,7 @@ module.exports = withMatrixClient(React.createClass({
</div>
</div>
);
}
else if (this.props.tileShape === "file_grid") {
} else if (this.props.tileShape === "file_grid") {
return (
<div className={classes}>
<div className="mx_EventTile_line" >
@ -564,7 +560,7 @@ module.exports = withMatrixClient(React.createClass({
</div>
<a
className="mx_EventTile_senderDetailsLink"
href={ permalink }
href={permalink}
onClick={this.onPermalinkClicked}
>
<div className="mx_EventTile_senderDetails">
@ -574,8 +570,7 @@ module.exports = withMatrixClient(React.createClass({
</a>
</div>
);
}
else {
} else {
return (
<div className={classes}>
<div className="mx_EventTile_msgOption">
@ -584,7 +579,7 @@ module.exports = withMatrixClient(React.createClass({
{ avatar }
{ sender }
<div className="mx_EventTile_line">
<a href={ permalink } onClick={this.onPermalinkClicked}>
<a href={permalink} onClick={this.onPermalinkClicked}>
{ timestamp }
</a>
{ this._renderE2EPadlock() }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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