From fec266f1c025e53962b0dd50128a06f915ea94eb Mon Sep 17 00:00:00 2001 From: David Baker <dave@matrix.org> Date: Thu, 13 Aug 2015 19:30:02 +0100 Subject: [PATCH] Move avatars into their own components so I can add functionality like custom default avatars and onerror sources without having to add it in 13 separate places. Add the aforementioned features. --- skins/base/css/atoms/MemberAvatar.css | 22 ++++++ skins/base/css/molecules/MemberTile.css | 6 -- skins/base/views/atoms/MemberAvatar.js | 34 +++++++++ skins/base/views/atoms/RoomAvatar.js | 34 +++++++++ skins/base/views/molecules/EventAsTextTile.js | 3 +- skins/base/views/molecules/MRoomMemberTile.js | 3 +- skins/base/views/molecules/MemberInfo.js | 6 +- skins/base/views/molecules/MemberTile.js | 7 +- skins/base/views/molecules/MessageComposer.js | 5 +- skins/base/views/molecules/MessageTile.js | 3 +- skins/base/views/molecules/RoomHeader.js | 10 ++- skins/base/views/molecules/RoomTile.js | 8 +- .../views/molecules/voip/MCallAnswerTile.js | 2 +- .../views/molecules/voip/MCallHangupTile.js | 2 +- .../views/molecules/voip/MCallInviteTile.js | 2 +- skins/base/views/organisms/Notifier.js | 7 +- src/ComponentBroker.js | 2 + src/DefaultAvatar.js | 38 ++++++++++ src/controllers/atoms/MemberAvatar.js | 76 +++++++++++++++++++ src/controllers/atoms/RoomAvatar.js | 67 ++++++++++++++++ 20 files changed, 314 insertions(+), 23 deletions(-) create mode 100644 skins/base/css/atoms/MemberAvatar.css create mode 100644 skins/base/views/atoms/MemberAvatar.js create mode 100644 skins/base/views/atoms/RoomAvatar.js create mode 100644 src/DefaultAvatar.js create mode 100644 src/controllers/atoms/MemberAvatar.js create mode 100644 src/controllers/atoms/RoomAvatar.js diff --git a/skins/base/css/atoms/MemberAvatar.css b/skins/base/css/atoms/MemberAvatar.css new file mode 100644 index 00000000..6422df79 --- /dev/null +++ b/skins/base/css/atoms/MemberAvatar.css @@ -0,0 +1,22 @@ +/* +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. +*/ + +.mx_MemberAvatar { + z-index: 20; + border-radius: 20px; + background-color: #dbdbdb; +} + diff --git a/skins/base/css/molecules/MemberTile.css b/skins/base/css/molecules/MemberTile.css index 2033b5b0..f296668f 100644 --- a/skins/base/css/molecules/MemberTile.css +++ b/skins/base/css/molecules/MemberTile.css @@ -31,12 +31,6 @@ limitations under the License. position: relative; } -.mx_MemberTile_avatarImg { - z-index: 20; - border-radius: 20px; - background-color: #dbdbdb; -} - .mx_MemberTile_inviteEditing { display: initial ! important; } diff --git a/skins/base/views/atoms/MemberAvatar.js b/skins/base/views/atoms/MemberAvatar.js new file mode 100644 index 00000000..4ef5fea9 --- /dev/null +++ b/skins/base/views/atoms/MemberAvatar.js @@ -0,0 +1,34 @@ +/* +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 React = require('react'); + +var MemberAvatarController = require("../../../../src/controllers/atoms/MemberAvatar"); + +module.exports = React.createClass({ + displayName: 'MemberAvatar', + mixins: [MemberAvatarController], + + render: function() { + return ( + <img className="mx_MemberAvatar" src={this.state.imageUrl} + onerror={this.onError} + width={this.props.width} height={this.props.height} /> + ); + } +}); diff --git a/skins/base/views/atoms/RoomAvatar.js b/skins/base/views/atoms/RoomAvatar.js new file mode 100644 index 00000000..48febbc8 --- /dev/null +++ b/skins/base/views/atoms/RoomAvatar.js @@ -0,0 +1,34 @@ +/* +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 React = require('react'); + +var RoomAvatarController = require("../../../../src/controllers/atoms/RoomAvatar"); + +module.exports = React.createClass({ + displayName: 'RoomAvatar', + mixins: [RoomAvatarController], + + render: function() { + return ( + <img className="mx_RoomAvatar" src={this.state.imageUrl} onerror={this.onError} + width={this.props.width} height={this.props.height} + /> + ); + } +}); diff --git a/skins/base/views/molecules/EventAsTextTile.js b/skins/base/views/molecules/EventAsTextTile.js index f53d83c6..bc585b81 100644 --- a/skins/base/views/molecules/EventAsTextTile.js +++ b/skins/base/views/molecules/EventAsTextTile.js @@ -22,6 +22,7 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var EventAsTextTileController = require("../../../../src/controllers/molecules/EventAsTextTile"); var ComponentBroker = require('../../../../src/ComponentBroker'); var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp'); +var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar'); var TextForEvent = require("../../../../src/TextForEvent"); module.exports = React.createClass({ @@ -34,7 +35,7 @@ module.exports = React.createClass({ return ( <div className="mx_MessageTile mx_MessageTile_notice"> <div className="mx_MessageTile_avatar"> - <img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/> + <MemberAvatar member={this.props.mxEvent.sender} /> </div> { timestamp } <span className="mx_SenderProfile"></span> diff --git a/skins/base/views/molecules/MRoomMemberTile.js b/skins/base/views/molecules/MRoomMemberTile.js index 3ca14283..fb65d375 100644 --- a/skins/base/views/molecules/MRoomMemberTile.js +++ b/skins/base/views/molecules/MRoomMemberTile.js @@ -24,6 +24,7 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var ComponentBroker = require('../../../../src/ComponentBroker'); var TextForEvent = require('../../../../src/TextForEvent'); var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp'); +var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar'); module.exports = React.createClass({ displayName: 'MRoomMemberTile', @@ -41,7 +42,7 @@ module.exports = React.createClass({ return ( <div className="mx_MessageTile mx_MessageTile_notice"> <div className="mx_MessageTile_avatar"> - <img src={ this.props.mxEvent.target ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.target, 40, 40, "crop") : null } width="40" height="40" alt=""/> + <MemberAvatar member={this.props.mxEvent.sender} /> </div> { timestamp } <span className="mx_SenderProfile"></span> diff --git a/skins/base/views/molecules/MemberInfo.js b/skins/base/views/molecules/MemberInfo.js index 3e4dbcd7..e79b99a2 100644 --- a/skins/base/views/molecules/MemberInfo.js +++ b/skins/base/views/molecules/MemberInfo.js @@ -20,6 +20,8 @@ var React = require('react'); var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var MemberInfoController = require("../../../../src/controllers/molecules/MemberInfo"); +var ComponentBroker = require('../../../../src/ComponentBroker'); +var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar'); module.exports = React.createClass({ displayName: 'MemberInfo', @@ -96,9 +98,7 @@ module.exports = React.createClass({ <img className="mx_MemberInfo_chevron" src="img/chevron-right.png" width="9" height="16" /> <div className="mx_MemberInfo_shim"></div> <div className="mx_MemberInfo_avatar"> - <img className="mx_MemberInfo_avatarImg" - src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 128, 128, "crop") : null } - width="128" height="128" alt=""/> + <MemberAvatar member={this.props.member} width={128} height={128} /> </div> <div className="mx_MemberInfo_field">{this.props.member.userId}</div> {opLabel} diff --git a/skins/base/views/molecules/MemberTile.js b/skins/base/views/molecules/MemberTile.js index cf5980b9..bd184051 100644 --- a/skins/base/views/molecules/MemberTile.js +++ b/skins/base/views/molecules/MemberTile.js @@ -24,6 +24,7 @@ var Modal = require("../../../../src/Modal"); var MemberTileController = require("../../../../src/controllers/molecules/MemberTile"); var MemberInfo = ComponentBroker.get('molecules/MemberInfo'); var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog"); +var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar'); // The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them. // Revert to Arial when this happens, which on OSX works at least. @@ -95,10 +96,8 @@ module.exports = React.createClass({ return ( <div className={mainClassName} onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }> <div className="mx_MemberTile_avatar"> - <img className="mx_MemberTile_avatarImg" - src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 40, 40, "crop") : null } - width="40" height="40" alt=""/> - { power } + <MemberAvatar member={this.props.member} /> + { power } </div> { nameEl } </div> diff --git a/skins/base/views/molecules/MessageComposer.js b/skins/base/views/molecules/MessageComposer.js index 639c5bff..a8d8a4eb 100644 --- a/skins/base/views/molecules/MessageComposer.js +++ b/skins/base/views/molecules/MessageComposer.js @@ -22,6 +22,9 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var MessageComposerController = require("../../../../src/controllers/molecules/MessageComposer"); var ContentMessages = require("../../../../src/ContentMessages"); +var ComponentBroker = require('../../../../src/ComponentBroker'); +var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar'); + module.exports = React.createClass({ displayName: 'MessageComposer', mixins: [MessageComposerController], @@ -47,7 +50,7 @@ module.exports = React.createClass({ <div className="mx_MessageComposer_wrapper"> <div className="mx_MessageComposer_row"> <div className="mx_MessageComposer_avatar"> - <img src={ MatrixClientPeg.get().getAvatarUrlForMember(me, 40, 40, "crop") } width="40" height="40" alt=""/> + <MemberAvatar member={me} /> </div> <div className="mx_MessageComposer_input"> <textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message" /> diff --git a/skins/base/views/molecules/MessageTile.js b/skins/base/views/molecules/MessageTile.js index b9a57079..d6b8713f 100644 --- a/skins/base/views/molecules/MessageTile.js +++ b/skins/base/views/molecules/MessageTile.js @@ -25,6 +25,7 @@ var ComponentBroker = require('../../../../src/ComponentBroker'); var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp'); var SenderProfile = ComponentBroker.get('molecules/SenderProfile'); +var MemberAvatar = ComponentBroker.get('atoms/MemberAvatar'); var UnknownMessageTile = ComponentBroker.get('molecules/UnknownMessageTile'); @@ -64,7 +65,7 @@ module.exports = React.createClass({ if (!this.props.continuation) { avatar = ( <div className="mx_MessageTile_avatar"> - <img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/> + <MemberAvatar member={this.props.mxEvent.sender} /> </div> ); sender = <SenderProfile mxEvent={this.props.mxEvent} />; diff --git a/skins/base/views/molecules/RoomHeader.js b/skins/base/views/molecules/RoomHeader.js index 8e0cadc2..e3003b88 100644 --- a/skins/base/views/molecules/RoomHeader.js +++ b/skins/base/views/molecules/RoomHeader.js @@ -22,6 +22,7 @@ var ComponentBroker = require('../../../../src/ComponentBroker'); var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var RoomHeaderController = require("../../../../src/controllers/molecules/RoomHeader"); var EditableText = ComponentBroker.get("atoms/EditableText"); +var RoomAvatar = ComponentBroker.get('atoms/RoomAvatar'); module.exports = React.createClass({ displayName: 'RoomHeader', @@ -93,11 +94,18 @@ module.exports = React.createClass({ ); } + var roomAvatar = null; + if (this.props.room) { + roomAvatar = ( + <RoomAvatar room={this.props.room} /> + ); + } + header = <div className="mx_RoomHeader_wrapper"> <div className="mx_RoomHeader_leftRow"> <div className="mx_RoomHeader_avatar"> - <img src={ MatrixClientPeg.get().getAvatarUrlForRoom(this.props.room, 48, 48, "crop") } width="48" height="48" alt=""/> + { roomAvatar } </div> <div className="mx_RoomHeader_info"> { name } diff --git a/skins/base/views/molecules/RoomTile.js b/skins/base/views/molecules/RoomTile.js index 6b80fc8a..b8e41fb8 100644 --- a/skins/base/views/molecules/RoomTile.js +++ b/skins/base/views/molecules/RoomTile.js @@ -23,6 +23,9 @@ var RoomTileController = require("../../../../src/controllers/molecules/RoomTile var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); +var ComponentBroker = require('../../../../src/ComponentBroker'); +var RoomAvatar = ComponentBroker.get('atoms/RoomAvatar'); + module.exports = React.createClass({ displayName: 'RoomTile', mixins: [RoomTileController], @@ -57,7 +60,10 @@ module.exports = React.createClass({ */ return ( <div className={classes} onClick={this.onClick}> - <div className="mx_RoomTile_avatar"><img src={ MatrixClientPeg.get().getAvatarUrlForRoom(this.props.room, 40, 40, "crop") } width="40" height="40" alt=""/>{ badge }</div> + <div className="mx_RoomTile_avatar"> + <RoomAvatar room={this.props.room} /> + { badge } + </div> <div className="mx_RoomTile_name">{name}</div> </div> ); diff --git a/skins/base/views/molecules/voip/MCallAnswerTile.js b/skins/base/views/molecules/voip/MCallAnswerTile.js index b7ea3cad..0dcce825 100644 --- a/skins/base/views/molecules/voip/MCallAnswerTile.js +++ b/skins/base/views/molecules/voip/MCallAnswerTile.js @@ -36,7 +36,7 @@ module.exports = React.createClass({ return ( <div className="mx_MessageTile mx_MessageTile_notice"> <div className="mx_MessageTile_avatar"> - <img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/> + <MemberAvatar member={this.props.mxEvent.sender} /> </div> <MessageTimestamp ts={this.props.mxEvent.getTs()} /> <span className="mx_SenderProfile"></span> diff --git a/skins/base/views/molecules/voip/MCallHangupTile.js b/skins/base/views/molecules/voip/MCallHangupTile.js index 261bd8d1..94308f9c 100644 --- a/skins/base/views/molecules/voip/MCallHangupTile.js +++ b/skins/base/views/molecules/voip/MCallHangupTile.js @@ -36,7 +36,7 @@ module.exports = React.createClass({ return ( <div className="mx_MessageTile mx_MessageTile_notice"> <div className="mx_MessageTile_avatar"> - <img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/> + <MemberAvatar member={this.props.mxEvent.sender} /> </div> <MessageTimestamp ts={this.props.mxEvent.getTs()} /> <span className="mx_SenderProfile"></span> diff --git a/skins/base/views/molecules/voip/MCallInviteTile.js b/skins/base/views/molecules/voip/MCallInviteTile.js index ed73553b..1dc08f1d 100644 --- a/skins/base/views/molecules/voip/MCallInviteTile.js +++ b/skins/base/views/molecules/voip/MCallInviteTile.js @@ -42,7 +42,7 @@ module.exports = React.createClass({ return ( <div className="mx_MessageTile mx_MessageTile_notice"> <div className="mx_MessageTile_avatar"> - <img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/> + <MemberAvatar member={this.props.mxEvent.sender} /> </div> <MessageTimestamp ts={this.props.mxEvent.getTs()} /> <span className="mx_SenderProfile"></span> diff --git a/skins/base/views/organisms/Notifier.js b/skins/base/views/organisms/Notifier.js index 7df76e29..8dcb03da 100644 --- a/skins/base/views/organisms/Notifier.js +++ b/skins/base/views/organisms/Notifier.js @@ -23,6 +23,9 @@ var TextForEvent = require("../../../../src/TextForEvent"); var extend = require("../../../../src/extend"); var dis = require("../../../../src/dispatcher"); +var ComponentBroker = require("../../../../src/ComponentBroker"); +var MemberAvatar = ComponentBroker.get("atoms/MemberAvatar"); + var NotifierView = { notificationMessageForEvent: function(ev) { @@ -57,11 +60,13 @@ var NotifierView = { if (ev.getContent().body) msg = ev.getContent().body; } + var avatarUrlrl = MemberAvatar.avatarUrlForMember(ev.sender); + var notification = new global.Notification( title, { "body": msg, - "icon": MatrixClientPeg.get().getAvatarUrlForMember(ev.sender) + "icon": avatarUrl } ); diff --git a/src/ComponentBroker.js b/src/ComponentBroker.js index 2476ff6a..a869b431 100644 --- a/src/ComponentBroker.js +++ b/src/ComponentBroker.js @@ -103,6 +103,8 @@ require('../skins/base/views/molecules/RoomDropTarget'); require('../skins/base/views/molecules/BottomLeftMenu'); require('../skins/base/views/molecules/DateSeparator'); require('../skins/base/views/atoms/voip/VideoFeed'); +require('../skins/base/views/atoms/MemberAvatar'); +require('../skins/base/views/atoms/RoomAvatar'); require('../skins/base/views/atoms/ImageView'); require('../skins/base/views/molecules/voip/VideoView'); require('../skins/base/views/molecules/voip/CallView'); diff --git a/src/DefaultAvatar.js b/src/DefaultAvatar.js new file mode 100644 index 00000000..5183d33b --- /dev/null +++ b/src/DefaultAvatar.js @@ -0,0 +1,38 @@ +/* +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'; + +module.exports = { + defaultAvatarUrlForString: function(s) { + var total = 0; + for (var i = 0; i < s.length; ++i) { + total += s.charCodeAt(i); + } + switch (total % 3) { + case 0: + return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9QjNbxSKP4eagAFnTseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAABSwCRWJw31gAAAAASUVORK5CYII="; + break; + case 1: + return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9chOaxgCP4eagAFk9seHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAtKMCks/JG8MAAAAASUVORK5CYII="; + break; + case 2: + return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9YzNayQCP4eagADldseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAyiACeHwPiu4AAAAASUVORK5CYII="; + break; + } + } +} + diff --git a/src/controllers/atoms/MemberAvatar.js b/src/controllers/atoms/MemberAvatar.js new file mode 100644 index 00000000..d2f4aa45 --- /dev/null +++ b/src/controllers/atoms/MemberAvatar.js @@ -0,0 +1,76 @@ +/* +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'); +var DefaultAvatar = require('../../DefaultAvatar'); + +var React = require('react'); + +module.exports = { + propTypes: { + member: React.PropTypes.object.isRequired, + width: React.PropTypes.number, + height: React.PropTypes.number, + resizeMethod: React.PropTypes.string, + }, + + getDefaultProps: function() { + return { + width: 40, + height: 40, + resizeMethod: 'crop' + } + }, + + // takes member as an arg so it can be used if the + // avatars are required outsode of components + // (eg. in html5 desktop notifs) + avatarUrlForMember(member) { + var url = MatrixClientPeg.get().getAvatarUrlForMember( + member, + this.props.width, this.props.height, this.props.resizeMethod, + false + ); + if (url === null) { + url = this.defaultAvatarUrl(member); + } + return url; + }, + + defaultAvatarUrl: function(member) { + return DefaultAvatar.defaultAvatarUrlForString( + member.userId + ); + }, + + onError: function(ev) { + // don't tightloop if the browser can't load a data url + if (ev.target.src == this.defaultAvatarUrl()) { + return; + } + this.setState({ + imageUrl: this.defaultAvatarUrl() + }); + }, + + getInitialState: function() { + return { + imageUrl: this.avatarUrlForMember(this.props.member) + }; + } +}; diff --git a/src/controllers/atoms/RoomAvatar.js b/src/controllers/atoms/RoomAvatar.js new file mode 100644 index 00000000..088d4694 --- /dev/null +++ b/src/controllers/atoms/RoomAvatar.js @@ -0,0 +1,67 @@ +/* +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'); +var DefaultAvatar = require('../../DefaultAvatar'); + +module.exports = { + getDefaultProps: function() { + return { + width: 40, + height: 40, + resizeMethod: 'crop' + } + }, + + // takes member as an arg so it can be used if the + // avatars are required outsode of components + // (eg. in html5 desktop notifs, although this is not) + avatarUrlForRoom(room) { + var url = MatrixClientPeg.get().getAvatarUrlForRoom( + room, + this.props.width, this.props.height, this.props.resizeMethod, + false + ); + if (url === null) { + url = this.defaultAvatarUrl(room); + } + return url; + }, + + defaultAvatarUrl: function(room) { + return DefaultAvatar.defaultAvatarUrlForString( + this.props.room.roomId + ); + }, + + onError: function(ev) { + // don't tightloop if the browser can't load a data url + if (ev.target.src == this.defaultAvatarUrl()) { + return; + } + this.setState({ + imageUrl: this.defaultAvatarUrl() + }); + }, + + getInitialState: function() { + return { + imageUrl: this.avatarUrlForRoom(this.props.room) + }; + } +};