2015-11-27 18:02:32 +03:00
/ *
2016-01-07 07:06:39 +03:00
Copyright 2015 , 2016 OpenMarket Ltd
2015-11-27 18:02:32 +03:00
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 classNames = require ( "classnames" ) ;
2016-09-12 20:50:46 +03:00
var Modal = require ( '../../../Modal' ) ;
2015-11-27 18:02:32 +03:00
var sdk = require ( '../../../index' ) ;
var MatrixClientPeg = require ( '../../../MatrixClientPeg' )
var TextForEvent = require ( '../../../TextForEvent' ) ;
2016-07-27 16:49:10 +03:00
var ContextualMenu = require ( '../../structures/ContextualMenu' ) ;
2016-03-17 00:15:38 +03:00
var dispatcher = require ( "../../../dispatcher" ) ;
2015-11-27 18:02:32 +03:00
2016-04-19 21:38:54 +03:00
var ObjectUtils = require ( '../../../ObjectUtils' ) ;
2015-11-27 18:02:32 +03:00
var bounce = false ;
try {
if ( global . localStorage ) {
bounce = global . localStorage . getItem ( 'avatar_bounce' ) == 'true' ;
}
} catch ( e ) {
}
var eventTileTypes = {
2015-11-30 18:19:43 +03:00
'm.room.message' : 'messages.MessageEvent' ,
2015-11-27 18:02:32 +03:00
'm.room.member' : 'messages.TextualEvent' ,
'm.call.invite' : 'messages.TextualEvent' ,
'm.call.answer' : 'messages.TextualEvent' ,
'm.call.hangup' : 'messages.TextualEvent' ,
'm.room.name' : 'messages.TextualEvent' ,
'm.room.topic' : 'messages.TextualEvent' ,
2016-03-16 02:47:40 +03:00
'm.room.third_party_invite' : 'messages.TextualEvent' ,
'm.room.history_visibility' : 'messages.TextualEvent' ,
2015-11-27 18:02:32 +03:00
} ;
var MAX _READ _AVATARS = 5 ;
2015-11-30 18:19:43 +03:00
// Our component structure for EventTiles on the timeline is:
//
// .-EventTile------------------------------------------------.
// | MemberAvatar (SenderProfile) TimeStamp |
// | .-{Message,Textual}Event---------------. Read Avatars |
// | | .-MFooBody-------------------. | |
// | | | (only if MessageEvent) | | |
// | | '----------------------------' | |
// | '--------------------------------------' |
// '----------------------------------------------------------'
2015-11-27 18:02:32 +03:00
module . exports = React . createClass ( {
2016-08-25 18:55:09 +03:00
displayName : 'EventTile' ,
2015-11-27 18:02:32 +03:00
statics : {
haveTileForEvent : function ( e ) {
2016-02-16 19:05:27 +03:00
if ( e . isRedacted ( ) ) return false ;
2015-11-27 18:02:32 +03:00
if ( eventTileTypes [ e . getType ( ) ] == undefined ) return false ;
if ( eventTileTypes [ e . getType ( ) ] == 'messages.TextualEvent' ) {
return TextForEvent . textForEvent ( e ) !== '' ;
} else {
return true ;
}
}
} ,
2015-12-24 03:12:37 +03:00
propTypes : {
/* the MatrixEvent to show */
mxEvent : React . PropTypes . object . isRequired ,
/ * t r u e i f t h i s i s a c o n t i n u a t i o n o f t h e p r e v i o u s e v e n t ( w h i c h h a s t h e
* effect of not showing another avatar / displayname
* /
continuation : React . PropTypes . bool ,
/ * t r u e i f t h i s i s t h e l a s t e v e n t i n t h e t i m e l i n e ( w h i c h h a s t h e e f f e c t
* of always showing the timestamp )
* /
last : React . PropTypes . bool ,
/ * t r u e i f t h i s i s s e a r c h c o n t e x t ( w h i c h h a s t h e e f f e c t o f g r e y i n g o u t
* the text
* /
contextual : React . PropTypes . bool ,
2016-03-05 05:05:29 +03:00
/* a list of words to highlight, ordered by longest first */
2015-12-24 03:12:37 +03:00
highlights : React . PropTypes . array ,
2016-02-17 22:50:04 +03:00
/* link URL for the highlights */
highlightLink : React . PropTypes . string ,
2016-02-01 19:31:12 +03:00
2016-07-18 03:35:42 +03:00
/* should show URL previews for this event */
showUrlPreview : React . PropTypes . bool ,
2016-03-05 05:05:29 +03:00
/* is this the focused event */
2016-02-03 11:03:10 +03:00
isSelectedEvent : React . PropTypes . bool ,
2016-02-22 20:19:04 +03:00
2016-04-02 02:36:19 +03:00
/* callback called when dynamic content in events are loaded */
onWidgetLoad : React . PropTypes . func ,
2016-04-19 21:38:54 +03:00
/* a list of Room Members whose read-receipts we should show */
readReceipts : React . PropTypes . arrayOf ( React . PropTypes . object ) ,
2016-04-21 01:03:05 +03:00
/ * o p a q u e r e a d r e c e i p t i n f o f o r e a c h u s e r I d ; u s e d b y R e a d R e c e i p t M a r k e r
* to manage its animations . Should be an empty object when the room
* first loads
* /
readReceiptMap : React . PropTypes . object ,
2016-04-22 19:03:15 +03:00
/ * A f u n c t i o n w h i c h i s u s e d t o c h e c k i f t h e p a r e n t p a n e l i s b e i n g
* unmounted , to avoid unnecessary work . Should return true if we
* are being unmounted .
* /
checkUnmounting : React . PropTypes . func ,
2016-04-19 21:38:54 +03:00
/ * t h e s t a t u s o f t h i s e v e n t - i e , m x E v e n t . s t a t u s . D e n o r m a l i s e d t o h e r e s o
* that we can tell when it changes . * /
eventSendStatus : React . PropTypes . string ,
2016-09-11 04:14:27 +03:00
/ * t h e s h a p e o f t h e t i l e . b y d e f a u l t , t h e l a y o u t i s i n t e n d e d f o r t h e
* normal room timeline . alternative values are : "file_list" , "file_grid"
* and "notif" . This could be done by CSS , but it ' d be horribly inefficient .
* It could also be done by subclassing EventTile , but that ' d be quite
* boiilerplatey . So just make the necessary render decisions conditional
* for now .
* /
tileShape : React . PropTypes . string ,
2015-12-24 03:12:37 +03:00
} ,
2015-11-27 18:02:32 +03:00
getInitialState : function ( ) {
2016-06-08 19:01:13 +03:00
return { menu : false , allReadAvatars : false , verified : null } ;
2015-11-27 18:02:32 +03:00
} ,
2016-04-21 01:03:05 +03:00
componentWillMount : function ( ) {
// don't do RR animations until we are mounted
this . _suppressReadReceiptAnimation = true ;
2016-06-08 19:01:13 +03:00
this . _verifyEvent ( this . props . mxEvent ) ;
2016-04-21 01:03:05 +03:00
} ,
componentDidMount : function ( ) {
this . _suppressReadReceiptAnimation = false ;
2016-06-23 19:27:23 +03:00
MatrixClientPeg . get ( ) . on ( "deviceVerificationChanged" ,
this . onDeviceVerificationChanged ) ;
2016-04-21 01:03:05 +03:00
} ,
2016-06-08 19:01:13 +03:00
componentWillReceiveProps : function ( nextProps ) {
if ( nextProps . mxEvent !== this . props . mxEvent ) {
this . _verifyEvent ( nextProps . mxEvent ) ;
}
} ,
2016-04-19 21:38:54 +03:00
shouldComponentUpdate : function ( nextProps , nextState ) {
if ( ! ObjectUtils . shallowEqual ( this . state , nextState ) ) {
return true ;
}
if ( ! this . _propsEqual ( this . props , nextProps ) ) {
return true ;
}
return false ;
} ,
2016-06-08 20:35:43 +03:00
componentWillUnmount : function ( ) {
2016-06-08 23:25:42 +03:00
var client = MatrixClientPeg . get ( ) ;
if ( client ) {
2016-06-23 19:27:23 +03:00
client . removeListener ( "deviceVerificationChanged" ,
this . onDeviceVerificationChanged ) ;
2016-06-08 23:25:42 +03:00
}
2016-06-08 20:35:43 +03:00
} ,
2016-06-23 19:27:23 +03:00
onDeviceVerificationChanged : function ( userId , device ) {
2016-06-08 23:25:42 +03:00
if ( userId == this . props . mxEvent . getSender ( ) ) {
this . _verifyEvent ( this . props . mxEvent ) ;
2016-06-08 20:35:43 +03:00
}
} ,
2016-06-08 19:01:13 +03:00
_verifyEvent : function ( mxEvent ) {
var verified = null ;
if ( mxEvent . isEncrypted ( ) ) {
verified = MatrixClientPeg . get ( ) . isEventSenderVerified ( mxEvent ) ;
}
this . setState ( {
verified : verified
} ) ;
} ,
2016-04-19 21:38:54 +03:00
_propsEqual : function ( objA , objB ) {
var keysA = Object . keys ( objA ) ;
var keysB = Object . keys ( objB ) ;
if ( keysA . length !== keysB . length ) {
return false ;
}
for ( var i = 0 ; i < keysA . length ; i ++ ) {
var key = keysA [ i ] ;
if ( ! objB . hasOwnProperty ( key ) ) {
return false ;
}
// need to deep-compare readReceipts
if ( key == 'readReceipts' ) {
var rA = objA [ key ] ;
var rB = objB [ key ] ;
2016-04-19 23:10:23 +03:00
if ( rA === rB ) {
continue ;
}
if ( ! rA || ! rB ) {
return false ;
}
2016-04-19 21:38:54 +03:00
if ( rA . length !== rB . length ) {
return false ;
}
for ( var j = 0 ; j < rA . length ; j ++ ) {
if ( rA [ j ] . userId !== rB [ j ] . userId ) {
return false ;
}
}
} else {
if ( objA [ key ] !== objB [ key ] ) {
return false ;
}
}
}
return true ;
} ,
2015-11-27 18:02:32 +03:00
shouldHighlight : function ( ) {
var actions = MatrixClientPeg . get ( ) . getPushActionsForEvent ( this . props . mxEvent ) ;
if ( ! actions || ! actions . tweaks ) { return false ; }
2016-02-19 04:56:03 +03:00
// don't show self-highlights from another of our clients
2016-09-12 03:37:51 +03:00
if ( this . props . mxEvent . getSender ( ) === MatrixClientPeg . get ( ) . credentials . userId )
2016-02-19 04:56:03 +03:00
{
return false ;
}
2016-04-08 23:42:29 +03:00
2015-11-27 18:02:32 +03:00
return actions . tweaks . highlight ;
} ,
onEditClicked : function ( e ) {
2016-07-27 12:41:24 +03:00
var MessageContextMenu = sdk . getComponent ( 'context_menus.MessageContextMenu' ) ;
2015-11-27 18:02:32 +03:00
var buttonRect = e . target . getBoundingClientRect ( )
2016-07-27 11:51:50 +03:00
// The window X and Y offsets are to adjust position when zoomed in to page
var x = buttonRect . right + window . pageXOffset ;
2016-07-27 18:09:07 +03:00
var y = ( buttonRect . top + ( e . target . height / 2 ) + window . pageYOffset ) - 19 ;
2015-11-27 18:02:32 +03:00
var self = this ;
ContextualMenu . createMenu ( MessageContextMenu , {
2016-07-27 18:09:07 +03:00
chevronOffset : 10 ,
2015-11-27 18:02:32 +03:00
mxEvent : this . props . mxEvent ,
left : x ,
top : y ,
2016-04-14 17:50:00 +03:00
eventTileOps : this . refs . tile && this . refs . tile . getEventTileOps ? this . refs . tile . getEventTileOps ( ) : undefined ,
2015-11-27 18:02:32 +03:00
onFinished : function ( ) {
self . setState ( { menu : false } ) ;
}
} ) ;
this . setState ( { menu : true } ) ;
} ,
toggleAllReadAvatars : function ( ) {
this . setState ( {
allReadAvatars : ! this . state . allReadAvatars
} ) ;
} ,
getReadAvatars : function ( ) {
2016-04-21 01:03:05 +03:00
var ReadReceiptMarker = sdk . getComponent ( 'rooms.ReadReceiptMarker' ) ;
2015-11-27 18:02:32 +03:00
var avatars = [ ] ;
var left = 0 ;
2016-04-19 21:38:54 +03:00
var receipts = this . props . readReceipts || [ ] ;
2015-11-27 18:02:32 +03:00
for ( var i = 0 ; i < receipts . length ; ++ i ) {
2016-04-19 21:38:54 +03:00
var member = receipts [ i ] ;
2015-11-27 18:02:32 +03:00
2016-04-21 01:03:05 +03:00
var hidden = true ;
if ( ( i < MAX _READ _AVATARS ) || this . state . allReadAvatars ) {
hidden = false ;
2015-11-27 18:02:32 +03:00
}
2016-04-21 01:03:05 +03:00
var userId = member . userId ;
var readReceiptInfo ;
2015-11-27 18:02:32 +03:00
2016-04-21 01:03:05 +03:00
if ( this . props . readReceiptMap ) {
readReceiptInfo = this . props . readReceiptMap [ userId ] ;
if ( ! readReceiptInfo ) {
readReceiptInfo = { } ;
this . props . readReceiptMap [ userId ] = readReceiptInfo ;
2015-11-27 18:02:32 +03:00
}
}
//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 (
2016-04-21 01:03:05 +03:00
< ReadReceiptMarker key = { userId } member = { member }
leftOffset = { left } hidden = { hidden }
readReceiptInfo = { readReceiptInfo }
2016-04-22 19:03:15 +03:00
checkUnmounting = { this . props . checkUnmounting }
2016-04-21 01:03:05 +03:00
suppressAnimation = { this . _suppressReadReceiptAnimation }
2015-11-27 18:02:32 +03:00
onClick = { this . toggleAllReadAvatars }
/ >
) ;
2016-04-21 01:03:05 +03:00
2015-11-27 18:02:32 +03:00
// TODO: we keep the extra read avatars in the dom to make animation simpler
// we could optimise this to reduce the dom size.
2016-04-21 01:03:05 +03:00
if ( ! hidden ) {
2015-11-27 18:02:32 +03:00
left -= 15 ;
}
}
2016-04-21 01:03:05 +03:00
var remText ;
2015-11-27 18:02:32 +03:00
if ( ! this . state . allReadAvatars ) {
var remainder = receipts . length - MAX _READ _AVATARS ;
if ( remainder > 0 ) {
remText = < span className = "mx_EventTile_readAvatarRemainder"
onClick = { this . toggleAllReadAvatars }
style = { { left : left } } > { remainder } +
< / s p a n > ;
left -= 15 ;
}
}
2016-04-21 01:03:05 +03:00
return < span className = "mx_EventTile_readAvatars" >
2015-11-27 18:02:32 +03:00
{ remText }
2016-04-21 01:03:05 +03:00
{ avatars }
2015-11-27 18:02:32 +03:00
< / s p a n > ;
} ,
2016-03-17 18:35:23 +03:00
onMemberAvatarClick : function ( event ) {
2016-03-17 14:56:46 +03:00
dispatcher . dispatch ( {
action : 'view_user' ,
2016-03-17 18:35:23 +03:00
member : this . props . mxEvent . sender ,
} ) ;
} ,
onSenderProfileClick : function ( event ) {
2016-03-18 19:33:22 +03:00
var mxEvent = this . props . mxEvent ;
2016-03-17 18:35:23 +03:00
dispatcher . dispatch ( {
action : 'insert_displayname' ,
2016-03-18 19:33:22 +03:00
displayname : mxEvent . sender ? mxEvent . sender . name : mxEvent . getSender ( ) ,
2016-03-17 14:56:46 +03:00
} ) ;
} ,
2016-09-12 20:50:46 +03:00
onCryptoClicked : function ( e ) {
2016-09-13 01:42:24 +03:00
var EncryptedEventDialog = sdk . getComponent ( "dialogs.EncryptedEventDialog" ) ;
2016-09-12 20:50:46 +03:00
var event = this . props . mxEvent ;
2016-09-13 01:42:24 +03:00
Modal . createDialog ( EncryptedEventDialog , {
event : event ,
2016-09-12 20:50:46 +03:00
} ) ;
} ,
2015-11-27 18:02:32 +03:00
render : function ( ) {
var MessageTimestamp = sdk . getComponent ( 'messages.MessageTimestamp' ) ;
2015-12-01 14:19:25 +03:00
var SenderProfile = sdk . getComponent ( 'messages.SenderProfile' ) ;
2015-11-27 18:02:32 +03:00
var MemberAvatar = sdk . getComponent ( 'avatars.MemberAvatar' ) ;
2016-07-20 14:03:13 +03:00
//console.log("EventTile showUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
2015-11-27 18:02:32 +03:00
var content = this . props . mxEvent . getContent ( ) ;
var msgtype = content . msgtype ;
2016-08-18 23:53:37 +03:00
var eventType = this . props . mxEvent . getType ( ) ;
2016-08-19 00:19:23 +03:00
// Info messages are basically information about commands processed on a
// room, or emote messages
2016-09-12 01:01:20 +03:00
var isInfoMessage = ( eventType !== 'm.room.message' ) ;
2015-11-27 18:02:32 +03:00
2016-08-25 18:55:09 +03:00
var EventTileType = sdk . getComponent ( eventTileTypes [ eventType ] ) ;
2015-11-27 18:02:32 +03:00
// This shouldn't happen: the caller should check we support this type
// before trying to instantiate us
if ( ! EventTileType ) {
throw new Error ( "Event type not supported" ) ;
}
2016-09-12 03:37:51 +03:00
var e2eEnabled = MatrixClientPeg . get ( ) . isRoomEncrypted ( this . props . mxEvent . getRoomId ( ) ) ;
var isSending = ( [ 'sending' , 'queued' , 'encrypting' ] . indexOf ( this . props . eventSendStatus ) !== - 1 ) ;
2015-11-27 18:02:32 +03:00
var classes = classNames ( {
mx _EventTile : true ,
2016-08-18 23:53:37 +03:00
mx _EventTile _info : isInfoMessage ,
2016-09-12 03:37:51 +03:00
mx _EventTile _encrypting : this . props . eventSendStatus == 'encrypting' ,
mx _EventTile _sending : isSending ,
2016-04-19 21:38:54 +03:00
mx _EventTile _notSent : this . props . eventSendStatus == 'not_sent' ,
2016-09-12 03:37:51 +03:00
mx _EventTile _highlight : this . props . tileShape == 'notif' ? false : this . shouldHighlight ( ) ,
2016-02-03 11:03:10 +03:00
mx _EventTile _selected : this . props . isSelectedEvent ,
2016-09-11 04:14:27 +03:00
mx _EventTile _continuation : this . props . tileShape ? '' : this . props . continuation ,
2015-11-27 18:02:32 +03:00
mx _EventTile _last : this . props . last ,
mx _EventTile _contextual : this . props . contextual ,
menu : this . state . menu ,
2016-09-14 03:25:43 +03:00
mx _EventTile _verified : this . state . verified == true || ( e2eEnabled && this . props . eventSendStatus ) ,
2016-06-08 19:01:13 +03:00
mx _EventTile _unverified : this . state . verified == false ,
2016-09-12 03:37:51 +03:00
mx _EventTile _bad : this . props . mxEvent . getContent ( ) . msgtype === 'm.bad.encrypted' ,
2015-11-27 18:02:32 +03:00
} ) ;
2016-09-11 04:14:27 +03:00
var permalink = "#/room/" + this . props . mxEvent . getRoomId ( ) + "/" + this . props . mxEvent . getId ( ) ;
2015-11-27 18:02:32 +03:00
var readAvatars = this . getReadAvatars ( ) ;
var avatar , sender ;
2016-08-25 18:55:09 +03:00
let avatarSize ;
let needsSenderProfile ;
2016-09-11 04:14:27 +03:00
if ( this . props . tileShape === "notif" ) {
avatarSize = 24 ;
needsSenderProfile = true ;
} else if ( isInfoMessage ) {
2016-09-12 01:01:20 +03:00
// a small avatar, with no sender profile, for
2016-08-25 18:55:09 +03:00
// joins/parts/etc
avatarSize = 14 ;
needsSenderProfile = false ;
} else if ( this . props . continuation ) {
// no avatar or sender profile for continuation messages
avatarSize = 0 ;
needsSenderProfile = false ;
} else {
avatarSize = 30 ;
2016-09-12 01:01:20 +03:00
needsSenderProfile = true ;
2016-08-25 18:55:09 +03:00
}
if ( this . props . mxEvent . sender && avatarSize ) {
2016-08-23 17:58:27 +03:00
avatar = (
2016-03-17 14:56:46 +03:00
< div className = "mx_EventTile_avatar" >
2016-08-25 18:55:09 +03:00
< MemberAvatar member = { this . props . mxEvent . sender }
width = { avatarSize } height = { avatarSize }
onClick = { this . onMemberAvatarClick }
/ >
2015-11-27 18:02:32 +03:00
< / d i v >
2016-08-25 18:55:09 +03:00
) ;
}
if ( needsSenderProfile ) {
let aux = null ;
2016-09-11 04:14:27 +03:00
if ( ! this . props . tileShape ) {
if ( msgtype === 'm.image' ) aux = "sent an image" ;
else if ( msgtype === 'm.video' ) aux = "sent a video" ;
else if ( msgtype === 'm.file' ) aux = "uploaded a file" ;
sender = < SenderProfile onClick = { this . onSenderProfileClick } mxEvent = { this . props . mxEvent } aux = { aux } / > ;
}
else {
sender = < SenderProfile mxEvent = { this . props . mxEvent } / > ;
}
2015-11-27 18:02:32 +03:00
}
2016-08-16 13:59:26 +03:00
var editButton = (
2016-08-16 19:34:33 +03:00
< img className = "mx_EventTile_editButton" src = "img/icon_context_message.svg" width = "19" height = "19" alt = "Options" title = "Options" onClick = { this . onEditClicked } / >
2016-08-16 13:59:26 +03:00
) ;
2016-09-12 03:37:51 +03:00
var e2e ;
2016-09-14 03:25:43 +03:00
if ( this . props . mxEvent . isEncrypted ( ) || ( e2eEnabled && this . props . eventSendStatus ) ) {
2016-09-12 03:37:51 +03:00
if ( this . props . mxEvent . getContent ( ) . msgtype === 'm.bad.encrypted' ) {
2016-09-12 20:50:46 +03:00
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" src = "img/e2e-blocked.svg" width = "12" height = "12" style = { { marginLeft : "-1px" } } / > ;
2016-09-12 03:37:51 +03:00
}
2016-09-14 03:25:43 +03:00
else if ( this . state . verified == true || ( e2eEnabled && this . props . eventSendStatus ) ) {
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" src = "img/e2e-verified.svg" width = "10" height = "12" alt = "Encrypted by a verified device" / > ;
2016-09-12 03:37:51 +03:00
}
else {
2016-09-14 03:25:43 +03:00
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" src = "img/e2e-warning.svg" width = "15" height = "12" style = { { marginLeft : "-2px" } } alt = "Encrypted by an unverified device!" / > ;
2016-09-12 03:37:51 +03:00
}
}
2016-09-14 03:25:43 +03:00
else if ( e2eEnabled ) {
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" src = "img/e2e-unencrypted.svg" width = "12" height = "12" / > ;
}
2016-09-12 03:37:51 +03:00
2016-09-11 04:14:27 +03:00
if ( this . props . tileShape === "notif" ) {
var room = MatrixClientPeg . get ( ) . getRoom ( this . props . mxEvent . getRoomId ( ) ) ;
return (
< div className = { classes } >
< div className = "mx_EventTile_roomName" >
< a href = { permalink } >
2016-09-12 19:29:10 +03:00
{ room ? room . name : '' }
2016-09-11 04:14:27 +03:00
< / a >
< / d i v >
< div className = "mx_EventTile_senderDetails" >
{ avatar }
< a href = { permalink } >
{ sender }
< MessageTimestamp ts = { this . props . mxEvent . getTs ( ) } / >
< / a >
< / d i v >
< div className = "mx_EventTile_line" >
< EventTileType ref = "tile"
mxEvent = { this . props . mxEvent }
highlights = { this . props . highlights }
highlightLink = { this . props . highlightLink }
showUrlPreview = { this . props . showUrlPreview }
onWidgetLoad = { this . props . onWidgetLoad } / >
< / d i v >
2015-11-27 18:02:32 +03:00
< / d i v >
2016-09-11 04:14:27 +03:00
) ;
}
else if ( this . props . tileShape === "file_grid" ) {
return (
< div className = { classes } >
< div className = "mx_EventTile_line" >
< EventTileType ref = "tile"
mxEvent = { this . props . mxEvent }
highlights = { this . props . highlights }
highlightLink = { this . props . highlightLink }
showUrlPreview = { this . props . showUrlPreview }
tileShape = { this . props . tileShape }
onWidgetLoad = { this . props . onWidgetLoad } / >
< / d i v >
< a className = "mx_EventTile_senderDetailsLink" href = { permalink } >
< div className = "mx_EventTile_senderDetails" >
{ sender }
< MessageTimestamp ts = { this . props . mxEvent . getTs ( ) } / >
< / d i v >
< / a >
2015-11-27 18:02:32 +03:00
< / d i v >
2016-09-11 04:14:27 +03:00
) ;
}
else {
return (
< div className = { classes } >
< div className = "mx_EventTile_msgOption" >
{ readAvatars }
< / d i v >
{ avatar }
{ sender }
< div className = "mx_EventTile_line" >
< a href = { permalink } >
< MessageTimestamp ts = { this . props . mxEvent . getTs ( ) } / >
< / a >
2016-09-12 03:37:51 +03:00
{ e2e }
2016-09-11 04:14:27 +03:00
< EventTileType ref = "tile"
mxEvent = { this . props . mxEvent }
highlights = { this . props . highlights }
highlightLink = { this . props . highlightLink }
showUrlPreview = { this . props . showUrlPreview }
onWidgetLoad = { this . props . onWidgetLoad } / >
{ editButton }
< / d i v >
< / d i v >
) ;
}
2015-11-27 18:02:32 +03:00
} ,
} ) ;