Merge branch 'develop' into wmwragg/direct-chat-sublist
This commit is contained in:
commit
6d1f9003e2
24 changed files with 593 additions and 195 deletions
|
@ -58,6 +58,10 @@ module.exports = React.createClass({
|
|||
|
||||
// called when the session load completes
|
||||
onLoadCompleted: React.PropTypes.func,
|
||||
|
||||
// displayname, if any, to set on the device when logging
|
||||
// in/registering.
|
||||
defaultDeviceDisplayName: React.PropTypes.string,
|
||||
},
|
||||
|
||||
PageTypes: {
|
||||
|
@ -185,6 +189,7 @@ module.exports = React.createClass({
|
|||
enableGuest: this.props.enableGuest,
|
||||
guestHsUrl: this.getCurrentHsUrl(),
|
||||
guestIsUrl: this.getCurrentIsUrl(),
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
}).done(()=>{
|
||||
// stuff this through the dispatcher so that it happens
|
||||
// after the on_logged_in action.
|
||||
|
@ -193,7 +198,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._stopMatrixClient();
|
||||
Lifecycle.stopMatrixClient();
|
||||
dis.unregister(this.dispatcherRef);
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("focus", this.onFocus);
|
||||
|
@ -601,16 +606,6 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
// stop all the background processes related to the current client
|
||||
_stopMatrixClient: function() {
|
||||
Notifier.stop();
|
||||
UserActivity.stop();
|
||||
Presence.stop();
|
||||
MatrixClientPeg.get().stopClient();
|
||||
MatrixClientPeg.get().removeAllListeners();
|
||||
MatrixClientPeg.unset();
|
||||
},
|
||||
|
||||
onKeyDown: function(ev) {
|
||||
/*
|
||||
// Remove this for now as ctrl+alt = alt-gr so this breaks keyboards which rely on alt-gr for numbers
|
||||
|
@ -935,10 +930,8 @@ module.exports = React.createClass({
|
|||
var NewVersionBar = sdk.getComponent('globals.NewVersionBar');
|
||||
var ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
||||
|
||||
// work out the HS URL prompts we should show for
|
||||
|
||||
console.log("rendering; loading="+this.state.loading+"; screen="+this.state.screen +
|
||||
"; logged_in="+this.state.logged_in+"; ready="+this.state.ready);
|
||||
// console.log("rendering; loading="+this.state.loading+"; screen="+this.state.screen +
|
||||
// "; logged_in="+this.state.logged_in+"; ready="+this.state.ready);
|
||||
|
||||
if (this.state.loading) {
|
||||
var Spinner = sdk.getComponent('elements.Spinner');
|
||||
|
@ -1051,6 +1044,7 @@ module.exports = React.createClass({
|
|||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
registrationUrl={this.props.registrationUrl}
|
||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||
onLoggedIn={this.onRegistered}
|
||||
onLoginClick={this.onLoginClick}
|
||||
onRegisterClick={this.onRegisterClick}
|
||||
|
@ -1077,6 +1071,7 @@ module.exports = React.createClass({
|
|||
customHsUrl={this.getCurrentHsUrl()}
|
||||
customIsUrl={this.getCurrentIsUrl()}
|
||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||
onForgotPasswordClick={this.onForgotPasswordClick}
|
||||
enableGuest={this.props.enableGuest}
|
||||
onCancelClick={this.guestCreds ? this.onReturnToGuestClick : null}
|
||||
|
|
|
@ -44,6 +44,8 @@ module.exports = React.createClass({
|
|||
// different home server without confusing users.
|
||||
fallbackHsUrl: React.PropTypes.string,
|
||||
|
||||
defaultDeviceDisplayName: React.PropTypes.string,
|
||||
|
||||
// login shouldn't know or care how registration is done.
|
||||
onRegisterClick: React.PropTypes.func.isRequired,
|
||||
|
||||
|
@ -136,7 +138,9 @@ module.exports = React.createClass({
|
|||
|
||||
var fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
|
||||
|
||||
var loginLogic = new Signup.Login(hsUrl, isUrl, fallbackHsUrl);
|
||||
var loginLogic = new Signup.Login(hsUrl, isUrl, fallbackHsUrl, {
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
});
|
||||
this._loginLogic = loginLogic;
|
||||
|
||||
loginLogic.getFlows().then(function(flows) {
|
||||
|
|
|
@ -45,6 +45,9 @@ module.exports = React.createClass({
|
|||
email: React.PropTypes.string,
|
||||
username: React.PropTypes.string,
|
||||
guestAccessToken: React.PropTypes.string,
|
||||
|
||||
defaultDeviceDisplayName: React.PropTypes.string,
|
||||
|
||||
// registration shouldn't know or care how login is done.
|
||||
onLoginClick: React.PropTypes.func.isRequired,
|
||||
onCancelClick: React.PropTypes.func
|
||||
|
@ -71,7 +74,9 @@ module.exports = React.createClass({
|
|||
this.dispatcherRef = dis.register(this.onAction);
|
||||
// attach this to the instance rather than this.state since it isn't UI
|
||||
this.registerLogic = new Signup.Register(
|
||||
this.props.customHsUrl, this.props.customIsUrl
|
||||
this.props.customHsUrl, this.props.customIsUrl, {
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
}
|
||||
);
|
||||
this.registerLogic.setClientSecret(this.props.clientSecret);
|
||||
this.registerLogic.setSessionId(this.props.sessionId);
|
||||
|
@ -154,6 +159,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
self.props.onLoggedIn({
|
||||
userId: response.user_id,
|
||||
deviceId: response.device_id,
|
||||
homeserverUrl: self.registerLogic.getHomeserverUrl(),
|
||||
identityServerUrl: self.registerLogic.getIdentityServerUrl(),
|
||||
accessToken: response.access_token
|
||||
|
@ -279,7 +285,7 @@ module.exports = React.createClass({
|
|||
|
||||
var returnToAppJsx;
|
||||
if (this.props.onCancelClick) {
|
||||
returnToAppJsx =
|
||||
returnToAppJsx =
|
||||
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
|
||||
Return to app
|
||||
</a>
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<span className="mx_MFileBody">
|
||||
<div className="mx_MImageBody_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
|
||||
<TintableSvg src="img/download.svg" width="12" height="14"/>
|
||||
Download {text}
|
||||
</a>
|
||||
|
|
|
@ -134,7 +134,7 @@ module.exports = React.createClass({
|
|||
onMouseLeave={this.onImageLeave} />
|
||||
</a>
|
||||
<div className="mx_MImageBody_download">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank" rel="noopener">
|
||||
<TintableSvg src="img/download.svg" width="12" height="14"/>
|
||||
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
|
||||
</a>
|
||||
|
|
|
@ -123,7 +123,7 @@ 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">{ 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"] }
|
||||
|
|
|
@ -67,6 +67,11 @@ module.exports = React.createClass({
|
|||
componentWillMount: function() {
|
||||
this._cancelDeviceList = null;
|
||||
|
||||
// only display the devices list if our client supports E2E *and* the
|
||||
// feature is enabled in the user settings
|
||||
this._enableDevices = MatrixClientPeg.get().isCryptoEnabled() &&
|
||||
UserSettingsStore.isFeatureEnabled("e2e_encryption");
|
||||
|
||||
this.setState({
|
||||
existingOneToOneRoomId: this.getExistingOneToOneRoomId()
|
||||
});
|
||||
|
@ -147,6 +152,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onDeviceVerificationChanged: function(userId, device) {
|
||||
if (!this._enableDevices) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userId == this.props.member.userId) {
|
||||
// no need to re-download the whole thing; just update our copy of
|
||||
// the list.
|
||||
|
@ -170,6 +179,10 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_downloadDeviceList: function(member) {
|
||||
if (!this._enableDevices) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cancelled = false;
|
||||
this._cancelDeviceList = function() { cancelled = true; }
|
||||
|
||||
|
@ -532,7 +545,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_renderDevices: function() {
|
||||
if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) {
|
||||
if (!this._enableDevices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_doInvite(address) {
|
||||
Invite.inviteToRoom(self.props.roomId, address).catch((err) => {
|
||||
Invite.inviteToRoom(this.props.roomId, address).catch((err) => {
|
||||
if (err !== null) {
|
||||
console.error("Failed to invite: %s", JSON.stringify(err));
|
||||
if (err.errcode == 'M_FORBIDDEN') {
|
||||
|
@ -196,7 +196,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
self.setState({
|
||||
this.setState({
|
||||
inviting: false
|
||||
});
|
||||
// XXX: hacky focus on the invite box
|
||||
|
@ -207,7 +207,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
}, 0);
|
||||
}).done();
|
||||
self.setState({
|
||||
this.setState({
|
||||
inviting: true
|
||||
});
|
||||
},
|
||||
|
@ -283,7 +283,7 @@ module.exports = React.createClass({
|
|||
if (inputs.length == 1) {
|
||||
// for a single address, we just send the invite
|
||||
promise.done(() => {
|
||||
this.doInvite(inputs[0]);
|
||||
this._doInvite(inputs[0]);
|
||||
});
|
||||
} else {
|
||||
// if there are several, display the confirmation/progress dialog
|
||||
|
|
|
@ -47,16 +47,6 @@ module.exports = React.createClass({
|
|||
tags[tagName] = ['yep'];
|
||||
});
|
||||
|
||||
var areNotifsMuted = false;
|
||||
if (!MatrixClientPeg.get().isGuest()) {
|
||||
var roomPushRule = MatrixClientPeg.get().getRoomPushRule("global", this.props.room.roomId);
|
||||
if (roomPushRule) {
|
||||
if (0 <= roomPushRule.actions.indexOf("dont_notify")) {
|
||||
areNotifsMuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: this._yankValueFromEvent("m.room.name", "name"),
|
||||
topic: this._yankValueFromEvent("m.room.topic", "topic"),
|
||||
|
@ -66,7 +56,6 @@ module.exports = React.createClass({
|
|||
power_levels_changed: false,
|
||||
tags_changed: false,
|
||||
tags: tags,
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
// isRoomPublished is loaded async in componentWillMount so when the component
|
||||
// inits, the saved value will always be undefined, however getInitialState()
|
||||
// is also called from the saving code so we must return the correct value here
|
||||
|
@ -188,12 +177,6 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
|
||||
if (this.state.areNotifsMuted !== originalState.areNotifsMuted) {
|
||||
promises.push(MatrixClientPeg.get().setRoomMutePushRule(
|
||||
"global", roomId, this.state.areNotifsMuted
|
||||
));
|
||||
}
|
||||
|
||||
// power levels
|
||||
var powerLevels = this._getPowerLevels();
|
||||
if (powerLevels) {
|
||||
|
@ -647,12 +630,6 @@ module.exports = React.createClass({
|
|||
{ tagsSection }
|
||||
|
||||
<div className="mx_RoomSettings_toggles">
|
||||
<label>
|
||||
<input type="checkbox" disabled={ cli.isGuest() }
|
||||
onChange={this._onToggle.bind(this, "areNotifsMuted", true, false)}
|
||||
defaultChecked={this.state.areNotifsMuted}/>
|
||||
'Mention only' notifications for this room
|
||||
</label>
|
||||
<div className="mx_RoomSettings_settings">
|
||||
<h3>Who can access this room?</h3>
|
||||
{ inviteGuestWarning }
|
||||
|
|
|
@ -22,6 +22,7 @@ var dis = require("../../../dispatcher");
|
|||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
var sdk = require('../../../index');
|
||||
var ContextualMenu = require('../../structures/ContextualMenu');
|
||||
var RoomNotifs = require('../../../RoomNotifs');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTile',
|
||||
|
@ -43,43 +44,41 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var areNotifsMuted = false;
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (!cli.isGuest()) {
|
||||
var roomPushRule = cli.getRoomPushRule("global", this.props.room.roomId);
|
||||
if (roomPushRule) {
|
||||
if (0 <= roomPushRule.actions.indexOf("dont_notify")) {
|
||||
areNotifsMuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return({
|
||||
hover : false,
|
||||
badgeHover : false,
|
||||
notificationTagMenu: false,
|
||||
roomTagMenu: false,
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
});
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
switch (payload.action) {
|
||||
case 'notification_change':
|
||||
// Is the notification about this room?
|
||||
if (payload.roomId === this.props.room.roomId) {
|
||||
this.setState( { areNotifsMuted : payload.areNotifsMuted });
|
||||
}
|
||||
break;
|
||||
_shouldShowNotifBadge: function() {
|
||||
const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
|
||||
return showBadgeInStates.indexOf(this.state.notifState) > -1;
|
||||
},
|
||||
|
||||
_shouldShowMentionBadge: function() {
|
||||
return this.state.notifState != RoomNotifs.MUTE;
|
||||
},
|
||||
|
||||
onAccountData: function(accountDataEvent) {
|
||||
if (accountDataEvent.getType() == 'm.push_rules') {
|
||||
this.setState({
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
componentWillMount: function() {
|
||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
|
@ -179,15 +178,19 @@ module.exports = React.createClass({
|
|||
var 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({
|
||||
'mx_RoomTile': true,
|
||||
'mx_RoomTile_selected': this.props.selected,
|
||||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_unreadNotify': notificationCount > 0 && !this.state.areNotifsMuted,
|
||||
'mx_RoomTile_highlight': this.props.highlight,
|
||||
'mx_RoomTile_unreadNotify': notifBadges,
|
||||
'mx_RoomTile_highlight': mentionBadges,
|
||||
'mx_RoomTile_invited': (me && me.membership == 'invite'),
|
||||
'mx_RoomTile_notificationTagMenu': this.state.notificationTagMenu,
|
||||
'mx_RoomTile_noBadges': !(this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted))
|
||||
'mx_RoomTile_noBadges': !badges,
|
||||
});
|
||||
|
||||
var avatarClasses = classNames({
|
||||
|
@ -214,7 +217,7 @@ module.exports = React.createClass({
|
|||
|
||||
if (this.state.badgeHover || this.state.notificationTagMenu) {
|
||||
badgeContent = "\u00B7\u00B7\u00B7";
|
||||
} else if (this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted)) {
|
||||
} else if (badges) {
|
||||
var limitedCount = (notificationCount > 99) ? '99+' : notificationCount;
|
||||
badgeContent = notificationCount ? limitedCount : '!';
|
||||
} else {
|
||||
|
@ -230,7 +233,7 @@ module.exports = React.createClass({
|
|||
var nameClasses = classNames({
|
||||
'mx_RoomTile_name': true,
|
||||
'mx_RoomTile_invite': this.props.isInvite,
|
||||
'mx_RoomTile_badgeShown': this.props.highlight || (notificationCount > 0 && !this.state.areNotifsMuted) || this.state.badgeHover || this.state.notificationTagMenu,
|
||||
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.notificationTagMenu,
|
||||
});
|
||||
|
||||
if (this.props.selected) {
|
||||
|
|
|
@ -52,7 +52,7 @@ export default class DevicesPanel extends React.Component {
|
|||
(error) => {
|
||||
if (this._unmounted) { return; }
|
||||
var errtxt;
|
||||
if (err.httpStatus == 404) {
|
||||
if (error.httpStatus == 404) {
|
||||
// 404 probably means the HS doesn't yet support the API.
|
||||
errtxt = "Your home server does not support device management.";
|
||||
} else {
|
||||
|
@ -127,6 +127,7 @@ export default class DevicesPanel extends React.Component {
|
|||
return (
|
||||
<div className={classes}>
|
||||
<div className="mx_DevicesPanel_header">
|
||||
<div className="mx_DevicesPanel_deviceId">ID</div>
|
||||
<div className="mx_DevicesPanel_deviceName">Name</div>
|
||||
<div className="mx_DevicesPanel_deviceLastSeen">Last seen</div>
|
||||
<div className="mx_DevicesPanel_deviceButtons"></div>
|
||||
|
|
|
@ -109,6 +109,9 @@ export default class DevicesPanelEntry extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="mx_DevicesPanel_device">
|
||||
<div className="mx_DevicesPanel_deviceId">
|
||||
{device.device_id}
|
||||
</div>
|
||||
<div className="mx_DevicesPanel_deviceName">
|
||||
<EditableTextContainer initialValue={device.display_name}
|
||||
onSubmit={this._onDisplayNameChanged}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue