diff --git a/src/component-index.js b/src/component-index.js
index f75dfa5d67..b391016fcd 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -32,6 +32,7 @@ module.exports.components['structures.RoomView'] = require('./components/structu
module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel');
module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar');
module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings');
+module.exports.components['views.avatars.BaseAvatar'] = require('./components/views/avatars/BaseAvatar');
module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar');
module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar');
module.exports.components['views.create_room.CreateRoomButton'] = require('./components/views/create_room/CreateRoomButton');
diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js
new file mode 100644
index 0000000000..2a7dcc1c01
--- /dev/null
+++ b/src/components/views/avatars/BaseAvatar.js
@@ -0,0 +1,140 @@
+/*
+Copyright 2015, 2016 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 React = require('react');
+var AvatarLogic = require("../../../Avatar");
+
+module.exports = React.createClass({
+ displayName: 'BaseAvatar',
+
+ propTypes: {
+ name: React.PropTypes.string.isRequired, // The name (first initial used as default)
+ idName: React.PropTypes.string, // ID for generating hash colours
+ title: React.PropTypes.string, // onHover title text
+ url: React.PropTypes.string, // highest priority of them all, shortcut to set in urls[0]
+ urls: React.PropTypes.array, // [highest_priority, ... , lowest_priority]
+ width: React.PropTypes.number,
+ height: React.PropTypes.number,
+ resizeMethod: React.PropTypes.string,
+ defaultToInitialLetter: React.PropTypes.bool // true to add default url
+ },
+
+ getDefaultProps: function() {
+ return {
+ width: 40,
+ height: 40,
+ resizeMethod: 'crop',
+ defaultToInitialLetter: true
+ }
+ },
+
+ getInitialState: function() {
+ return this._getState(this.props);
+ },
+
+ componentWillReceiveProps: function(nextProps) {
+ // work out if we need to call setState (if the image URLs array has changed)
+ var newState = this._getState(nextProps);
+ var newImageUrls = newState.imageUrls;
+ var oldImageUrls = this.state.imageUrls;
+ if (newImageUrls.length !== oldImageUrls.length) {
+ this.setState(newState); // detected a new entry
+ }
+ else {
+ // check each one to see if they are the same
+ for (var i = 0; i < newImageUrls.length; i++) {
+ if (oldImageUrls[i] !== newImageUrls[i]) {
+ this.setState(newState); // detected a diff
+ break;
+ }
+ }
+ }
+ },
+
+ _getState: function(props) {
+ // work out the full set of urls to try to load. This is formed like so:
+ // imageUrls: [ props.url, props.urls, default image ]
+
+ var urls = props.urls || [];
+ if (props.url) {
+ urls.unshift(props.url); // put in urls[0]
+ }
+
+ var defaultImageUrl = null;
+ if (props.defaultToInitialLetter) {
+ defaultImageUrl = AvatarLogic.defaultAvatarUrlForString(
+ props.idName || props.name
+ );
+ urls.push(defaultImageUrl); // lowest priority
+ }
+ return {
+ imageUrls: urls,
+ defaultImageUrl: defaultImageUrl,
+ urlsIndex: 0
+ };
+ },
+
+ onError: function(ev) {
+ var nextIndex = this.state.urlsIndex + 1;
+ if (nextIndex < this.state.imageUrls.length) {
+ // try the next one
+ this.setState({
+ urlsIndex: nextIndex
+ });
+ }
+ },
+
+ _getInitialLetter: function() {
+ var name = this.props.name;
+ var initial = name[0];
+ if ((initial === '@' || initial === '#') && name[1]) {
+ initial = name[1];
+ }
+ return initial.toUpperCase();
+ },
+
+ render: function() {
+ var name = this.props.name;
+
+ var imageUrl = this.state.imageUrls[this.state.urlsIndex];
+
+ if (imageUrl === this.state.defaultImageUrl) {
+ var initialLetter = this._getInitialLetter();
+ return (
+
+
+ );
+ }
+ return (
+
+ );
+ }
+});
diff --git a/src/components/views/avatars/MemberAvatar.js b/src/components/views/avatars/MemberAvatar.js
index f209006b1c..5e2dbbb23a 100644
--- a/src/components/views/avatars/MemberAvatar.js
+++ b/src/components/views/avatars/MemberAvatar.js
@@ -18,22 +18,16 @@ limitations under the License.
var React = require('react');
var Avatar = require('../../../Avatar');
-var MatrixClientPeg = require('../../../MatrixClientPeg');
+var sdk = require("../../../index");
module.exports = React.createClass({
displayName: 'MemberAvatar',
propTypes: {
- member: React.PropTypes.object,
+ member: React.PropTypes.object.isRequired,
width: React.PropTypes.number,
height: React.PropTypes.number,
- resizeMethod: React.PropTypes.string,
- /**
- * The custom display name to use for this member. This can serve as a
- * drop in replacement for RoomMember objects, or as a clobber name on
- * an existing RoomMember. Used for 3pid invites.
- */
- customDisplayName: React.PropTypes.string
+ resizeMethod: React.PropTypes.string
},
getDefaultProps: function() {
@@ -45,77 +39,29 @@ module.exports = React.createClass({
},
getInitialState: function() {
- var defaultImageUrl = Avatar.defaultAvatarUrlForString(
- this.props.customDisplayName || this.props.member.userId
- )
- return {
- imageUrl: this._getMemberImageUrl() || defaultImageUrl,
- defaultImageUrl: defaultImageUrl
- };
+ return this._getState(this.props);
},
componentWillReceiveProps: function(nextProps) {
- this.refreshUrl();
+ this.setState(this._getState(nextProps));
},
- onError: function(ev) {
- // don't tightloop if the browser can't load a data url
- if (ev.target.src == this.state.defaultImageUrl) {
- return;
- }
- this.setState({
- imageUrl: this.state.defaultImageUrl
- });
- },
-
- _getMemberImageUrl: function() {
- if (!this.props.member) { return null; }
-
- return Avatar.avatarUrlForMember(this.props.member,
- this.props.width,
- this.props.height,
- this.props.resizeMethod);
- },
-
- _getInitialLetter: function() {
- var name = this.props.customDisplayName || this.props.member.name;
- var initial = name[0];
- if (initial === '@' && name[1]) {
- initial = name[1];
- }
- return initial.toUpperCase();
- },
-
- refreshUrl: function() {
- var newUrl = this._getMemberImageUrl();
- if (newUrl != this.currentUrl) {
- this.currentUrl = newUrl;
- this.setState({imageUrl: newUrl});
+ _getState: function(props) {
+ return {
+ name: props.member.name,
+ title: props.member.userId,
+ imageUrl: Avatar.avatarUrlForMember(props.member,
+ props.width,
+ props.height,
+ props.resizeMethod)
}
},
render: function() {
- var name = this.props.customDisplayName || this.props.member.name;
-
- if (this.state.imageUrl === this.state.defaultImageUrl) {
- var initialLetter = this._getInitialLetter();
- return (
-
+
+
-
- );
- }
+ var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
return (
-
+
-
-