diff --git a/src/component-index.js b/src/component-index.js index 0cb7e257a0..967cc5d685 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -64,11 +64,11 @@ module.exports.components['views.login.LoginHeader'] = require('./components/vie module.exports.components['views.login.PasswordLogin'] = require('./components/views/login/PasswordLogin'); module.exports.components['views.login.RegistrationForm'] = require('./components/views/login/RegistrationForm'); module.exports.components['views.login.ServerConfig'] = require('./components/views/login/ServerConfig'); +module.exports.components['views.messages.MAudioBody'] = require('./components/views/messages/MAudioBody'); module.exports.components['views.messages.MFileBody'] = require('./components/views/messages/MFileBody'); module.exports.components['views.messages.MImageBody'] = require('./components/views/messages/MImageBody'); module.exports.components['views.messages.MVideoBody'] = require('./components/views/messages/MVideoBody'); module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent'); -module.exports.components['views.messages.MAudioBody'] = require('./components/views/messages/MAudioBody'); module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody'); module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent'); module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody'); @@ -85,6 +85,7 @@ module.exports.components['views.rooms.MemberTile'] = require('./components/view module.exports.components['views.rooms.MessageComposer'] = require('./components/views/rooms/MessageComposer'); module.exports.components['views.rooms.MessageComposerInput'] = require('./components/views/rooms/MessageComposerInput'); module.exports.components['views.rooms.PresenceLabel'] = require('./components/views/rooms/PresenceLabel'); +module.exports.components['views.rooms.ReadReceiptMarker'] = require('./components/views/rooms/ReadReceiptMarker'); module.exports.components['views.rooms.RoomHeader'] = require('./components/views/rooms/RoomHeader'); module.exports.components['views.rooms.RoomList'] = require('./components/views/rooms/RoomList'); module.exports.components['views.rooms.RoomNameEditor'] = require('./components/views/rooms/RoomNameEditor'); diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index defc8151a9..49f4783eaa 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -81,6 +81,10 @@ module.exports = React.createClass({ // the event after which we are showing a disappearing read marker // animation this.currentGhostEventId = null; + + // opaque readreceipt info for each userId; used by ReadReceiptMarker + // to manage its animations + this._readReceiptMap = {}; }, /* get the DOM node representing the given event */ @@ -346,6 +350,7 @@ module.exports = React.createClass({ diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 3fe6a08f97..5c70c9da10 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -17,7 +17,6 @@ limitations under the License. 'use strict'; var React = require('react'); -var ReactDom = require('react-dom'); var classNames = require("classnames"); var sdk = require('../../../index'); @@ -25,8 +24,6 @@ var MatrixClientPeg = require('../../../MatrixClientPeg') var TextForEvent = require('../../../TextForEvent'); var ContextualMenu = require('../../../ContextualMenu'); -var Velociraptor = require('../../../Velociraptor'); -require('../../../VelocityBounce'); var dispatcher = require("../../../dispatcher"); var ObjectUtils = require('../../../ObjectUtils'); @@ -113,6 +110,12 @@ module.exports = React.createClass({ /* a list of Room Members whose read-receipts we should show */ readReceipts: React.PropTypes.arrayOf(React.PropTypes.object), + /* opaque readreceipt info for each userId; used by ReadReceiptMarker + * to manage its animations. Should be an empty object when the room + * first loads + */ + readReceiptMap: React.PropTypes.object, + /* the status of this event - ie, mxEvent.status. Denormalised to here so * that we can tell when it changes. */ eventSendStatus: React.PropTypes.string, @@ -122,6 +125,15 @@ module.exports = React.createClass({ return {menu: false, allReadAvatars: false}; }, + componentWillMount: function() { + // don't do RR animations until we are mounted + this._suppressReadReceiptAnimation = true; + }, + + componentDidMount: function() { + this._suppressReadReceiptAnimation = false; + }, + shouldComponentUpdate: function (nextProps, nextState) { if (!ObjectUtils.shallowEqual(this.state, nextState)) { return true; @@ -217,80 +229,53 @@ module.exports = React.createClass({ }, getReadAvatars: function() { + var ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker'); var avatars = []; - var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var left = 0; - var reorderTransitionOpts = { - duration: 100, - easing: 'easeOut' - }; - var receipts = this.props.readReceipts || []; for (var i = 0; i < receipts.length; ++i) { var member = receipts[i]; - // Using react refs here would mean both getting Velociraptor to expose - // them and making them scoped to the whole RoomView. Not impossible, but - // getElementById seems simpler at least for a first cut. - var oldAvatarDomNode = document.getElementById('mx_readAvatar'+member.userId); - var startStyles = []; - var enterTransitionOpts = []; - var oldNodeTop = -15; // For avatars that weren't on screen, act as if they were just off the top - if (oldAvatarDomNode) { - oldNodeTop = oldAvatarDomNode.getBoundingClientRect().top; + var hidden = true; + if ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) { + hidden = false; } - if (this.readAvatarNode) { - var topOffset = oldNodeTop - this.readAvatarNode.getBoundingClientRect().top; + var userId = member.userId; + var readReceiptInfo; - if (oldAvatarDomNode && oldAvatarDomNode.style.left !== '0px') { - var leftOffset = oldAvatarDomNode.style.left; - // start at the old height and in the old h pos - startStyles.push({ top: topOffset, left: leftOffset }); - enterTransitionOpts.push(reorderTransitionOpts); + if (this.props.readReceiptMap) { + readReceiptInfo = this.props.readReceiptMap[userId]; + if (!readReceiptInfo) { + readReceiptInfo = {}; + this.props.readReceiptMap[userId] = readReceiptInfo; } - - // then shift to the rightmost column, - // and then it will drop down to its resting position - startStyles.push({ top: topOffset, left: '0px' }); - enterTransitionOpts.push({ - duration: bounce ? Math.min(Math.log(Math.abs(topOffset)) * 200, 3000) : 300, - easing: bounce ? 'easeOutBounce' : 'easeOutCubic', - }); } - var style = { - left: left+'px', - top: '0px', - visibility: ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) ? 'visible' : 'hidden' - }; - //console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility); // add to the start so the most recent is on the end (ie. ends up rightmost) avatars.unshift( -