From cfb81a4aecdc6ae20866a7cd0e7ff1a5e77626ad Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 15 Jan 2016 12:02:28 +0000 Subject: [PATCH] Factor out avatar stuff to BaseAvatar. Make MemberAvatar use it instead. --- src/component-index.js | 1 + src/components/views/avatars/BaseAvatar.js | 131 +++++++++++++++++++ src/components/views/avatars/MemberAvatar.js | 86 +++--------- 3 files changed, 148 insertions(+), 70 deletions(-) create mode 100644 src/components/views/avatars/BaseAvatar.js 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 ( + + + + + ); + } + 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 ( - + ); } });