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' ;
2017-05-19 00:00:44 +03:00
2015-11-27 18:02:32 +03:00
var React = require ( 'react' ) ;
var classNames = require ( "classnames" ) ;
2017-05-25 13:39:08 +03:00
import { _t } from '../../../languageHandler' ;
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 TextForEvent = require ( '../../../TextForEvent' ) ;
2016-11-14 19:00:24 +03:00
import WithMatrixClient from '../../../wrappers/WithMatrixClient' ;
2015-11-27 18:02:32 +03:00
2016-07-27 16:49:10 +03:00
var ContextualMenu = require ( '../../structures/ContextualMenu' ) ;
2017-03-08 19:55:44 +03:00
import dis from '../../../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 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' ,
2017-06-01 15:31:24 +03:00
'm.room.avatar' : 'messages.RoomAvatarEvent' ,
2015-11-27 18:02:32 +03:00
'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' ,
2016-09-15 19:01:02 +03:00
'm.room.encryption' : 'messages.TextualEvent' ,
2017-04-06 19:02:35 +03:00
'm.room.power_levels' : '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) | | |
// | | '----------------------------' | |
// | '--------------------------------------' |
// '----------------------------------------------------------'
2016-11-14 19:00:24 +03:00
module . exports = WithMatrixClient ( React . createClass ( {
2016-08-25 18:55:09 +03:00
displayName : 'EventTile' ,
2015-11-27 18:02:32 +03:00
2015-12-24 03:12:37 +03:00
propTypes : {
2016-11-14 19:00:24 +03:00
/* MatrixClient instance for sender verification etc */
matrixClient : React . PropTypes . object . isRequired ,
2015-12-24 03:12:37 +03:00
/* the MatrixEvent to show */
mxEvent : React . PropTypes . object . isRequired ,
2017-03-06 17:20:24 +03:00
/ * t r u e i f m x E v e n t i s r e d a c t e d . T h i s i s a p r o p b e c a u s e u s i n g m x E v e n t . i s R e d a c t e d ( )
* might not be enough when deciding shouldComponentUpdate - prevProps . mxEvent
* references the same this . props . mxEvent .
* /
isRedacted : React . PropTypes . bool ,
2015-12-24 03:12:37 +03:00
/ * 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
2016-12-09 14:24:10 +03:00
/* a list of read-receipts we should show. Each object has a 'roomMember' and 'ts'. */
2016-04-19 21:38:54 +03:00
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 ,
2017-05-20 00:45:56 +03:00
// show twelve hour timestamps
isTwelveHour : React . PropTypes . bool ,
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-11-14 19:00:24 +03:00
this . props . matrixClient . on ( "deviceVerificationChanged" ,
2016-06-23 19:27:23 +03:00
this . onDeviceVerificationChanged ) ;
2016-11-15 14:12:52 +03:00
this . props . mxEvent . on ( "Event.decrypted" , this . _onDecrypted ) ;
2016-04-21 01:03:05 +03:00
} ,
2017-01-20 17:22:27 +03:00
componentWillReceiveProps : function ( nextProps ) {
2016-06-08 19:01:13 +03:00
if ( nextProps . mxEvent !== this . props . mxEvent ) {
this . _verifyEvent ( nextProps . mxEvent ) ;
}
} ,
2017-01-20 17:22:27 +03:00
shouldComponentUpdate : function ( nextProps , nextState ) {
2016-04-19 21:38:54 +03:00
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-11-14 19:00:24 +03:00
var client = this . props . matrixClient ;
client . removeListener ( "deviceVerificationChanged" ,
this . onDeviceVerificationChanged ) ;
2016-11-15 14:12:52 +03:00
this . props . mxEvent . removeListener ( "Event.decrypted" , this . _onDecrypted ) ;
} ,
/ * * c a l l e d w h e n t h e e v e n t i s d e c r y p t e d a f t e r w e s h o w i t .
* /
_onDecrypted : function ( ) {
// we need to re-verify the sending device.
this . _verifyEvent ( this . props . mxEvent ) ;
this . forceUpdate ( ) ;
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 ( ) ) {
2016-11-14 19:00:24 +03:00
verified = this . props . matrixClient . isEventSenderVerified ( mxEvent ) ;
2016-06-08 19:01:13 +03:00
}
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 ++ ) {
2016-12-09 14:24:10 +03:00
if ( rA [ j ] . roomMember . userId !== rB [ j ] . roomMember . userId ) {
2016-04-19 21:38:54 +03:00
return false ;
}
}
} else {
if ( objA [ key ] !== objB [ key ] ) {
return false ;
}
}
}
return true ;
} ,
2015-11-27 18:02:32 +03:00
shouldHighlight : function ( ) {
2016-11-14 19:00:24 +03:00
var actions = this . props . matrixClient . getPushActionsForEvent ( this . props . mxEvent ) ;
2015-11-27 18:02:32 +03:00
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-11-14 19:00:24 +03:00
if ( this . props . mxEvent . getSender ( ) === this . props . matrixClient . 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' ) ;
2017-01-20 17:22:27 +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 ;
2017-01-17 17:11:15 +03:00
var y = ( buttonRect . top + ( buttonRect . 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 ( ) {
2017-04-22 17:40:29 +03:00
// return early if there are no read receipts
if ( ! this . props . readReceipts || this . props . readReceipts . length === 0 ) {
2017-04-21 23:28:28 +03:00
return ( < span className = "mx_EventTile_readAvatars" > < / s p a n > ) ;
}
2017-03-27 16:34:05 +03:00
const ReadReceiptMarker = sdk . getComponent ( 'rooms.ReadReceiptMarker' ) ;
const avatars = [ ] ;
const receiptOffset = 15 ;
let left = 0 ;
2015-11-27 18:02:32 +03:00
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-12-09 14:24:10 +03:00
var receipt = 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
}
2017-03-27 16:34:05 +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.
// If hidden, set offset equal to the offset of the final visible avatar or
// else set it proportional to index
left = ( hidden ? MAX _READ _AVATARS - 1 : i ) * - receiptOffset ;
2015-11-27 18:02:32 +03:00
2016-12-09 14:24:10 +03:00
var userId = receipt . roomMember . userId ;
2016-04-21 01:03:05 +03:00
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
}
}
// add to the start so the most recent is on the end (ie. ends up rightmost)
avatars . unshift (
2016-12-09 14:24:10 +03:00
< ReadReceiptMarker key = { userId } member = { receipt . roomMember }
2016-04-21 01:03:05 +03:00
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-12-09 14:24:10 +03:00
timestamp = { receipt . ts }
2015-11-27 18:02:32 +03:00
/ >
) ;
}
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 }
2017-03-27 16:34:05 +03:00
style = { { right : - ( left - receiptOffset ) } } > { remainder } +
2015-11-27 18:02:32 +03:00
< / s p a n > ;
}
}
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
onSenderProfileClick : function ( event ) {
2016-03-18 19:33:22 +03:00
var mxEvent = this . props . mxEvent ;
2017-03-08 19:55:44 +03:00
dis . dispatch ( {
2016-03-17 18:35:23 +03:00
action : 'insert_displayname' ,
2016-12-03 03:20:50 +03:00
displayname : ( mxEvent . sender ? mxEvent . sender . name : mxEvent . getSender ( ) ) . replace ( ' (IRC)' , '' ) ,
2016-03-17 14:56:46 +03:00
} ) ;
} ,
2016-09-12 20:50:46 +03:00
onCryptoClicked : function ( e ) {
var event = this . props . mxEvent ;
2017-01-16 20:01:26 +03:00
Modal . createDialogAsync ( ( cb ) => {
2017-01-20 17:22:27 +03:00
require ( [ '../../../async-components/views/dialogs/EncryptedEventDialog' ] , cb ) ;
2017-01-16 20:01:26 +03:00
} , {
2016-09-13 01:42:24 +03:00
event : event ,
2016-09-12 20:50:46 +03:00
} ) ;
} ,
2017-03-08 19:55:44 +03:00
onPermalinkClicked : function ( e ) {
2017-03-09 12:56:52 +03:00
// This allows the permalink to be opened in a new tab/window or copied as
2017-03-08 19:55:44 +03:00
// matrix.to, but also for it to enable routing within Riot when clicked.
e . preventDefault ( ) ;
dis . dispatch ( {
action : 'view_room' ,
event _id : this . props . mxEvent . getId ( ) ,
2017-06-08 16:17:49 +03:00
highlighted : true ,
2017-03-08 19:55:44 +03:00
room _id : this . props . mxEvent . getRoomId ( ) ,
} ) ;
} ,
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
2017-04-26 20:00:25 +03:00
// Info messages are basically information about commands processed on a room
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-11-14 19:00:24 +03:00
var e2eEnabled = this . props . matrixClient . isRoomEncrypted ( this . props . mxEvent . getRoomId ( ) ) ;
2016-09-12 03:37:51 +03:00
var isSending = ( [ 'sending' , 'queued' , 'encrypting' ] . indexOf ( this . props . eventSendStatus ) !== - 1 ) ;
2017-03-06 17:20:24 +03:00
const isRedacted = ( eventType === 'm.room.message' ) && this . props . isRedacted ;
2016-09-12 03:37:51 +03:00
2017-05-20 00:26:24 +03:00
const classes = classNames ( {
2015-11-27 18:02:32 +03:00
mx _EventTile : true ,
2016-08-18 23:53:37 +03:00
mx _EventTile _info : isInfoMessage ,
2017-05-20 00:45:56 +03:00
mx _EventTile _12hr : this . props . isTwelveHour ,
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-16 00:49:00 +03:00
mx _EventTile _verified : this . state . verified == true ,
2016-06-08 19:01:13 +03:00
mx _EventTile _unverified : this . state . verified == false ,
2017-04-26 20:00:25 +03:00
mx _EventTile _bad : msgtype === 'm.bad.encrypted' ,
mx _EventTile _emote : msgtype === 'm.emote' ,
2017-03-03 18:42:24 +03:00
mx _EventTile _redacted : isRedacted ,
2015-11-27 18:02:32 +03:00
} ) ;
2017-03-08 19:55:44 +03:00
const permalink = "https://matrix.to/#/" +
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 ;
2017-03-16 20:00:10 +03:00
if ( this . props . tileShape === "notif" ) {
2016-09-11 04:14:27 +03:00
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 ;
2016-09-16 04:40:09 +03:00
} else if ( this . props . continuation && this . props . tileShape !== "file_grid" ) {
2016-08-25 18:55:09 +03:00
// 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 }
2016-11-10 18:18:59 +03:00
viewUserOnClick = { true }
2016-08-25 18:55:09 +03:00
/ >
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 ) {
2017-05-23 17:16:31 +03:00
if ( msgtype === 'm.image' ) aux = _t ( 'sent an image' ) ;
else if ( msgtype === 'm.video' ) aux = _t ( 'sent a video' ) ;
else if ( msgtype === 'm.file' ) aux = _t ( 'uploaded a file' ) ;
2016-09-11 04:14:27 +03:00
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
2017-05-20 00:26:24 +03:00
const editButton = (
2017-05-31 01:01:23 +03:00
< span className = "mx_EventTile_editButton" title = { _t ( "Options" ) } onClick = { this . onEditClicked } / >
2016-08-16 13:59:26 +03:00
) ;
2017-05-20 00:26:24 +03:00
let e2e ;
2016-09-15 21:25:53 +03:00
// cosmetic padlocks:
if ( ( e2eEnabled && this . props . eventSendStatus ) || this . props . mxEvent . getType ( ) === 'm.room.encryption' ) {
2017-06-08 16:08:51 +03:00
e2e = < img style = { { cursor : 'initial' , marginLeft : '-1px' } } className = "mx_EventTile_e2eIcon" alt = { _t ( "Encrypted by a verified device" ) } src = "img/e2e-verified.svg" width = "10" height = "12" / > ;
2016-09-15 21:25:53 +03:00
}
// real padlocks
else 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' ) {
2017-06-08 16:08:51 +03:00
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" alt = { _t ( "Undecryptable" ) } 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 ) ) {
2017-06-08 16:08:51 +03:00
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" alt = { _t ( "Encrypted by a verified device" ) } src = "img/e2e-verified.svg" width = "10" height = "12" / > ;
2016-09-12 03:37:51 +03:00
}
else {
2017-06-08 16:08:51 +03:00
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" alt = { _t ( "Encrypted by an unverified device" ) } src = "img/e2e-warning.svg" width = "15" height = "12" style = { { marginLeft : "-2px" } } / > ;
2016-09-12 03:37:51 +03:00
}
}
2016-09-14 03:25:43 +03:00
else if ( e2eEnabled ) {
2017-06-08 16:08:51 +03:00
e2e = < img onClick = { this . onCryptoClicked } className = "mx_EventTile_e2eIcon" alt = { _t ( "Unencrypted message" ) } src = "img/e2e-unencrypted.svg" width = "12" height = "12" / > ;
2016-09-14 03:25:43 +03:00
}
2017-03-16 20:00:10 +03:00
const timestamp = this . props . mxEvent . getTs ( ) ?
2017-05-20 00:45:56 +03:00
< MessageTimestamp showTwelveHour = { this . props . isTwelveHour } ts = { this . props . mxEvent . getTs ( ) } / > : null ;
2016-09-12 03:37:51 +03:00
2016-09-11 04:14:27 +03:00
if ( this . props . tileShape === "notif" ) {
2017-05-20 00:26:24 +03:00
const room = this . props . matrixClient . getRoom ( this . props . mxEvent . getRoomId ( ) ) ;
2016-09-11 04:14:27 +03:00
return (
< div className = { classes } >
< div className = "mx_EventTile_roomName" >
2017-03-08 19:55:44 +03:00
< a href = { permalink } onClick = { this . onPermalinkClicked } >
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 }
2017-03-08 19:55:44 +03:00
< a href = { permalink } onClick = { this . onPermalinkClicked } >
2016-09-11 04:14:27 +03:00
{ sender }
2017-03-03 18:42:24 +03:00
{ timestamp }
2016-09-11 04:14:27 +03:00
< / 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 >
2017-03-08 19:55:44 +03:00
< a
className = "mx_EventTile_senderDetailsLink"
href = { permalink }
onClick = { this . onPermalinkClicked }
>
2016-09-11 04:14:27 +03:00
< div className = "mx_EventTile_senderDetails" >
{ sender }
2017-03-03 18:42:24 +03:00
{ timestamp }
2016-09-11 04:14:27 +03:00
< / 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" >
2017-03-08 19:55:44 +03:00
< a href = { permalink } onClick = { this . onPermalinkClicked } >
2017-03-03 18:42:24 +03:00
{ timestamp }
2016-09-11 04:14:27 +03:00
< / 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
} ,
2016-11-14 19:00:24 +03:00
} ) ) ;
module . exports . haveTileForEvent = function ( e ) {
2017-03-03 19:45:29 +03:00
// Only messages have a tile (black-rectangle) if redacted
if ( e . isRedacted ( ) && e . getType ( ) !== 'm.room.message' ) return false ;
2016-11-14 19:00:24 +03:00
if ( eventTileTypes [ e . getType ( ) ] == undefined ) return false ;
if ( eventTileTypes [ e . getType ( ) ] == 'messages.TextualEvent' ) {
return TextForEvent . textForEvent ( e ) !== '' ;
} else {
return true ;
}
} ;