Merge branch 'vector' of github.com:matrix-org/matrix-react-sdk into erikj/room_editing

This commit is contained in:
Erik Johnston 2015-07-20 18:18:04 +01:00
commit 813cf0481e
11 changed files with 199 additions and 49 deletions

View file

@ -51,3 +51,11 @@ limitations under the License.
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.mx_MemberTile_unavailable {
opacity: 0.75;
}
.mx_MemberTile_offline {
opacity: 0.5;
}

View file

@ -46,9 +46,21 @@ module.exports = React.createClass({
var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png"; var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
power = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>; power = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>;
} }
var presenceClass = "mx_MemberTile_offline";
var mainClassName = "mx_MemberTile ";
if (this.props.member.user) {
if (this.props.member.user.presence === "online") {
presenceClass = "mx_MemberTile_online";
}
else if (this.props.member.user.presence === "unavailable") {
presenceClass = "mx_MemberTile_unavailable";
}
}
mainClassName += presenceClass;
return ( return (
<div className="mx_MemberTile" onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave } > <div className={mainClassName} onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }
>
<div className="mx_MemberTile_avatar"> <div className="mx_MemberTile_avatar">
<img className="mx_MemberTile_avatarImg" <img className="mx_MemberTile_avatarImg"
src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 40, 40, "crop") : null } src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 40, 40, "crop") : null }

View file

@ -47,7 +47,7 @@ module.exports = React.createClass({
return Object.keys(self.state.memberDict).map(function(userId) { return Object.keys(self.state.memberDict).map(function(userId) {
var m = self.state.memberDict[userId]; var m = self.state.memberDict[userId];
return ( return (
<MemberTile key={userId} member={m} /> <MemberTile key={userId} member={m} ref={userId} />
); );
}); });
}, },

View file

@ -116,6 +116,10 @@ function _setCallListeners(call) {
_setCallState(call, call.roomId, "busy"); _setCallState(call, call.roomId, "busy");
pause("ringbackAudio"); pause("ringbackAudio");
play("busyAudio"); play("busyAudio");
Modal.createDialog(ErrorDialog, {
title: "Call Timeout",
description: "The remote side failed to pick up."
});
} }
else if (oldState === "invite_sent") { else if (oldState === "invite_sent") {
_setCallState(call, call.roomId, "stop_ringback"); _setCallState(call, call.roomId, "stop_ringback");

View file

@ -22,18 +22,25 @@ var Matrix = require("matrix-js-sdk");
var matrixClient = null; var matrixClient = null;
var localStorage = window.localStorage; var localStorage = window.localStorage;
function createClient(hs_url, is_url, user_id, access_token) {
var opts = {
baseUrl: hs_url,
idBaseUrl: is_url,
accessToken: access_token,
userId: user_id
};
matrixClient = Matrix.createClient(opts);
}
if (localStorage) { if (localStorage) {
var hs_url = localStorage.getItem("mx_hs_url"); var hs_url = localStorage.getItem("mx_hs_url");
var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org'; var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
var access_token = localStorage.getItem("mx_access_token"); var access_token = localStorage.getItem("mx_access_token");
var user_id = localStorage.getItem("mx_user_id"); var user_id = localStorage.getItem("mx_user_id");
if (access_token && user_id && hs_url) { if (access_token && user_id && hs_url) {
matrixClient = Matrix.createClient({ createClient(hs_url, is_url, user_id, access_token);
baseUrl: hs_url,
idBaseUrl: is_url,
accessToken: access_token,
userId: user_id
});
} }
} }
@ -42,8 +49,8 @@ module.exports = {
return matrixClient; return matrixClient;
}, },
replace: function(cli) { unset: function() {
matrixClient = cli; matrixClient = null;
}, },
replaceUsingUrls: function(hs_url, is_url) { replaceUsingUrls: function(hs_url, is_url) {
@ -51,6 +58,23 @@ module.exports = {
baseUrl: hs_url, baseUrl: hs_url,
idBaseUrl: is_url idBaseUrl: is_url
}); });
},
replaceUsingAccessToken: function(hs_url, is_url, user_id, access_token) {
createClient(hs_url, is_url, user_id, access_token);
if (localStorage) {
try {
localStorage.clear();
localStorage.setItem("mx_hs_url", hs_url);
localStorage.setItem("mx_is_url", is_url);
localStorage.setItem("mx_user_id", user_id);
localStorage.setItem("mx_access_token", access_token);
} catch (e) {
console.warn("Error using local storage: can't persist session!");
}
} else {
console.warn("No local storage available: can't persist session!");
}
} }
}; };

