diff --git a/src/component-index.js b/src/component-index.js
index 7ae15ba12c..5fcf8e1ce0 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..0472b1c651
--- /dev/null
+++ b/src/components/views/avatars/BaseAvatar.js
@@ -0,0 +1,131 @@
+/*
+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,
+ idName: React.PropTypes.string, // ID for generating hash colours
+ title: React.PropTypes.string,
+ url: React.PropTypes.string, // highest priority of them all
+ urls: React.PropTypes.array, // [highest_priority, ... , lowest_priority]
+ width: React.PropTypes.number,
+ height: React.PropTypes.number,
+ resizeMethod: React.PropTypes.string,
+ defaultToInitialLetter: React.PropTypes.bool
+ },
+
+ getDefaultProps: function() {
+ return {
+ width: 40,
+ height: 40,
+ resizeMethod: 'crop',
+ defaultToInitialLetter: true
+ }
+ },
+
+ getInitialState: function() {
+ var defaultImageUrl = null;
+ if (this.props.defaultToInitialLetter) {
+ defaultImageUrl = AvatarLogic.defaultAvatarUrlForString(
+ this.props.idName || this.props.name
+ );
+ }
+ return {
+ imageUrl: this.props.url || (this.props.urls ? this.props.urls[0] : null),
+ defaultImageUrl: defaultImageUrl,
+ urlsIndex: 0
+ };
+ },
+
+ componentWillReceiveProps: function(nextProps) {
+ // retry all the urls again, they may have changed.
+ if (this.props.urls && this.state.urlsIndex > 0) {
+ this.setState({
+ urlsIndex: 0,
+ imageUrl: this.props.urls[0]
+ });
+ }
+ },
+
+ onError: function(ev) {
+ var failedUrl = ev.target.src;
+
+ if (this.props.urls) {
+ var nextIndex = this.state.urlsIndex + 1;
+ if (nextIndex < this.props.urls.length) {
+ // try another
+ this.setState({
+ urlsIndex: nextIndex,
+ imageUrl: this.props.urls[nextIndex]
+ });
+ return;
+ }
+ }
+
+ // either no urls array or we've reached the end of it, we may have a default
+ // we can use...
+ if (this.props.defaultToInitialLetter) {
+ if (failedUrl === this.state.defaultImageUrl) {
+ return; // don't tightloop if the browser can't load the default URL
+ }
+ this.setState({ imageUrl: this.state.defaultImageUrl })
+ }
+ },
+
+ _getInitialLetter: function() {
+ var name = this.props.name;
+ var initial = name[0];
+ if (initial === '@' && name[1]) {
+ initial = name[1];
+ }
+ return initial.toUpperCase();
+ },
+
+ render: function() {
+ var name = this.props.name;
+
+ if (this.state.imageUrl === this.state.defaultImageUrl) {
+ var initialLetter = this._getInitialLetter();
+ return (
+
+
+ { initialLetter }
+
+
+
+ );
+ }
+ 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 (
-
- { initialLetter }
-
-
- );
- }
+ var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
return (
-
+
);
}
});