2015-09-18 20:39:16 +03:00
/ *
2016-01-07 07:06:39 +03:00
Copyright 2015 , 2016 OpenMarket Ltd
2018-03-19 19:47:12 +03:00
Copyright 2017 , 2018 Vector Creations Ltd
2015-09-18 20:39:16 +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 .
* /
/ *
* State vars :
* 'can' : {
* kick : boolean ,
* ban : boolean ,
* mute : boolean ,
* modifyLevel : boolean
* } ,
* 'muted' : boolean ,
* 'isTargetMod' : boolean
* /
2017-02-14 16:40:19 +03:00
import React from 'react' ;
2017-12-26 04:03:18 +03:00
import PropTypes from 'prop-types' ;
2017-02-14 16:40:19 +03:00
import classNames from 'classnames' ;
import dis from '../../../dispatcher' ;
import Modal from '../../../Modal' ;
import sdk from '../../../index' ;
2017-05-25 13:39:08 +03:00
import { _t } from '../../../languageHandler' ;
2017-02-14 16:40:19 +03:00
import createRoom from '../../../createRoom' ;
import DMRoomMap from '../../../utils/DMRoomMap' ;
import Unread from '../../../Unread' ;
2017-02-14 17:33:21 +03:00
import { findReadReceiptFromUserId } from '../../../utils/Receipt' ;
2017-07-07 13:34:20 +03:00
import withMatrixClient from '../../../wrappers/withMatrixClient' ;
2017-01-25 01:41:52 +03:00
import AccessibleButton from '../elements/AccessibleButton' ;
2017-10-25 02:58:16 +03:00
import RoomViewStore from '../../../stores/RoomViewStore' ;
2018-03-19 19:47:12 +03:00
import SdkConfig from '../../../SdkConfig' ;
2017-06-08 19:26:40 +03:00
2017-07-07 13:34:20 +03:00
module . exports = withMatrixClient ( React . createClass ( {
2015-11-26 20:49:39 +03:00
displayName : 'MemberInfo' ,
2015-09-18 20:39:16 +03:00
2016-06-08 15:13:41 +03:00
propTypes : {
2017-12-26 04:03:18 +03:00
matrixClient : PropTypes . object . isRequired ,
member : PropTypes . object . isRequired ,
2015-11-30 17:14:30 +03:00
} ,
2016-06-08 15:13:41 +03:00
getInitialState : function ( ) {
return {
can : {
kick : false ,
ban : false ,
mute : false ,
2017-10-11 19:56:17 +03:00
modifyLevel : false ,
2016-06-08 15:13:41 +03:00
} ,
muted : false ,
isTargetMod : false ,
updating : 0 ,
2016-06-09 00:54:48 +03:00
devicesLoading : true ,
devices : null ,
2017-09-15 02:10:02 +03:00
isIgnoring : false ,
2017-01-20 17:22:27 +03:00
} ;
2015-09-18 20:39:16 +03:00
} ,
2016-06-08 15:13:41 +03:00
componentWillMount : function ( ) {
this . _cancelDeviceList = null ;
2016-07-17 21:41:53 +03:00
2016-11-21 13:25:48 +03:00
// only display the devices list if our client supports E2E
this . _enableDevices = this . props . matrixClient . isCryptoEnabled ( ) ;
2016-09-09 18:59:59 +03:00
2016-11-03 21:55:09 +03:00
const cli = this . props . matrixClient ;
2016-09-09 18:59:59 +03:00
cli . on ( "deviceVerificationChanged" , this . onDeviceVerificationChanged ) ;
cli . on ( "Room" , this . onRoom ) ;
cli . on ( "deleteRoom" , this . onDeleteRoom ) ;
cli . on ( "Room.timeline" , this . onRoomTimeline ) ;
cli . on ( "Room.name" , this . onRoomName ) ;
cli . on ( "Room.receipt" , this . onRoomReceipt ) ;
cli . on ( "RoomState.events" , this . onRoomStateEvents ) ;
cli . on ( "RoomMember.name" , this . onRoomMemberName ) ;
2017-10-25 02:58:16 +03:00
cli . on ( "RoomMember.membership" , this . onRoomMemberMembership ) ;
2016-09-09 18:59:59 +03:00
cli . on ( "accountData" , this . onAccountData ) ;
2017-09-15 02:10:02 +03:00
this . _checkIgnoreState ( ) ;
2016-06-08 15:13:41 +03:00
} ,
componentDidMount : function ( ) {
this . _updateStateForNewMember ( this . props . member ) ;
} ,
2015-12-04 19:15:55 +03:00
componentWillReceiveProps : function ( newProps ) {
2017-10-25 00:21:33 +03:00
if ( this . props . member . userId !== newProps . member . userId ) {
2016-06-08 15:13:41 +03:00
this . _updateStateForNewMember ( newProps . member ) ;
}
} ,
componentWillUnmount : function ( ) {
2017-10-11 19:56:17 +03:00
const client = this . props . matrixClient ;
2016-06-08 23:25:42 +03:00
if ( client ) {
2016-06-23 19:27:23 +03:00
client . removeListener ( "deviceVerificationChanged" , this . onDeviceVerificationChanged ) ;
2016-09-09 18:59:59 +03:00
client . removeListener ( "Room" , this . onRoom ) ;
client . removeListener ( "deleteRoom" , this . onDeleteRoom ) ;
client . removeListener ( "Room.timeline" , this . onRoomTimeline ) ;
client . removeListener ( "Room.name" , this . onRoomName ) ;
client . removeListener ( "Room.receipt" , this . onRoomReceipt ) ;
client . removeListener ( "RoomState.events" , this . onRoomStateEvents ) ;
client . removeListener ( "RoomMember.name" , this . onRoomMemberName ) ;
2017-10-25 02:58:16 +03:00
client . removeListener ( "RoomMember.membership" , this . onRoomMemberMembership ) ;
2016-09-09 18:59:59 +03:00
client . removeListener ( "accountData" , this . onAccountData ) ;
2016-06-08 23:25:42 +03:00
}
2016-06-08 15:13:41 +03:00
if ( this . _cancelDeviceList ) {
this . _cancelDeviceList ( ) ;
}
} ,
2017-09-15 02:10:02 +03:00
_checkIgnoreState : function ( ) {
2017-09-15 05:16:56 +03:00
const isIgnoring = this . props . matrixClient . isUserIgnored ( this . props . member . userId ) ;
2017-09-15 02:10:02 +03:00
this . setState ( { isIgnoring : isIgnoring } ) ;
} ,
2016-09-17 22:12:56 +03:00
_disambiguateDevices : function ( devices ) {
2017-10-11 19:56:17 +03:00
const names = Object . create ( null ) ;
for ( let i = 0 ; i < devices . length ; i ++ ) {
2017-10-25 00:21:33 +03:00
const name = devices [ i ] . getDisplayName ( ) ;
2017-10-11 19:56:17 +03:00
const indexList = names [ name ] || [ ] ;
2016-09-17 22:12:56 +03:00
indexList . push ( i ) ;
names [ name ] = indexList ;
}
2017-10-25 00:21:33 +03:00
for ( const name in names ) {
2016-09-17 22:12:56 +03:00
if ( names [ name ] . length > 1 ) {
names [ name ] . forEach ( ( j ) => {
devices [ j ] . ambiguous = true ;
} ) ;
}
}
} ,
2016-06-23 19:27:23 +03:00
onDeviceVerificationChanged : function ( userId , device ) {
2016-08-17 11:57:06 +03:00
if ( ! this . _enableDevices ) {
return ;
}
2017-10-25 00:21:33 +03:00
if ( userId === this . props . member . userId ) {
2016-06-08 23:25:42 +03:00
// no need to re-download the whole thing; just update our copy of
// the list.
2017-07-19 01:46:03 +03:00
// Promise.resolve to handle transition from static result to promise; can be removed
// in future
Promise . resolve ( this . props . matrixClient . getStoredDevicesForUser ( userId ) ) . then ( ( devices ) => {
this . setState ( { devices : devices } ) ;
} ) ;
2016-06-08 20:35:43 +03:00
}
} ,
2016-09-09 18:59:59 +03:00
onRoom : function ( room ) {
this . forceUpdate ( ) ;
} ,
onDeleteRoom : function ( roomId ) {
this . forceUpdate ( ) ;
} ,
onRoomTimeline : function ( ev , room , toStartOfTimeline ) {
if ( toStartOfTimeline ) return ;
this . forceUpdate ( ) ;
} ,
onRoomName : function ( room ) {
this . forceUpdate ( ) ;
} ,
onRoomReceipt : function ( receiptEvent , room ) {
// because if we read a notification, it will affect notification count
// only bother updating if there's a receipt from us
2017-02-14 16:58:29 +03:00
if ( findReadReceiptFromUserId ( receiptEvent , this . props . matrixClient . credentials . userId ) ) {
2016-09-09 18:59:59 +03:00
this . forceUpdate ( ) ;
}
} ,
onRoomStateEvents : function ( ev , state ) {
this . forceUpdate ( ) ;
} ,
onRoomMemberName : function ( ev , member ) {
this . forceUpdate ( ) ;
} ,
2017-10-25 02:58:16 +03:00
onRoomMemberMembership : function ( ev , member ) {
if ( this . props . member . userId === member . userId ) this . forceUpdate ( ) ;
} ,
2016-09-09 18:59:59 +03:00
onAccountData : function ( ev ) {
2017-10-25 00:21:33 +03:00
if ( ev . getType ( ) === 'm.direct' ) {
2016-09-09 18:59:59 +03:00
this . forceUpdate ( ) ;
}
} ,
2016-06-08 15:13:41 +03:00
_updateStateForNewMember : function ( member ) {
2017-10-11 19:56:17 +03:00
const newState = this . _calculateOpsPermissions ( member ) ;
2016-06-09 00:54:48 +03:00
newState . devicesLoading = true ;
2016-06-08 15:13:41 +03:00
newState . devices = null ;
this . setState ( newState ) ;
if ( this . _cancelDeviceList ) {
this . _cancelDeviceList ( ) ;
this . _cancelDeviceList = null ;
}
this . _downloadDeviceList ( member ) ;
} ,
_downloadDeviceList : function ( member ) {
2016-08-17 11:57:06 +03:00
if ( ! this . _enableDevices ) {
return ;
}
2017-10-11 19:56:17 +03:00
let cancelled = false ;
2017-01-20 17:22:27 +03:00
this . _cancelDeviceList = function ( ) { cancelled = true ; } ;
2016-06-08 15:13:41 +03:00
2017-10-11 19:56:17 +03:00
const client = this . props . matrixClient ;
const self = this ;
2017-07-19 01:46:03 +03:00
client . downloadKeys ( [ member . userId ] , true ) . then ( ( ) => {
return client . getStoredDevicesForUser ( member . userId ) ;
} ) . finally ( function ( ) {
2016-06-09 00:54:48 +03:00
self . _cancelDeviceList = null ;
2017-07-19 01:46:03 +03:00
} ) . done ( function ( devices ) {
2016-06-08 15:13:41 +03:00
if ( cancelled ) {
// we got cancelled - presumably a different user now
return ;
}
2016-09-17 22:12:56 +03:00
self . _disambiguateDevices ( devices ) ;
2016-06-09 00:54:48 +03:00
self . setState ( { devicesLoading : false , devices : devices } ) ;
} , function ( err ) {
console . log ( "Error downloading devices" , err ) ;
self . setState ( { devicesLoading : false } ) ;
2016-06-08 15:13:41 +03:00
} ) ;
2015-12-04 19:15:55 +03:00
} ,
2017-09-15 02:10:02 +03:00
onIgnoreToggle : function ( ) {
const ignoredUsers = this . props . matrixClient . getIgnoredUsers ( ) ;
if ( this . state . isIgnoring ) {
const index = ignoredUsers . indexOf ( this . props . member . userId ) ;
if ( index !== - 1 ) ignoredUsers . splice ( index , 1 ) ;
} else {
ignoredUsers . push ( this . props . member . userId ) ;
}
2017-10-25 00:21:33 +03:00
this . props . matrixClient . setIgnoredUsers ( ignoredUsers ) . then ( ( ) => {
return this . setState ( { isIgnoring : ! this . state . isIgnoring } ) ;
} ) ;
2017-09-15 02:10:02 +03:00
} ,
2015-09-18 20:39:16 +03:00
onKick : function ( ) {
2017-03-27 18:53:00 +03:00
const membership = this . props . member . membership ;
2017-02-14 16:40:19 +03:00
const ConfirmUserActionDialog = sdk . getComponent ( "dialogs.ConfirmUserActionDialog" ) ;
2017-07-27 19:19:18 +03:00
Modal . createTrackedDialog ( 'Confirm User Action Dialog' , 'onKick' , ConfirmUserActionDialog , {
2017-02-14 16:40:19 +03:00
member : this . props . member ,
2017-10-12 22:37:12 +03:00
action : membership === "invite" ? _t ( "Disinvite" ) : _t ( "Kick" ) ,
title : membership === "invite" ? _t ( "Disinvite this user?" ) : _t ( "Kick this user?" ) ,
2017-10-25 00:21:33 +03:00
askReason : membership === "join" ,
2017-02-14 16:40:19 +03:00
danger : true ,
2017-02-17 20:27:46 +03:00
onFinished : ( proceed , reason ) => {
2017-02-14 16:40:19 +03:00
if ( ! proceed ) return ;
this . setState ( { updating : this . state . updating + 1 } ) ;
this . props . matrixClient . kick (
this . props . member . roomId , this . props . member . userId ,
2017-10-11 19:56:17 +03:00
reason || undefined ,
2017-02-14 16:40:19 +03:00
) . then ( function ( ) {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console . log ( "Kick success" ) ;
} , function ( err ) {
const ErrorDialog = sdk . getComponent ( "dialogs.ErrorDialog" ) ;
2017-03-13 01:59:41 +03:00
console . error ( "Kick error: " + err ) ;
2017-08-10 17:17:52 +03:00
Modal . createTrackedDialog ( 'Failed to kick' , '' , ErrorDialog , {
2017-05-23 17:16:31 +03:00
title : _t ( "Failed to kick" ) ,
2017-04-23 03:48:27 +03:00
description : ( ( err && err . message ) ? err . message : "Operation failed" ) ,
2017-02-14 16:40:19 +03:00
} ) ;
2017-10-11 19:56:17 +03:00
} ,
2017-02-14 16:40:19 +03:00
) . finally ( ( ) => {
this . setState ( { updating : this . state . updating - 1 } ) ;
2016-01-18 16:38:40 +03:00
} ) ;
2017-10-11 19:56:17 +03:00
} ,
2016-04-13 03:46:10 +03:00
} ) ;
2015-09-18 20:39:16 +03:00
} ,
2017-02-14 20:29:40 +03:00
onBanOrUnban : function ( ) {
2017-02-14 16:40:19 +03:00
const ConfirmUserActionDialog = sdk . getComponent ( "dialogs.ConfirmUserActionDialog" ) ;
2017-07-27 19:19:18 +03:00
Modal . createTrackedDialog ( 'Confirm User Action Dialog' , 'onBanOrUnban' , ConfirmUserActionDialog , {
2017-02-14 16:40:19 +03:00
member : this . props . member ,
2017-10-25 00:21:33 +03:00
action : this . props . member . membership === 'ban' ? _t ( "Unban" ) : _t ( "Ban" ) ,
2017-10-25 19:19:27 +03:00
title : this . props . member . membership === 'ban' ? _t ( "Unban this user?" ) : _t ( "Ban this user?" ) ,
2017-10-25 00:21:33 +03:00
askReason : this . props . member . membership !== 'ban' ,
danger : this . props . member . membership !== 'ban' ,
2017-02-17 20:27:46 +03:00
onFinished : ( proceed , reason ) => {
2017-02-14 16:40:19 +03:00
if ( ! proceed ) return ;
this . setState ( { updating : this . state . updating + 1 } ) ;
2017-02-14 19:03:30 +03:00
let promise ;
2017-10-25 00:21:33 +03:00
if ( this . props . member . membership === 'ban' ) {
2017-02-14 19:03:30 +03:00
promise = this . props . matrixClient . unban (
this . props . member . roomId , this . props . member . userId ,
) ;
} else {
promise = this . props . matrixClient . ban (
this . props . member . roomId , this . props . member . userId ,
2017-10-11 19:56:17 +03:00
reason || undefined ,
2017-02-14 19:03:30 +03:00
) ;
}
promise . then (
2017-02-14 16:40:19 +03:00
function ( ) {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console . log ( "Ban success" ) ;
} , function ( err ) {
const ErrorDialog = sdk . getComponent ( "dialogs.ErrorDialog" ) ;
2017-03-13 01:59:41 +03:00
console . error ( "Ban error: " + err ) ;
2017-08-10 17:17:52 +03:00
Modal . createTrackedDialog ( 'Failed to ban user' , '' , ErrorDialog , {
2017-05-23 17:16:31 +03:00
title : _t ( "Error" ) ,
description : _t ( "Failed to ban user" ) ,
2017-02-14 16:40:19 +03:00
} ) ;
2017-10-11 19:56:17 +03:00
} ,
2017-02-14 16:40:19 +03:00
) . finally ( ( ) => {
this . setState ( { updating : this . state . updating - 1 } ) ;
2016-01-18 16:38:40 +03:00
} ) ;
2017-02-14 16:40:19 +03:00
} ,
2016-04-13 03:46:10 +03:00
} ) ;
2015-09-18 20:39:16 +03:00
} ,
onMuteToggle : function ( ) {
2017-10-11 19:56:17 +03:00
const ErrorDialog = sdk . getComponent ( "dialogs.ErrorDialog" ) ;
const roomId = this . props . member . roomId ;
const target = this . props . member . userId ;
const room = this . props . matrixClient . getRoom ( roomId ) ;
2017-10-25 00:21:33 +03:00
if ( ! room ) return ;
const powerLevelEvent = room . currentState . getStateEvents ( "m.room.power_levels" , "" ) ;
if ( ! powerLevelEvent ) return ;
2017-10-11 19:56:17 +03:00
const isMuted = this . state . muted ;
const powerLevels = powerLevelEvent . getContent ( ) ;
const levelToSend = (
2015-09-18 20:39:16 +03:00
( powerLevels . events ? powerLevels . events [ "m.room.message" ] : null ) ||
powerLevels . events _default
) ;
2017-10-11 19:56:17 +03:00
let level ;
2015-09-18 20:39:16 +03:00
if ( isMuted ) { // unmute
level = levelToSend ;
2017-10-11 19:56:17 +03:00
} else { // mute
2015-09-18 20:39:16 +03:00
level = levelToSend - 1 ;
}
2016-01-22 18:29:57 +03:00
level = parseInt ( level ) ;
2015-09-18 20:39:16 +03:00
2017-10-25 00:21:33 +03:00
if ( ! isNaN ( level ) ) {
2016-04-13 03:46:10 +03:00
this . setState ( { updating : this . state . updating + 1 } ) ;
2016-11-03 21:55:09 +03:00
this . props . matrixClient . setPowerLevel ( roomId , target , level , powerLevelEvent ) . then (
2016-01-22 18:29:57 +03:00
function ( ) {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console . log ( "Mute toggle success" ) ;
} , function ( err ) {
2017-03-13 01:59:41 +03:00
console . error ( "Mute error: " + err ) ;
2017-08-10 17:17:52 +03:00
Modal . createTrackedDialog ( 'Failed to mute user' , '' , ErrorDialog , {
2017-05-23 17:16:31 +03:00
title : _t ( "Error" ) ,
description : _t ( "Failed to mute user" ) ,
2016-01-22 18:29:57 +03:00
} ) ;
2017-10-11 19:56:17 +03:00
} ,
2016-04-13 03:46:10 +03:00
) . finally ( ( ) => {
this . setState ( { updating : this . state . updating - 1 } ) ;
} ) ;
2016-01-22 18:29:57 +03:00
}
2015-09-18 20:39:16 +03:00
} ,
onModToggle : function ( ) {
2017-10-11 19:56:17 +03:00
const ErrorDialog = sdk . getComponent ( "dialogs.ErrorDialog" ) ;
const roomId = this . props . member . roomId ;
const target = this . props . member . userId ;
const room = this . props . matrixClient . getRoom ( roomId ) ;
2017-10-25 00:21:33 +03:00
if ( ! room ) return ;
const powerLevelEvent = room . currentState . getStateEvents ( "m.room.power_levels" , "" ) ;
if ( ! powerLevelEvent ) return ;
2017-10-11 19:56:17 +03:00
const me = room . getMember ( this . props . matrixClient . credentials . userId ) ;
2017-10-25 00:21:33 +03:00
if ( ! me ) return ;
2017-10-11 19:56:17 +03:00
const defaultLevel = powerLevelEvent . getContent ( ) . users _default ;
let modLevel = me . powerLevel - 1 ;
2016-01-18 04:18:02 +03:00
if ( modLevel > 50 && defaultLevel < 50 ) modLevel = 50 ; // try to stick with the vector level defaults
2015-09-18 20:39:16 +03:00
// toggle the level
2017-10-11 19:56:17 +03:00
const newLevel = this . state . isTargetMod ? defaultLevel : modLevel ;
2016-04-13 03:46:10 +03:00
this . setState ( { updating : this . state . updating + 1 } ) ;
2016-11-03 21:55:09 +03:00
this . props . matrixClient . setPowerLevel ( roomId , target , parseInt ( newLevel ) , powerLevelEvent ) . then (
2016-01-18 16:38:40 +03:00
function ( ) {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console . log ( "Mod toggle success" ) ;
} , function ( err ) {
2017-10-25 00:21:33 +03:00
if ( err . errcode === 'M_GUEST_ACCESS_FORBIDDEN' ) {
2017-05-15 19:31:26 +03:00
dis . dispatch ( { action : 'view_set_mxid' } ) ;
2016-03-22 16:19:29 +03:00
} else {
2017-03-13 01:59:41 +03:00
console . error ( "Toggle moderator error:" + err ) ;
2017-08-10 17:17:52 +03:00
Modal . createTrackedDialog ( 'Failed to toggle moderator status' , '' , ErrorDialog , {
2017-05-23 17:16:31 +03:00
title : _t ( "Error" ) ,
description : _t ( "Failed to toggle moderator status" ) ,
2016-03-22 16:19:29 +03:00
} ) ;
}
2017-10-11 19:56:17 +03:00
} ,
2016-04-13 03:46:10 +03:00
) . finally ( ( ) => {
this . setState ( { updating : this . state . updating - 1 } ) ;
} ) ;
2015-09-18 20:39:16 +03:00
} ,
2016-03-21 03:49:18 +03:00
_applyPowerChange : function ( roomId , target , powerLevel , powerLevelEvent ) {
2016-04-13 03:46:10 +03:00
this . setState ( { updating : this . state . updating + 1 } ) ;
2016-11-03 21:55:09 +03:00
this . props . matrixClient . setPowerLevel ( roomId , target , parseInt ( powerLevel ) , powerLevelEvent ) . then (
2016-03-21 03:49:18 +03:00
function ( ) {
// NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here!
console . log ( "Power change success" ) ;
} , function ( err ) {
2017-01-13 18:17:34 +03:00
const ErrorDialog = sdk . getComponent ( "dialogs.ErrorDialog" ) ;
2017-03-13 01:59:41 +03:00
console . error ( "Failed to change power level " + err ) ;
2017-08-10 17:17:52 +03:00
Modal . createTrackedDialog ( 'Failed to change power level' , '' , ErrorDialog , {
2017-05-23 17:16:31 +03:00
title : _t ( "Error" ) ,
description : _t ( "Failed to change power level" ) ,
2016-03-21 03:49:18 +03:00
} ) ;
2017-10-11 19:56:17 +03:00
} ,
2016-04-13 03:46:10 +03:00
) . finally ( ( ) => {
this . setState ( { updating : this . state . updating - 1 } ) ;
2017-01-13 18:17:34 +03:00
} ) . done ( ) ;
2016-03-21 03:49:18 +03:00
} ,
2016-01-18 04:18:02 +03:00
onPowerChange : function ( powerLevel ) {
2017-10-11 19:56:17 +03:00
const roomId = this . props . member . roomId ;
const target = this . props . member . userId ;
const room = this . props . matrixClient . getRoom ( roomId ) ;
2018-01-03 02:15:36 +03:00
if ( ! room ) return ;
const powerLevelEvent = room . currentState . getStateEvents ( "m.room.power_levels" , "" ) ;
if ( ! powerLevelEvent ) return ;
2018-01-05 14:11:20 +03:00
if ( ! powerLevelEvent . getContent ( ) . users ) {
this . _applyPowerChange ( roomId , target , powerLevel , powerLevelEvent ) ;
2016-01-18 04:18:02 +03:00
return ;
}
2018-01-03 02:15:36 +03:00
2018-01-05 14:11:20 +03:00
const myUserId = this . props . matrixClient . getUserId ( ) ;
const QuestionDialog = sdk . getComponent ( "dialogs.QuestionDialog" ) ;
2018-01-05 14:55:43 +03:00
// If we are changing our own PL it can only ever be decreasing, which we cannot reverse.
2018-01-05 14:11:20 +03:00
if ( myUserId === target ) {
Modal . createTrackedDialog ( 'Demoting Self' , '' , QuestionDialog , {
title : _t ( "Warning!" ) ,
description :
< div >
{ _t ( "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges." ) } < br / >
{ _t ( "Are you sure?" ) }
< / d i v > ,
button : _t ( "Continue" ) ,
onFinished : ( confirmed ) => {
if ( confirmed ) {
this . _applyPowerChange ( roomId , target , powerLevel , powerLevelEvent ) ;
}
} ,
} ) ;
2016-01-18 04:18:02 +03:00
return ;
}
2018-01-03 02:15:36 +03:00
2018-01-05 14:11:20 +03:00
const myPower = powerLevelEvent . getContent ( ) . users [ myUserId ] ;
if ( parseInt ( myPower ) === parseInt ( powerLevel ) ) {
Modal . createTrackedDialog ( 'Promote to PL100 Warning' , '' , QuestionDialog , {
title : _t ( "Warning!" ) ,
description :
< div >
{ _t ( "You will not be able to undo this change as you are promoting the user to have the same power level as yourself." ) } < br / >
{ _t ( "Are you sure?" ) }
< / d i v > ,
button : _t ( "Continue" ) ,
onFinished : ( confirmed ) => {
if ( confirmed ) {
this . _applyPowerChange ( roomId , target , powerLevel , powerLevelEvent ) ;
}
} ,
} ) ;
return ;
2016-03-21 03:49:18 +03:00
}
2018-01-03 02:15:36 +03:00
this . _applyPowerChange ( roomId , target , powerLevel , powerLevelEvent ) ;
2016-04-13 03:46:10 +03:00
} ,
2016-01-18 04:18:02 +03:00
2016-09-09 18:15:01 +03:00
onNewDMClick : function ( ) {
this . setState ( { updating : this . state . updating + 1 } ) ;
2016-09-09 21:25:00 +03:00
createRoom ( { dmUserId : this . props . member . userId } ) . finally ( ( ) => {
2016-09-09 18:15:01 +03:00
this . setState ( { updating : this . state . updating - 1 } ) ;
} ) . done ( ) ;
2015-09-22 18:37:39 +03:00
} ,
onLeaveClick : function ( ) {
2015-12-13 16:49:28 +03:00
dis . dispatch ( {
action : 'leave_room' ,
room _id : this . props . member . roomId ,
2015-09-22 18:37:39 +03:00
} ) ;
2015-09-18 20:39:16 +03:00
} ,
2015-12-04 19:15:55 +03:00
_calculateOpsPermissions : function ( member ) {
2017-02-14 19:03:30 +03:00
const defaultPerms = {
2015-09-18 20:39:16 +03:00
can : { } ,
muted : false ,
} ;
2017-02-14 19:03:30 +03:00
const room = this . props . matrixClient . getRoom ( member . roomId ) ;
2017-10-25 00:21:33 +03:00
if ( ! room ) return defaultPerms ;
const powerLevels = room . currentState . getStateEvents ( "m.room.power_levels" , "" ) ;
if ( ! powerLevels ) return defaultPerms ;
2017-02-14 19:03:30 +03:00
const me = room . getMember ( this . props . matrixClient . credentials . userId ) ;
2017-10-25 00:21:33 +03:00
if ( ! me ) return defaultPerms ;
2017-02-14 19:03:30 +03:00
const them = member ;
2015-09-18 20:39:16 +03:00
return {
can : this . _calculateCanPermissions (
2017-10-11 19:56:17 +03:00
me , them , powerLevels . getContent ( ) ,
2015-09-18 20:39:16 +03:00
) ,
muted : this . _isMuted ( them , powerLevels . getContent ( ) ) ,
2017-10-11 19:56:17 +03:00
isTargetMod : them . powerLevel > powerLevels . getContent ( ) . users _default ,
2015-09-18 20:39:16 +03:00
} ;
} ,
_calculateCanPermissions : function ( me , them , powerLevels ) {
2017-11-13 15:08:53 +03:00
const isMe = me . userId === them . userId ;
2017-02-14 19:03:30 +03:00
const can = {
2015-09-18 20:39:16 +03:00
kick : false ,
ban : false ,
mute : false ,
2017-10-11 19:56:17 +03:00
modifyLevel : false ,
2017-11-13 15:08:53 +03:00
modifyLevelMax : 0 ,
2015-09-18 20:39:16 +03:00
} ;
2017-11-13 15:08:53 +03:00
const canAffectUser = them . powerLevel < me . powerLevel || isMe ;
2015-09-18 20:39:16 +03:00
if ( ! canAffectUser ) {
//console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
return can ;
}
2017-02-14 19:03:30 +03:00
const editPowerLevel = (
2015-09-18 20:39:16 +03:00
( powerLevels . events ? powerLevels . events [ "m.room.power_levels" ] : null ) ||
powerLevels . state _default
) ;
2016-01-18 04:40:19 +03:00
2015-09-18 20:39:16 +03:00
can . kick = me . powerLevel >= powerLevels . kick ;
can . ban = me . powerLevel >= powerLevels . ban ;
can . mute = me . powerLevel >= editPowerLevel ;
2017-11-13 15:08:53 +03:00
can . modifyLevel = me . powerLevel >= editPowerLevel && ( isMe || me . powerLevel > them . powerLevel ) ;
can . modifyLevelMax = me . powerLevel ;
2015-09-18 20:39:16 +03:00
return can ;
} ,
_isMuted : function ( member , powerLevelContent ) {
2017-10-25 00:21:33 +03:00
if ( ! powerLevelContent || ! member ) return false ;
2017-10-11 19:56:17 +03:00
const levelToSend = (
2015-09-18 20:39:16 +03:00
( powerLevelContent . events ? powerLevelContent . events [ "m.room.message" ] : null ) ||
powerLevelContent . events _default
) ;
return member . powerLevel < levelToSend ;
2015-11-26 20:49:39 +03:00
} ,
onCancel : function ( e ) {
dis . dispatch ( {
action : "view_user" ,
2017-10-11 19:56:17 +03:00
member : null ,
2015-11-26 20:49:39 +03:00
} ) ;
} ,
2017-01-20 17:22:27 +03:00
onMemberAvatarClick : function ( ) {
2017-10-25 00:21:33 +03:00
const member = this . props . member ;
const avatarUrl = member . user ? member . user . avatarUrl : member . events . member . getContent ( ) . avatar _url ;
2017-11-16 16:19:36 +03:00
if ( ! avatarUrl ) return ;
2016-04-04 02:18:18 +03:00
2017-10-11 19:56:17 +03:00
const httpUrl = this . props . matrixClient . mxcUrlToHttp ( avatarUrl ) ;
const ImageView = sdk . getComponent ( "elements.ImageView" ) ;
const params = {
2016-04-02 22:24:23 +03:00
src : httpUrl ,
2017-10-25 00:21:33 +03:00
name : member . name ,
2016-04-02 22:24:23 +03:00
} ;
Modal . createDialog ( ImageView , params , "mx_Dialog_lightbox" ) ;
} ,
2017-03-06 20:44:29 +03:00
onRoomTileClick ( roomId ) {
dis . dispatch ( {
action : 'view_room' ,
room _id : roomId ,
} ) ;
} ,
2016-06-08 15:13:41 +03:00
_renderDevices : function ( ) {
2017-10-25 00:21:33 +03:00
if ( ! this . _enableDevices ) return null ;
2016-06-14 13:57:08 +03:00
2017-10-11 19:56:17 +03:00
const devices = this . state . devices ;
const MemberDeviceInfo = sdk . getComponent ( 'rooms.MemberDeviceInfo' ) ;
const Spinner = sdk . getComponent ( "elements.Spinner" ) ;
2016-06-08 15:13:41 +03:00
2017-10-11 19:56:17 +03:00
let devComponents ;
2016-06-09 00:54:48 +03:00
if ( this . state . devicesLoading ) {
2016-06-08 15:13:41 +03:00
// still loading
devComponents = < Spinner / > ;
2016-06-09 00:54:48 +03:00
} else if ( devices === null ) {
2017-05-23 17:16:31 +03:00
devComponents = _t ( "Unable to load device list" ) ;
2016-06-09 00:54:48 +03:00
} else if ( devices . length === 0 ) {
2017-05-23 17:16:31 +03:00
devComponents = _t ( "No devices with registered encryption keys" ) ;
2016-06-08 15:13:41 +03:00
} else {
devComponents = [ ] ;
2017-10-11 19:56:17 +03:00
for ( let i = 0 ; i < devices . length ; i ++ ) {
2016-06-08 15:13:41 +03:00
devComponents . push ( < MemberDeviceInfo key = { i }
userId = { this . props . member . userId }
2017-10-11 19:56:17 +03:00
device = { devices [ i ] } / > ) ;
2016-06-08 15:13:41 +03:00
}
}
return (
< div >
2017-05-23 17:16:31 +03:00
< h3 > { _t ( "Devices" ) } < / h 3 >
2016-06-23 19:27:23 +03:00
< div className = "mx_MemberInfo_devices" >
2017-10-11 19:56:17 +03:00
{ devComponents }
2016-06-23 19:27:23 +03:00
< / d i v >
2016-06-08 15:13:41 +03:00
< / d i v >
) ;
} ,
2017-09-15 02:10:02 +03:00
_renderUserOptions : function ( ) {
2017-10-07 21:25:13 +03:00
const cli = this . props . matrixClient ;
const member = this . props . member ;
2017-09-15 02:10:02 +03:00
let ignoreButton = null ;
2017-10-25 01:01:40 +03:00
let insertPillButton = null ;
2017-10-25 02:58:16 +03:00
let inviteUserButton = null ;
2017-10-07 21:25:13 +03:00
let readReceiptButton = null ;
// Only allow the user to ignore the user if its not ourselves
// same goes for jumping to read receipt
if ( member . userId !== cli . getUserId ( ) ) {
2017-09-15 02:10:02 +03:00
ignoreButton = (
< AccessibleButton onClick = { this . onIgnoreToggle } className = "mx_MemberInfo_field" >
2017-10-11 19:56:17 +03:00
{ this . state . isIgnoring ? _t ( "Unignore" ) : _t ( "Ignore" ) }
2017-09-15 02:10:02 +03:00
< / A c c e s s i b l e B u t t o n >
) ;
2017-10-07 21:25:13 +03:00
if ( member . roomId ) {
const room = cli . getRoom ( member . roomId ) ;
const eventId = room . getEventReadUpTo ( member . userId ) ;
const onReadReceiptButton = function ( ) {
dis . dispatch ( {
action : 'view_room' ,
highlighted : true ,
event _id : eventId ,
room _id : member . roomId ,
} ) ;
} ;
2017-10-25 01:01:40 +03:00
const onInsertPillButton = function ( ) {
dis . dispatch ( {
action : 'insert_mention' ,
user _id : member . userId ,
} ) ;
} ;
2017-10-07 21:25:13 +03:00
readReceiptButton = (
< AccessibleButton onClick = { onReadReceiptButton } className = "mx_MemberInfo_field" >
2017-10-07 21:27:49 +03:00
{ _t ( 'Jump to read receipt' ) }
2017-10-07 21:25:13 +03:00
< / A c c e s s i b l e B u t t o n >
) ;
2017-10-25 01:01:40 +03:00
insertPillButton = (
< AccessibleButton onClick = { onInsertPillButton } className = { "mx_MemberInfo_field" } >
{ _t ( 'Mention' ) }
< / A c c e s s i b l e B u t t o n >
) ;
2017-10-07 21:25:13 +03:00
}
2017-10-25 02:58:16 +03:00
if ( ! member || ! member . membership || member . membership === 'leave' ) {
const roomId = member && member . roomId ? member . roomId : RoomViewStore . getRoomId ( ) ;
const onInviteUserButton = async ( ) => {
try {
await cli . invite ( roomId , member . userId ) ;
} catch ( err ) {
const ErrorDialog = sdk . getComponent ( 'dialogs.ErrorDialog' ) ;
Modal . createTrackedDialog ( 'Failed to invite' , '' , ErrorDialog , {
title : _t ( 'Failed to invite' ) ,
description : ( ( err && err . message ) ? err . message : "Operation failed" ) ,
} ) ;
}
} ;
inviteUserButton = (
< AccessibleButton onClick = { onInviteUserButton } className = "mx_MemberInfo_field" >
{ _t ( 'Invite' ) }
< / A c c e s s i b l e B u t t o n >
) ;
2017-10-07 21:25:13 +03:00
}
2017-09-15 02:10:02 +03:00
}
2017-10-26 20:10:10 +03:00
if ( ! ignoreButton && ! readReceiptButton && ! insertPillButton && ! inviteUserButton ) return null ;
2017-09-15 02:10:02 +03:00
return (
< div >
< h3 > { _t ( "User Options" ) } < / h 3 >
< div className = "mx_MemberInfo_buttons" >
2017-10-07 21:25:13 +03:00
{ readReceiptButton }
2017-10-25 01:01:40 +03:00
{ insertPillButton }
2017-10-11 19:56:17 +03:00
{ ignoreButton }
2017-10-25 02:58:16 +03:00
{ inviteUserButton }
2016-06-23 19:27:23 +03:00
< / d i v >
2016-06-08 15:13:41 +03:00
< / d i v >
) ;
} ,
2015-11-26 20:49:39 +03:00
render : function ( ) {
2017-10-25 00:21:33 +03:00
let startChat ;
let kickButton ;
let banButton ;
let muteButton ;
let giveModButton ;
let spinner ;
2016-11-03 21:55:09 +03:00
if ( this . props . member . userId !== this . props . matrixClient . credentials . userId ) {
const dmRoomMap = new DMRoomMap ( this . props . matrixClient ) ;
2017-08-28 03:04:18 +03:00
// dmRooms will not include dmRooms that we have been invited into but did not join.
// Because DMRoomMap runs off account_data[m.direct] which is only set on join of dm room.
// XXX: we potentially want DMs we have been invited to, to also show up here :L
// especially as logic below concerns specially if we haven't joined but have been invited
2016-09-09 18:15:01 +03:00
const dmRooms = dmRoomMap . getDMRoomsForUserId ( this . props . member . userId ) ;
const RoomTile = sdk . getComponent ( "rooms.RoomTile" ) ;
const tiles = [ ] ;
for ( const roomId of dmRooms ) {
2016-11-03 21:55:09 +03:00
const room = this . props . matrixClient . getRoom ( roomId ) ;
2016-09-09 18:15:01 +03:00
if ( room ) {
2016-11-03 21:55:09 +03:00
const me = room . getMember ( this . props . matrixClient . credentials . userId ) ;
2018-01-04 19:41:16 +03:00
2017-08-28 03:04:18 +03:00
// not a DM room if we have are not joined
if ( ! me . membership || me . membership !== 'join' ) continue ;
// not a DM room if they are not joined
2017-08-28 02:39:59 +03:00
const them = this . props . member ;
2017-08-28 03:04:18 +03:00
if ( ! them . membership || them . membership !== 'join' ) continue ;
2017-08-28 02:39:59 +03:00
2017-08-28 02:16:22 +03:00
const highlight = room . getUnreadNotificationCount ( 'highlight' ) > 0 || me . membership === 'invite' ;
2016-09-09 18:15:01 +03:00
tiles . push (
< RoomTile key = { room . roomId } room = { room }
2018-03-05 15:36:02 +03:00
transparent = { true }
2016-09-09 18:15:01 +03:00
collapsed = { false }
selected = { false }
unread = { Unread . doesRoomHaveUnreadMessages ( room ) }
highlight = { highlight }
2017-08-28 02:16:22 +03:00
isInvite = { me . membership === "invite" }
2017-03-06 20:44:29 +03:00
onClick = { this . onRoomTileClick }
2017-10-11 19:56:17 +03:00
/ > ,
2016-09-09 18:15:01 +03:00
) ;
2016-07-17 21:41:53 +03:00
}
}
2016-09-09 18:15:01 +03:00
const labelClasses = classNames ( {
mx _MemberInfo _createRoom _label : true ,
mx _RoomTile _name : true ,
} ) ;
2017-01-13 19:25:26 +03:00
const startNewChat = < AccessibleButton
2016-09-09 18:15:01 +03:00
className = "mx_MemberInfo_createRoom"
onClick = { this . onNewDMClick }
>
< div className = "mx_RoomTile_avatar" >
< img src = "img/create-big.svg" width = "26" height = "26" / >
< / d i v >
2017-05-23 17:16:31 +03:00
< div className = { labelClasses } > < i > { _t ( "Start a chat" ) } < / i > < / d i v >
2017-01-24 23:47:24 +03:00
< / A c c e s s i b l e B u t t o n > ;
2016-09-09 18:15:01 +03:00
startChat = < div >
2017-05-23 17:16:31 +03:00
< h3 > { _t ( "Direct chats" ) } < / h 3 >
2017-10-11 19:56:17 +03:00
{ tiles }
{ startNewChat }
2016-09-09 18:15:01 +03:00
< / d i v > ;
2015-11-26 20:49:39 +03:00
}
2016-04-13 03:46:10 +03:00
if ( this . state . updating ) {
2017-10-11 19:56:17 +03:00
const Loader = sdk . getComponent ( "elements.Spinner" ) ;
spinner = < Loader imgClassName = "mx_ContextualMenu_spinner" / > ;
2015-11-26 20:49:39 +03:00
}
if ( this . state . can . kick ) {
2017-01-25 01:41:52 +03:00
const membership = this . props . member . membership ;
2017-05-23 17:16:31 +03:00
const kickLabel = membership === "invite" ? _t ( "Disinvite" ) : _t ( "Kick" ) ;
2017-01-25 01:41:52 +03:00
kickButton = (
< AccessibleButton className = "mx_MemberInfo_field"
onClick = { this . onKick } >
2017-10-11 19:56:17 +03:00
{ kickLabel }
2017-01-25 01:41:52 +03:00
< / A c c e s s i b l e B u t t o n >
) ;
2015-11-26 20:49:39 +03:00
}
if ( this . state . can . ban ) {
2017-05-23 17:16:31 +03:00
let label = _t ( "Ban" ) ;
2017-10-25 00:21:33 +03:00
if ( this . props . member . membership === 'ban' ) {
2017-05-23 17:16:31 +03:00
label = _t ( "Unban" ) ;
2017-02-14 19:03:30 +03:00
}
2017-01-25 01:41:52 +03:00
banButton = (
< AccessibleButton className = "mx_MemberInfo_field"
2017-02-14 20:29:40 +03:00
onClick = { this . onBanOrUnban } >
2017-10-11 19:56:17 +03:00
{ label }
2017-01-25 01:41:52 +03:00
< / A c c e s s i b l e B u t t o n >
) ;
2015-11-26 20:49:39 +03:00
}
if ( this . state . can . mute ) {
2017-05-23 17:16:31 +03:00
const muteLabel = this . state . muted ? _t ( "Unmute" ) : _t ( "Mute" ) ;
2017-01-25 01:41:52 +03:00
muteButton = (
< AccessibleButton className = "mx_MemberInfo_field"
onClick = { this . onMuteToggle } >
2017-10-11 19:56:17 +03:00
{ muteLabel }
2017-01-25 01:41:52 +03:00
< / A c c e s s i b l e B u t t o n >
) ;
2015-11-26 20:49:39 +03:00
}
2016-01-18 04:40:19 +03:00
if ( this . state . can . toggleMod ) {
2017-10-11 19:56:17 +03:00
const giveOpLabel = this . state . isTargetMod ? _t ( "Revoke Moderator" ) : _t ( "Make Moderator" ) ;
2017-01-13 19:25:26 +03:00
giveModButton = < AccessibleButton className = "mx_MemberInfo_field" onClick = { this . onModToggle } >
2017-10-11 19:56:17 +03:00
{ giveOpLabel }
2017-01-24 23:47:24 +03:00
< / A c c e s s i b l e B u t t o n > ;
2015-11-26 20:49:39 +03:00
}
2017-10-11 19:56:17 +03:00
let adminTools ;
2016-01-18 04:18:02 +03:00
if ( kickButton || banButton || muteButton || giveModButton ) {
2016-04-13 03:46:10 +03:00
adminTools =
2016-01-18 04:18:02 +03:00
< div >
2017-10-11 19:56:17 +03:00
< h3 > { _t ( "Admin Tools" ) } < / h 3 >
2016-01-18 04:18:02 +03:00
< div className = "mx_MemberInfo_buttons" >
2017-10-11 19:56:17 +03:00
{ muteButton }
{ kickButton }
{ banButton }
{ giveModButton }
2016-01-18 04:18:02 +03:00
< / d i v >
2017-01-20 17:22:27 +03:00
< / d i v > ;
2016-01-18 04:18:02 +03:00
}
2016-08-09 22:01:51 +03:00
const memberName = this . props . member . name ;
2016-07-05 07:54:18 +03:00
2017-10-25 00:21:33 +03:00
let presenceState ;
let presenceLastActiveAgo ;
let presenceCurrentlyActive ;
2017-05-15 04:43:23 +03:00
if ( this . props . member . user ) {
2017-10-25 00:21:33 +03:00
presenceState = this . props . member . user . presence ;
presenceLastActiveAgo = this . props . member . user . lastActiveAgo ;
presenceCurrentlyActive = this . props . member . user . currentlyActive ;
2017-05-15 04:43:23 +03:00
}
2017-11-13 15:08:53 +03:00
const room = this . props . matrixClient . getRoom ( this . props . member . roomId ) ;
2018-01-18 02:25:39 +03:00
const powerLevelEvent = room ? room . currentState . getStateEvents ( "m.room.power_levels" , "" ) : null ;
const powerLevelUsersDefault = powerLevelEvent ? powerLevelEvent . getContent ( ) . users _default : 0 ;
2017-10-25 02:58:16 +03:00
2018-03-19 19:47:12 +03:00
const enablePresenceByHsUrl = SdkConfig . get ( ) [ "enable_presence_by_hs_url" ] ;
const hsUrl = this . props . matrixClient . baseUrl ;
let showPresence = true ;
if ( enablePresenceByHsUrl && enablePresenceByHsUrl [ hsUrl ] !== undefined ) {
showPresence = enablePresenceByHsUrl [ hsUrl ] ;
}
let presenceLabel = null ;
if ( showPresence ) {
2018-03-21 15:45:09 +03:00
const PresenceLabel = sdk . getComponent ( 'rooms.PresenceLabel' ) ;
2018-03-19 19:47:12 +03:00
presenceLabel = < PresenceLabel activeAgo = { presenceLastActiveAgo }
currentlyActive = { presenceCurrentlyActive }
presenceState = { presenceState } / > ;
}
2017-11-13 15:08:53 +03:00
let roomMemberDetails = null ;
2017-10-25 02:58:16 +03:00
if ( this . props . member . roomId ) { // is in room
const PowerSelector = sdk . getComponent ( 'elements.PowerSelector' ) ;
roomMemberDetails = < div >
< div className = "mx_MemberInfo_profileField" >
{ _t ( "Level:" ) } < b >
< PowerSelector controlled = { true }
value = { parseInt ( this . props . member . powerLevel ) }
2017-11-13 15:08:53 +03:00
maxValue = { this . state . can . modifyLevelMax }
2017-10-25 02:58:16 +03:00
disabled = { ! this . state . can . modifyLevel }
2017-11-13 15:08:53 +03:00
usersDefault = { powerLevelUsersDefault }
2017-10-25 02:58:16 +03:00
onChange = { this . onPowerChange } / >
< / b >
< / d i v >
< div className = "mx_MemberInfo_profileField" >
2018-03-19 19:47:12 +03:00
{ presenceLabel }
2017-10-25 02:58:16 +03:00
< / d i v >
< / d i v > ;
2017-05-15 04:43:23 +03:00
}
2018-03-21 15:00:56 +03:00
const GeminiScrollbarWrapper = sdk . getComponent ( "elements.GeminiScrollbarWrapper" ) ;
2017-10-11 19:56:17 +03:00
const MemberAvatar = sdk . getComponent ( 'avatars.MemberAvatar' ) ;
2016-08-11 05:25:12 +03:00
const EmojiText = sdk . getComponent ( 'elements.EmojiText' ) ;
2015-11-26 20:49:39 +03:00
return (
< div className = "mx_MemberInfo" >
2018-03-21 15:00:56 +03:00
< GeminiScrollbarWrapper autoshow = { true } >
2017-10-11 19:56:17 +03:00
< AccessibleButton className = "mx_MemberInfo_cancel" onClick = { this . onCancel } > < img src = "img/cancel.svg" width = "18" height = "18" / > < / A c c e s s i b l e B u t t o n >
2017-06-08 19:26:40 +03:00
< div className = "mx_MemberInfo_avatar" >
< MemberAvatar onClick = { this . onMemberAvatarClick } member = { this . props . member } width = { 48 } height = { 48 } / >
2016-01-18 04:18:02 +03:00
< / d i v >
2017-06-08 19:26:40 +03:00
2017-10-11 19:56:17 +03:00
< EmojiText element = "h2" > { memberName } < / E m o j i T e x t >
2017-06-08 19:26:40 +03:00
< div className = "mx_MemberInfo_profile" >
< div className = "mx_MemberInfo_profileField" >
{ this . props . member . userId }
< / d i v >
2017-10-25 02:58:16 +03:00
{ roomMemberDetails }
2017-05-15 04:43:23 +03:00
< / d i v >
2016-01-18 04:18:02 +03:00
2017-09-15 02:10:02 +03:00
{ this . _renderUserOptions ( ) }
2017-06-08 19:26:40 +03:00
{ adminTools }
2016-09-17 17:07:41 +03:00
2017-06-08 19:26:40 +03:00
{ startChat }
2016-01-18 04:18:02 +03:00
2017-06-08 19:26:40 +03:00
{ this . _renderDevices ( ) }
2016-06-08 15:13:41 +03:00
2017-06-08 19:26:40 +03:00
{ spinner }
2018-03-21 15:00:56 +03:00
< / G e m i n i S c r o l l b a r W r a p p e r >
2015-11-26 20:49:39 +03:00
< / d i v >
) ;
2017-10-11 19:56:17 +03:00
} ,
2016-11-03 21:55:09 +03:00
} ) ) ;