105
src/Presence.js Normal file
View file

@ -0,0 +1,105 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
"use strict";
var MatrixClientPeg = require("./MatrixClientPeg");
// Time in ms after that a user is considered as unavailable/away
var UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
var PRESENCE_STATES = ["online", "offline", "unavailable"];
// The current presence state
var state, timer;
module.exports = {
/**
* Start listening the user activity to evaluate his presence state.
* Any state change will be sent to the Home Server.
*/
start: function() {
var self = this;
this.running = true;
if (undefined === state) {
// The user is online if they move the mouse or press a key
document.onmousemove = function() { self._resetTimer(); };
document.onkeypress = function() { self._resetTimer(); };
this._resetTimer();
}
},
/**
* Stop tracking user activity
*/
stop: function() {
this.running = false;
if (timer) {
clearTimeout(timer);
timer = undefined;
}
state = undefined;
},
/**
* Get the current presence state.
* @returns {string} the presence state (see PRESENCE enum)
*/
getState: function() {
return state;
},
/**
* Set the presence state.
* If the state has changed, the Home Server will be notified.
* @param {string} newState the new presence state (see PRESENCE enum)
*/
setState: function(newState) {
if (newState === state) {
return;
}
if (PRESENCE_STATES.indexOf(newState) === -1) {
throw new Error("Bad presence state: " + newState);
}
if (!this.running) {
return;
}
state = newState;
MatrixClientPeg.get().setPresence(state).catch(function(err) {
console.error("Failed to set presence: %s", err);
});
},
/**
* Callback called when the user made no action on the page for UNAVAILABLE_TIME ms.
* @private
*/
_onUnavailableTimerFire: function() {
this.setState("unavailable");
},
/**
* Callback called when the user made an action on the page
* @private
*/
_resetTimer: function() {
var self = this;
this.setState("online");
// Re-arm the timer
clearTimeout(timer);
timer = setTimeout(function() {
self._onUnavailableTimerFire();
}, UNAVAILABLE_TIME_MS);
}
};

View file

@ -106,7 +106,7 @@ module.exports = {
// show the message // show the message
this.element.value = this.data[this.position]; this.element.value = this.data[this.position];
} }
else if (this.originalText) { else if (this.originalText !== undefined) {
// restore the original text the user was typing. // restore the original text the user was typing.
this.element.value = this.originalText; this.element.value = this.originalText;
} }

View file

