2016-02-08 18:04:54 +00:00
/ *
Copyright 2015 , 2016 OpenMarket Ltd
2018-08-03 17:02:09 +00:00
Copyright 2017 , 2018 New Vector Ltd
2016-02-08 18:04:54 +00: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 .
* /
2017-05-23 14:16:31 +00:00
import React from 'react' ;
2017-12-26 01:03:18 +00:00
import PropTypes from 'prop-types' ;
2017-11-09 15:58:15 +00:00
import Matrix from 'matrix-js-sdk' ;
2017-11-13 19:19:33 +00:00
import { _t } from '../../languageHandler' ;
2017-05-23 14:16:31 +00:00
import sdk from '../../index' ;
import WhoIsTyping from '../../WhoIsTyping' ;
import MatrixClientPeg from '../../MatrixClientPeg' ;
import MemberAvatar from '../views/avatars/MemberAvatar' ;
2017-11-09 15:58:15 +00:00
import Resend from '../../Resend' ;
2018-01-09 17:57:18 +00:00
import * as cryptodevices from '../../cryptodevices' ;
2018-06-23 15:40:27 +00:00
import dis from '../../dispatcher' ;
2017-01-20 16:51:35 +00:00
2017-01-23 15:01:39 +00:00
const STATUS _BAR _HIDDEN = 0 ;
const STATUS _BAR _EXPANDED = 1 ;
const STATUS _BAR _EXPANDED _LARGE = 2 ;
2017-11-09 15:58:15 +00:00
function getUnsentMessages ( room ) {
if ( ! room ) { return [ ] ; }
return room . getPendingEvents ( ) . filter ( function ( ev ) {
return ev . status === Matrix . EventStatus . NOT _SENT ;
} ) ;
} ;
2016-02-08 18:04:54 +00:00
module . exports = React . createClass ( {
displayName : 'RoomStatusBar' ,
propTypes : {
// the room this statusbar is representing.
2017-12-26 01:03:18 +00:00
room : PropTypes . object . isRequired ,
2016-07-15 15:10:27 +00:00
2016-02-08 18:04:54 +00:00
// the number of messages which have arrived since we've been scrolled up
2017-12-26 01:03:18 +00:00
numUnreadMessages : PropTypes . number ,
2016-02-08 18:04:54 +00:00
// this is true if we are fully scrolled-down, and are looking at
// the end of the live timeline.
2017-12-26 01:03:18 +00:00
atEndOfLiveTimeline : PropTypes . bool ,
2016-02-08 18:04:54 +00:00
2017-10-12 03:15:00 +00:00
// This is true when the user is alone in the room, but has also sent a message.
// Used to suggest to the user to invite someone
2017-12-26 01:03:18 +00:00
sentMessageAndIsAlone : PropTypes . bool ,
2017-10-12 03:15:00 +00:00
2016-02-08 18:04:54 +00:00
// true if there is an active call in this room (means we show
// the 'Active Call' text in the status bar if there is nothing
// more interesting)
2017-12-26 01:03:18 +00:00
hasActiveCall : PropTypes . bool ,
2016-02-09 11:36:57 +00:00
2017-01-24 16:01:39 +00:00
// Number of names to display in typing indication. E.g. set to 3, will
// result in "X, Y, Z and 100 others are typing."
2017-12-26 01:03:18 +00:00
whoIsTypingLimit : PropTypes . number ,
2017-01-24 16:01:39 +00:00
2016-02-09 11:36:57 +00:00
// callback for when the user clicks on the 'resend all' button in the
// 'unsent messages' bar
2017-12-26 01:03:18 +00:00
onResendAllClick : PropTypes . func ,
2016-02-09 11:36:57 +00:00
2016-03-21 16:49:07 +00:00
// callback for when the user clicks on the 'cancel all' button in the
// 'unsent messages' bar
2017-12-26 01:03:18 +00:00
onCancelAllClick : PropTypes . func ,
2016-03-21 16:49:07 +00:00
2017-10-12 03:15:00 +00:00
// callback for when the user clicks on the 'invite others' button in the
// 'you are alone' bar
2017-12-26 01:03:18 +00:00
onInviteClick : PropTypes . func ,
2017-10-12 03:15:00 +00:00
// callback for when the user clicks on the 'stop warning me' button in the
// 'you are alone' bar
2017-12-26 01:03:18 +00:00
onStopWarningClick : PropTypes . func ,
2017-10-12 03:15:00 +00:00
2016-02-09 11:36:57 +00:00
// callback for when the user clicks on the 'scroll to bottom' button
2017-12-26 01:03:18 +00:00
onScrollToBottomClick : PropTypes . func ,
2016-02-23 11:06:16 +00:00
// callback for when we do something that changes the size of the
// status bar. This is used to trigger a re-layout in the parent
// component.
2017-12-26 01:03:18 +00:00
onResize : PropTypes . func ,
2017-01-23 15:01:39 +00:00
// callback for when the status bar can be hidden from view, as it is
// not displaying anything
2017-12-26 01:03:18 +00:00
onHidden : PropTypes . func ,
2017-02-02 17:48:47 +00:00
2017-01-23 15:01:39 +00:00
// callback for when the status bar is displaying something and should
// be visible
2017-12-26 01:03:18 +00:00
onVisible : PropTypes . func ,
2016-02-08 18:04:54 +00:00
} ,
2017-01-24 17:16:26 +00:00
getDefaultProps : function ( ) {
return {
2017-02-09 10:30:06 +00:00
whoIsTypingLimit : 3 ,
2017-01-24 17:16:26 +00:00
} ;
} ,
2016-02-08 18:04:54 +00:00
getInitialState : function ( ) {
return {
syncState : MatrixClientPeg . get ( ) . getSyncState ( ) ,
2018-08-03 17:02:09 +00:00
syncStateData : MatrixClientPeg . get ( ) . getSyncStateData ( ) ,
2017-02-09 10:01:51 +00:00
usersTyping : WhoIsTyping . usersTypingApartFromMe ( this . props . room ) ,
2017-11-11 15:57:20 +00:00
unsentMessages : getUnsentMessages ( this . props . room ) ,
2016-02-08 18:04:54 +00:00
} ;
} ,
componentWillMount : function ( ) {
MatrixClientPeg . get ( ) . on ( "sync" , this . onSyncStateChange ) ;
2016-02-23 16:17:50 +00:00
MatrixClientPeg . get ( ) . on ( "RoomMember.typing" , this . onRoomMemberTyping ) ;
2017-12-06 19:02:26 +00:00
MatrixClientPeg . get ( ) . on ( "Room.localEchoUpdated" , this . _onRoomLocalEchoUpdated ) ;
2016-02-08 18:04:54 +00:00
2017-03-28 08:30:41 +00:00
this . _checkSize ( ) ;
} ,
2017-01-23 15:01:39 +00:00
2017-03-28 08:30:41 +00:00
componentDidUpdate : function ( ) {
this . _checkSize ( ) ;
2016-02-23 11:06:16 +00:00
} ,
2016-02-08 18:04:54 +00:00
componentWillUnmount : function ( ) {
2016-02-15 21:50:39 +00:00
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
2017-10-11 16:56:17 +00:00
const client = MatrixClientPeg . get ( ) ;
2016-02-23 16:17:50 +00:00
if ( client ) {
client . removeListener ( "sync" , this . onSyncStateChange ) ;
client . removeListener ( "RoomMember.typing" , this . onRoomMemberTyping ) ;
2017-12-06 19:02:26 +00:00
client . removeListener ( "Room.localEchoUpdated" , this . _onRoomLocalEchoUpdated ) ;
2016-02-15 21:50:39 +00:00
}
2016-02-08 18:04:54 +00:00
} ,
2018-08-03 17:02:09 +00:00
onSyncStateChange : function ( state , prevState , data ) {
2016-02-08 18:04:54 +00:00
if ( state === "SYNCING" && prevState === "SYNCING" ) {
return ;
}
this . setState ( {
2017-10-11 16:56:17 +00:00
syncState : state ,
2018-08-03 17:02:09 +00:00
syncStateData : data ,
2016-02-08 18:04:54 +00:00
} ) ;
} ,
2016-02-23 16:17:50 +00:00
onRoomMemberTyping : function ( ev , member ) {
this . setState ( {
2017-09-15 02:25:02 +00:00
usersTyping : WhoIsTyping . usersTypingApartFromMeAndIgnored ( this . props . room ) ,
2016-02-23 16:17:50 +00:00
} ) ;
} ,
2018-01-09 13:52:37 +00:00
_onSendWithoutVerifyingClick : function ( ) {
2018-01-09 17:57:18 +00:00
cryptodevices . getUnknownDevicesForRoom ( MatrixClientPeg . get ( ) , this . props . room ) . then ( ( devices ) => {
cryptodevices . markAllDevicesKnown ( MatrixClientPeg . get ( ) , devices ) ;
2018-01-09 13:52:37 +00:00
Resend . resendUnsentEvents ( this . props . room ) ;
} ) ;
} ,
2017-11-09 15:58:15 +00:00
_onResendAllClick : function ( ) {
Resend . resendUnsentEvents ( this . props . room ) ;
2018-06-23 15:40:27 +00:00
dis . dispatch ( { action : 'focus_composer' } ) ;
2017-11-09 15:58:15 +00:00
} ,
_onCancelAllClick : function ( ) {
Resend . cancelUnsentEvents ( this . props . room ) ;
2018-06-23 15:40:27 +00:00
dis . dispatch ( { action : 'focus_composer' } ) ;
2017-11-09 15:58:15 +00:00
} ,
_onShowDevicesClick : function ( ) {
2018-01-09 17:57:18 +00:00
cryptodevices . showUnknownDeviceDialogForMessages ( MatrixClientPeg . get ( ) , this . props . room ) ;
2017-11-09 15:58:15 +00:00
} ,
2017-12-06 19:02:26 +00:00
_onRoomLocalEchoUpdated : function ( event , room , oldEventId , oldStatus ) {
2017-11-09 15:58:15 +00:00
if ( room . roomId !== this . props . room . roomId ) return ;
this . setState ( {
unsentMessages : getUnsentMessages ( this . props . room ) ,
} ) ;
} ,
2017-03-28 08:30:41 +00:00
// Check whether current size is greater than 0, if yes call props.onVisible
2017-10-11 16:56:17 +00:00
_checkSize : function ( ) {
2018-01-01 21:15:26 +00:00
if ( this . _getSize ( ) ) {
if ( this . props . onVisible ) this . props . onVisible ( ) ;
} else {
if ( this . props . onHidden ) this . props . onHidden ( ) ;
2017-03-28 08:30:41 +00:00
}
} ,
2017-01-23 15:01:39 +00:00
// We don't need the actual height - just whether it is likely to have
// changed - so we use '0' to indicate normal size, and other values to
// indicate other sizes.
2017-03-28 08:30:41 +00:00
_getSize : function ( ) {
2018-08-03 17:02:09 +00:00
if ( this . _shouldShowConnectionError ( ) ||
2017-03-28 08:30:41 +00:00
( this . state . usersTyping . length > 0 ) ||
this . props . numUnreadMessages ||
! this . props . atEndOfLiveTimeline ||
2017-10-12 03:15:00 +00:00
this . props . hasActiveCall ||
this . props . sentMessageAndIsAlone
2017-02-22 14:03:30 +00:00
) {
2017-01-23 15:01:39 +00:00
return STATUS _BAR _EXPANDED ;
2017-11-09 15:58:15 +00:00
} else if ( this . state . unsentMessages . length > 0 ) {
2017-01-23 15:01:39 +00:00
return STATUS _BAR _EXPANDED _LARGE ;
2016-02-23 11:06:16 +00:00
}
2017-01-23 15:01:39 +00:00
return STATUS _BAR _HIDDEN ;
} ,
2016-02-23 11:06:16 +00:00
2016-02-09 14:42:32 +00:00
// return suitable content for the image on the left of the status bar.
//
// if wantPlaceholder is true, we include a "..." placeholder if
// there is nothing better to put in.
_getIndicator : function ( wantPlaceholder ) {
if ( this . props . numUnreadMessages ) {
return (
< div className = "mx_RoomStatusBar_scrollDownIndicator"
2017-10-11 16:56:17 +00:00
onClick = { this . props . onScrollToBottomClick } >
2016-02-09 14:42:32 +00:00
< img src = "img/newmessages.svg" width = "24" height = "24"
2017-10-11 16:56:17 +00:00
alt = "" / >
2016-02-09 14:42:32 +00:00
< / d i v >
) ;
}
if ( ! this . props . atEndOfLiveTimeline ) {
return (
< div className = "mx_RoomStatusBar_scrollDownIndicator"
2017-10-11 16:56:17 +00:00
onClick = { this . props . onScrollToBottomClick } >
2016-02-09 14:42:32 +00:00
< img src = "img/scrolldown.svg" width = "24" height = "24"
2017-10-11 16:56:17 +00:00
alt = { _t ( "Scroll to bottom of page" ) }
title = { _t ( "Scroll to bottom of page" ) } / >
2016-02-09 14:42:32 +00:00
< / d i v >
) ;
}
if ( this . props . hasActiveCall ) {
2017-10-11 16:56:17 +00:00
const TintableSvg = sdk . getComponent ( "elements.TintableSvg" ) ;
2016-02-09 14:42:32 +00:00
return (
2017-10-11 16:56:17 +00:00
< TintableSvg src = "img/sound-indicator.svg" width = "23" height = "20" / >
2016-02-09 14:42:32 +00:00
) ;
}
2018-08-03 17:02:09 +00:00
if ( this . _shouldShowConnectionError ( ) ) {
2016-02-18 18:16:48 +00:00
return null ;
}
2016-02-09 14:42:32 +00:00
if ( wantPlaceholder ) {
return (
2017-01-20 16:51:35 +00:00
< div className = "mx_RoomStatusBar_typingIndicatorAvatars" >
2017-10-11 16:56:17 +00:00
{ this . _renderTypingIndicatorAvatars ( this . props . whoIsTypingLimit ) }
2016-03-28 14:09:36 +00:00
< / d i v >
2016-02-09 14:42:32 +00:00
) ;
}
return null ;
} ,
2017-01-20 16:51:35 +00:00
_renderTypingIndicatorAvatars : function ( limit ) {
2017-02-09 10:01:51 +00:00
let users = this . state . usersTyping ;
2017-01-20 16:51:35 +00:00
2017-02-09 10:30:06 +00:00
let othersCount = 0 ;
if ( users . length > limit ) {
othersCount = users . length - limit + 1 ;
users = users . slice ( 0 , limit - 1 ) ;
}
2017-01-20 16:51:35 +00:00
2017-02-15 16:29:08 +00:00
const avatars = users . map ( ( u ) => {
2017-01-20 16:51:35 +00:00
return (
< MemberAvatar
key = { u . userId }
member = { u }
width = { 24 }
height = { 24 }
resizeMethod = "crop"
/ >
) ;
} ) ;
if ( othersCount > 0 ) {
avatars . push (
2017-02-02 18:47:15 +00:00
< span className = "mx_RoomStatusBar_typingIndicatorRemaining" key = "others" >
2017-10-11 16:56:17 +00:00
+ { othersCount }
< / s p a n > ,
2017-01-20 16:51:35 +00:00
) ;
}
return avatars ;
} ,
2016-02-09 14:42:32 +00:00
2018-08-03 17:02:09 +00:00
_shouldShowConnectionError : function ( ) {
// no conn bar trumps unread count since you can't get unread messages
// without a connection! (technically may already have some but meh)
// It also trumps the "some not sent" msg since you can't resend without
// a connection!
// There's one situation in which we don't show this 'no connection' bar, and that's
// if it's a monthly-active-user limit error: those are shown in the top bar.
const errorIsMauError = Boolean (
this . state . syncStateData &&
this . state . syncStateData . error &&
this . state . syncStateData . error . errcode === 'M_MAU_LIMIT_EXCEEDED'
) ;
return this . state . syncState === "ERROR" && ! errorIsMauError ;
} ,
2017-12-06 19:04:29 +00:00
_getUnsentMessageContent : function ( ) {
2017-11-09 15:58:15 +00:00
const unsentMessages = this . state . unsentMessages ;
if ( ! unsentMessages . length ) return null ;
let title ;
let content ;
const hasUDE = unsentMessages . some ( ( m ) => {
return m . error && m . error . name === "UnknownDeviceError" ;
} ) ;
if ( hasUDE ) {
title = _t ( "Message not sent due to unknown devices being present" ) ;
2017-11-16 15:59:16 +00:00
content = _t (
2018-01-18 09:57:29 +00:00
"<showDevicesText>Show devices</showDevicesText>, <sendAnywayText>send anyway</sendAnywayText> or <cancelText>cancel</cancelText>." ,
2017-11-16 15:59:16 +00:00
{ } ,
{
'showDevicesText' : ( sub ) => < a className = "mx_RoomStatusBar_resend_link" key = "resend" onClick = { this . _onShowDevicesClick } > { sub } < / a > ,
2018-01-09 13:52:37 +00:00
'sendAnywayText' : ( sub ) => < a className = "mx_RoomStatusBar_resend_link" key = "sendAnyway" onClick = { this . _onSendWithoutVerifyingClick } > { sub } < / a > ,
2017-11-16 15:59:16 +00:00
'cancelText' : ( sub ) => < a className = "mx_RoomStatusBar_resend_link" key = "cancel" onClick = { this . _onCancelAllClick } > { sub } < / a > ,
} ,
2017-11-09 15:58:15 +00:00
) ;
} else {
2018-06-27 14:08:56 +00:00
let consentError = null ;
2018-08-02 16:09:04 +00:00
let mauError = null ;
2018-06-27 14:08:56 +00:00
for ( const m of unsentMessages ) {
if ( m . error && m . error . errcode === 'M_CONSENT_NOT_GIVEN' ) {
consentError = m . error ;
break ;
2018-08-02 16:09:04 +00:00
} else if ( m . error && m . error . errcode === 'M_MAU_LIMIT_EXCEEDED' ) {
mauError = m . error ;
break ;
2018-06-27 14:08:56 +00:00
}
}
if ( consentError ) {
title = _t (
"You can't send any messages until you review and agree to " +
"<consentLink>our terms and conditions</consentLink>." ,
{ } ,
{
'consentLink' : ( sub ) =>
< a href = { consentError . data && consentError . data . consent _uri } target = "_blank" >
{ sub }
< / a > ,
} ,
) ;
2018-08-02 16:09:04 +00:00
} else if ( mauError ) {
title = _t ( "Your message wasn’ t sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service." ) ;
2018-06-27 14:08:56 +00:00
} else if (
2017-11-09 15:58:15 +00:00
unsentMessages . length === 1 &&
unsentMessages [ 0 ] . error &&
unsentMessages [ 0 ] . error . data &&
unsentMessages [ 0 ] . error . data . error
) {
title = unsentMessages [ 0 ] . error . data . error ;
} else {
2018-01-04 15:05:08 +00:00
title = _t ( '%(count)s of your messages have not been sent.' , { count : unsentMessages . length } ) ;
2017-11-09 15:58:15 +00:00
}
2018-01-04 15:05:08 +00:00
content = _t ( "%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. " +
2017-11-16 15:59:16 +00:00
"You can also select individual messages to resend or cancel." ,
2018-01-04 15:05:08 +00:00
{ count : unsentMessages . length } ,
2017-11-16 15:59:16 +00:00
{
'resendText' : ( sub ) =>
< a className = "mx_RoomStatusBar_resend_link" key = "resend" onClick = { this . _onResendAllClick } > { sub } < / a > ,
'cancelText' : ( sub ) =>
< a className = "mx_RoomStatusBar_resend_link" key = "cancel" onClick = { this . _onCancelAllClick } > { sub } < / a > ,
} ,
2017-11-09 15:58:15 +00:00
) ;
}
return < div className = "mx_RoomStatusBar_connectionLostBar" >
< img src = "img/warning.svg" width = "24" height = "23" title = { _t ( "Warning" ) } alt = { _t ( "Warning" ) } / >
2018-06-27 14:19:27 +00:00
< div >
< div className = "mx_RoomStatusBar_connectionLostBar_title" >
{ title }
< / d i v >
< div className = "mx_RoomStatusBar_connectionLostBar_desc" >
{ content }
< / d i v >
2017-11-09 15:58:15 +00:00
< / d i v >
< / d i v > ;
} ,
2016-02-09 14:42:32 +00:00
// return suitable content for the main (text) part of the status bar.
_getContent : function ( ) {
2016-08-11 02:25:12 +00:00
const EmojiText = sdk . getComponent ( 'elements.EmojiText' ) ;
2016-02-08 18:04:54 +00:00
2018-08-03 17:02:09 +00:00
if ( this . _shouldShowConnectionError ( ) ) {
2016-02-08 18:04:54 +00:00
return (
2016-02-09 14:42:32 +00:00
< div className = "mx_RoomStatusBar_connectionLostBar" >
2017-10-11 16:56:17 +00:00
< img src = "img/warning.svg" width = "24" height = "23" title = "/!\ " alt = "/!\ " / >
2018-06-29 08:46:01 +00:00
< div >
< div className = "mx_RoomStatusBar_connectionLostBar_title" >
{ _t ( 'Connectivity to the server has been lost.' ) }
< / d i v >
< div className = "mx_RoomStatusBar_connectionLostBar_desc" >
{ _t ( 'Sent messages will be stored until your connection has returned.' ) }
< / d i v >
2016-02-08 18:04:54 +00:00
< / d i v >
< / d i v >
) ;
}
2017-11-09 15:58:15 +00:00
if ( this . state . unsentMessages . length > 0 ) {
return this . _getUnsentMessageContent ( ) ;
2016-02-08 18:04:54 +00:00
}
// unread count trumps who is typing since the unread count is only
// set when you've scrolled up
2016-02-09 12:40:11 +00:00
if ( this . props . numUnreadMessages ) {
2017-06-07 10:40:46 +00:00
// MUST use var name "count" for pluralization to kick in
2017-10-11 16:56:17 +00:00
const unreadMsgs = _t ( "%(count)s new messages" , { count : this . props . numUnreadMessages } ) ;
2016-02-08 18:04:54 +00:00
return (
2016-02-09 14:42:32 +00:00
< div className = "mx_RoomStatusBar_unreadMessagesBar"
2017-10-11 16:56:17 +00:00
onClick = { this . props . onScrollToBottomClick } >
{ unreadMsgs }
2016-02-08 18:04:54 +00:00
< / d i v >
) ;
}
2017-02-09 10:01:51 +00:00
const typingString = WhoIsTyping . whoIsTypingString (
this . state . usersTyping ,
2017-10-11 16:56:17 +00:00
this . props . whoIsTypingLimit ,
2017-02-09 10:01:51 +00:00
) ;
2016-02-08 18:04:54 +00:00
if ( typingString ) {
return (
2016-02-09 14:42:32 +00:00
< div className = "mx_RoomStatusBar_typingBar" >
2017-10-11 16:56:17 +00:00
< EmojiText > { typingString } < / E m o j i T e x t >
2016-02-08 18:04:54 +00:00
< / d i v >
) ;
}
if ( this . props . hasActiveCall ) {
return (
2016-02-09 14:42:32 +00:00
< div className = "mx_RoomStatusBar_callBar" >
2017-10-11 16:56:17 +00:00
< b > { _t ( 'Active call' ) } < / b >
2016-02-08 18:04:54 +00:00
< / d i v >
) ;
}
2017-10-12 03:15:00 +00:00
// If you're alone in the room, and have sent a message, suggest to invite someone
if ( this . props . sentMessageAndIsAlone ) {
return (
< div className = "mx_RoomStatusBar_isAlone" >
2017-11-13 19:19:33 +00:00
{ _t ( "There's no one else here! Would you like to <inviteText>invite others</inviteText> " +
"or <nowarnText>stop warning about the empty room</nowarnText>?" ,
{ } ,
{
'inviteText' : ( sub ) =>
< a className = "mx_RoomStatusBar_resend_link" key = "invite" onClick = { this . props . onInviteClick } > { sub } < / a > ,
'nowarnText' : ( sub ) =>
< a className = "mx_RoomStatusBar_resend_link" key = "nowarn" onClick = { this . props . onStopWarningClick } > { sub } < / a > ,
} ,
2017-10-12 03:15:00 +00:00
) }
< / d i v >
) ;
}
2016-02-09 14:42:32 +00:00
return null ;
2016-02-08 18:04:54 +00:00
} ,
2016-02-09 14:42:32 +00:00
render : function ( ) {
2017-10-11 16:56:17 +00:00
const content = this . _getContent ( ) ;
const indicator = this . _getIndicator ( this . state . usersTyping . length > 0 ) ;
2016-02-09 14:42:32 +00:00
return (
< div className = "mx_RoomStatusBar" >
< div className = "mx_RoomStatusBar_indicator" >
2017-10-11 16:56:17 +00:00
{ indicator }
2016-02-09 14:42:32 +00:00
< / d i v >
2017-10-11 16:56:17 +00:00
{ content }
2016-02-09 14:42:32 +00:00
< / d i v >
) ;
2016-07-15 15:10:27 +00:00
} ,
2016-02-08 18:04:54 +00:00
} ) ;