@ -18,6 +18,9 @@ limitations under the License.
var React = require("react"); var React = require("react");
var MatrixClientPeg = require("../../MatrixClientPeg"); var MatrixClientPeg = require("../../MatrixClientPeg");
var Modal = require("../../Modal");
var ComponentBroker = require('../../ComponentBroker');
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
var INITIAL_LOAD_NUM_MEMBERS = 50; var INITIAL_LOAD_NUM_MEMBERS = 50;
@ -37,6 +40,7 @@ module.exports = {
componentWillUnmount: function() { componentWillUnmount: function() {
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
MatrixClientPeg.get().removeListener("User.presence", this.userPresenceFn);
} }
}, },
@ -48,8 +52,19 @@ module.exports = {
memberDict: self.roomMembers() memberDict: self.roomMembers()
}); });
}, 50); }, 50);
},
// Attach a SINGLE listener for global presence changes then locate the
// member tile and re-render it. This is more efficient than every tile
// evar attaching their own listener.
function updateUserState(event, user) {
var tile = self.refs[user.userId];
if (tile) {
tile.forceUpdate();
}
}
MatrixClientPeg.get().on("User.presence", updateUserState);
this.userPresenceFn = updateUserState;
},
// Remember to set 'key' on a MemberList to the ID of the room it's for // Remember to set 'key' on a MemberList to the ID of the room it's for
/*componentWillReceiveProps: function(newProps) { /*componentWillReceiveProps: function(newProps) {
},*/ },*/
@ -67,6 +82,10 @@ module.exports = {
inputText = inputText.trim(); // react requires es5-shim so we know trim() exists inputText = inputText.trim(); // react requires es5-shim so we know trim() exists
if (inputText[0] !== '@' || inputText.indexOf(":") === -1) { if (inputText[0] !== '@' || inputText.indexOf(":") === -1) {
console.error("Bad user ID to invite: %s", inputText); console.error("Bad user ID to invite: %s", inputText);
Modal.createDialog(ErrorDialog, {
title: "Invite Error",
description: "Malformed user ID. Should look like '@localpart:domain'"
});
return; return;
} }
self.setState({ self.setState({
@ -81,6 +100,10 @@ module.exports = {
}); });
}, function(err) { }, function(err) {
console.error("Failed to invite: %s", JSON.stringify(err)); console.error("Failed to invite: %s", JSON.stringify(err));
Modal.createDialog(ErrorDialog, {
title: "Invite Server Error",
description: err.message
});
self.setState({ self.setState({
inviting: false inviting: false
}); });

View file

@ -21,7 +21,7 @@ var Loader = require("react-loader");
var MatrixClientPeg = require("../../MatrixClientPeg"); var MatrixClientPeg = require("../../MatrixClientPeg");
var RoomListSorter = require("../../RoomListSorter"); var RoomListSorter = require("../../RoomListSorter");
var Presence = require("../../Presence");
var dis = require("../../dispatcher"); var dis = require("../../dispatcher");
var ComponentBroker = require('../../ComponentBroker'); var ComponentBroker = require('../../ComponentBroker');
@ -89,8 +89,9 @@ module.exports = {
window.localStorage.clear(); window.localStorage.clear();
} }
Notifier.stop(); Notifier.stop();
Presence.stop();
MatrixClientPeg.get().removeAllListeners(); MatrixClientPeg.get().removeAllListeners();
MatrixClientPeg.replace(null); MatrixClientPeg.unset();
break; break;
case 'start_registration': case 'start_registration':
if (this.state.logged_in) return; if (this.state.logged_in) return;
@ -187,6 +188,7 @@ module.exports = {
}); });
}); });
Notifier.start(); Notifier.start();
Presence.start();
cli.startClient(); cli.startClient();
}, },

View file

@ -75,27 +75,10 @@ module.exports = {
'user': formVals.username, 'user': formVals.username,
'password': formVals.password 'password': formVals.password
}).done(function(data) { }).done(function(data) {
// XXX: we assume this means we're logged in, but there could be a next stage MatrixClientPeg.replaceUsingAccessToken(
MatrixClientPeg.replace(Matrix.createClient({ self.state.hs_url, self.state.is_url,
baseUrl: self.state.hs_url, data.user_id, data.access_token
idBaseUrl: self.state.is_url, );
userId: data.user_id,
accessToken: data.access_token
}));
var localStorage = window.localStorage;
if (localStorage) {
try {
localStorage.clear();
localStorage.setItem("mx_hs_url", self.state.hs_url);
localStorage.setItem("mx_is_url", self.state.is_url);
localStorage.setItem("mx_user_id", data.user_id);
localStorage.setItem("mx_access_token", data.access_token);
} catch (e) {
console.warn("Error using local storage: can't persist session!");
}
} else {
console.warn("No local storage available: can't persist session!");
}
if (self.props.onLoggedIn) { if (self.props.onLoggedIn) {
self.props.onLoggedIn(); self.props.onLoggedIn();
} }

View file

@ -259,20 +259,9 @@ module.exports = {
}, },
onRegistered: function(user_id, access_token) { onRegistered: function(user_id, access_token) {
MatrixClientPeg.replace(Matrix.createClient({ MatrixClientPeg.replaceUsingAccessToken(
baseUrl: this.state.hs_url, this.state.hs_url, this.state.is_url, user_id, access_token
idBaseUrl: this.state.is_url, );
userId: user_id,
accessToken: access_token
}));
var localStorage = window.localStorage;
if (localStorage) {
localStorage.setItem("mx_hs_url", this.state.hs_url);
localStorage.setItem("mx_user_id", user_id);
localStorage.setItem("mx_access_token", access_token);
} else {
console.warn("No local storage available: can't persist session!");
}
if (this.props.onLoggedIn) { if (this.props.onLoggedIn) {
this.props.onLoggedIn(); this.props.onLoggedIn();
} }