import React from 'react'; import PropTypes from 'prop-types'; import { View, Text, ScrollView } from 'react-native'; import { BorderlessButton } from 'react-native-gesture-handler'; import { connect } from 'react-redux'; import moment from 'moment'; import _ from 'lodash'; import { SafeAreaView } from 'react-navigation'; import { CustomIcon } from '../../lib/Icons'; import Status from '../../containers/Status'; import Avatar from '../../containers/Avatar'; import styles from './styles'; import sharedStyles from '../Styles'; import database from '../../lib/database'; import RocketChat from '../../lib/rocketchat'; import RoomTypeIcon from '../../containers/RoomTypeIcon'; import I18n from '../../i18n'; import { CustomHeaderButtons, Item } from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; import log from '../../utils/log'; import { themes } from '../../constants/colors'; import { withTheme } from '../../theme'; import { themedHeader } from '../../utils/navigation'; import { getUserSelector } from '../../selectors/login'; import Markdown from '../../containers/markdown'; const PERMISSION_EDIT_ROOM = 'edit-room'; const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd' ? ( <> <Text testID='room-info-view-name' style={[styles.roomTitle, { color: themes[theme].titleText }]}>{ name }</Text> {username && <Text testID='room-info-view-username' style={[styles.roomUsername, { color: themes[theme].auxiliaryText }]}>{`@${ username }`}</Text>} {!!statusText && <View testID='room-info-view-custom-status'><Markdown msg={statusText} style={[styles.roomUsername, { color: themes[theme].auxiliaryText }]} preview theme={theme} /></View>} </> ) : ( <View style={styles.roomTitleRow}> <RoomTypeIcon type={room.prid ? 'discussion' : room.t} key='room-info-type' theme={theme} /> <Text testID='room-info-view-name' style={[styles.roomTitle, { color: themes[theme].titleText }]} key='room-info-name'>{RocketChat.getRoomTitle(room)}</Text> </View> ) ); class RoomInfoView extends React.Component { static navigationOptions = ({ navigation, screenProps }) => { const showEdit = navigation.getParam('showEdit'); const rid = navigation.getParam('rid'); const t = navigation.getParam('t'); return { title: t === 'd' ? I18n.t('User_Info') : I18n.t('Room_Info'), ...themedHeader(screenProps.theme), headerRight: showEdit ? ( <CustomHeaderButtons> <Item iconName='edit' onPress={() => navigation.navigate('RoomInfoEditView', { rid })} testID='room-info-view-edit-button' /> </CustomHeaderButtons> ) : null }; } static propTypes = { navigation: PropTypes.object, user: PropTypes.shape({ id: PropTypes.string, token: PropTypes.string }), baseUrl: PropTypes.string, Message_TimeFormat: PropTypes.string, theme: PropTypes.string } constructor(props) { super(props); const room = props.navigation.getParam('room'); const roomUser = props.navigation.getParam('member'); this.rid = props.navigation.getParam('rid'); this.t = props.navigation.getParam('t'); this.state = { room: room || { rid: this.rid, t: this.t }, roomUser: roomUser || {}, parsedRoles: [] }; } async componentDidMount() { const { roomUser, room: roomState } = this.state; if (this.t === 'd' && !_.isEmpty(roomUser)) { return; } if (this.t === 'd') { try { const roomUserId = RocketChat.getUidDirectMessage(roomState); const result = await RocketChat.getUserInfo(roomUserId); if (result.success) { const { roles } = result.user; let parsedRoles = []; if (roles && roles.length) { parsedRoles = await Promise.all(roles.map(async(role) => { const description = await this.getRoleDescription(role); return description; })); } this.setState({ roomUser: result.user, parsedRoles }); } } catch (e) { log(e); } return; } const { navigation } = this.props; let room = navigation.getParam('room'); if (room && room.observe) { this.roomObservable = room.observe(); this.subscription = this.roomObservable .subscribe((changes) => { this.setState({ room: changes }); }); } else { try { const result = await RocketChat.getRoomInfo(this.rid); if (result.success) { // eslint-disable-next-line prefer-destructuring room = result.room; this.setState({ room }); } } catch (e) { log(e); } } const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid); if (permissions[PERMISSION_EDIT_ROOM] && !room.prid && this.t !== 'l') { navigation.setParams({ showEdit: true }); } } componentWillUnmount() { if (this.subscription && this.subscription.unsubscribe) { this.subscription.unsubscribe(); } } getRoleDescription = async(id) => { const db = database.active; try { const rolesCollection = db.collections.get('roles'); const role = await rolesCollection.find(id); if (role) { return role.description; } return null; } catch (e) { return null; } } goRoom = async() => { const { roomUser } = this.state; const { username } = roomUser; const { navigation } = this.props; try { const result = await RocketChat.createDirectMessage(username); if (result.success) { await navigation.navigate('RoomsListView'); const rid = result.room._id; navigation.navigate('RoomView', { rid, name: RocketChat.getRoomTitle(roomUser), t: 'd' }); } } catch (e) { // do nothing } } videoCall = () => RocketChat.callJitsi(this.rid) isDirect = () => this.t === 'd' renderItem = ({ label, content }) => { const { theme } = this.props; return ( <View style={styles.item}> <Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>{label}</Text> <Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content || `__${ I18n.t('No_label_provided', { label: label.toLowerCase() }) }__`} theme={theme} /> </View> ); } renderRole = (description) => { const { theme } = this.props; if (description) { return ( <View style={[styles.roleBadge, { backgroundColor: themes[theme].auxiliaryBackground }]} key={description}> <Text style={styles.role}>{ description }</Text> </View> ); } return null; } renderRoles = () => { const { parsedRoles } = this.state; const { theme } = this.props; if (parsedRoles && parsedRoles.length) { return ( <View style={styles.item}> <Text style={[styles.itemLabel, { color: themes[theme].titleText }]}>{I18n.t('Roles')}</Text> <View style={styles.rolesContainer}> {parsedRoles.map(role => this.renderRole(role))} </View> </View> ); } return null; } renderTimezone = () => { const { roomUser } = this.state; const { Message_TimeFormat } = this.props; if (roomUser) { const { utcOffset } = roomUser; if (!utcOffset) { return null; } return this.renderItem({ label: I18n.t('Timezone'), content: `${ moment().utcOffset(utcOffset).format(Message_TimeFormat) } (UTC ${ utcOffset })` }); } return null; } renderAvatar = (room, roomUser) => { const { baseUrl, user, theme } = this.props; return ( <Avatar text={room.name || roomUser.username} size={100} style={styles.avatar} type={this.t} baseUrl={baseUrl} userId={user.id} token={user.token} > {this.t === 'd' && roomUser._id ? <Status style={[sharedStyles.status, styles.status]} theme={theme} size={24} id={roomUser._id} /> : null} </Avatar> ); } renderBroadcast = () => this.renderItem({ label: I18n.t('Broadcast_Channel'), content: I18n.t('Broadcast_channel_Description') }); renderCustomFields = () => { const { roomUser } = this.state; if (roomUser) { const { customFields } = roomUser; if (!roomUser.customFields) { return null; } return ( Object.keys(customFields).map((title) => { if (!customFields[title]) { return; } return ( <View style={styles.item} key={title}> <Text style={styles.itemLabel}>{title}</Text> <Text style={styles.itemContent}>{customFields[title]}</Text> </View> ); }) ); } return null; } renderButton = (onPress, iconName, text) => { const { theme } = this.props; return ( <BorderlessButton onPress={onPress} style={styles.roomButton} > <CustomIcon name={iconName} size={30} color={themes[theme].actionTintColor} /> <Text style={[styles.roomButtonText, { color: themes[theme].actionTintColor }]}>{text}</Text> </BorderlessButton> ); } renderButtons = () => ( <View style={styles.roomButtonsContainer}> {this.renderButton(this.goRoom, 'message', I18n.t('Message'))} {this.renderButton(this.videoCall, 'video', I18n.t('Video_call'))} </View> ) renderChannel = () => { const { room } = this.state; const { description, topic, announcement } = room; return ( <> {this.renderItem({ label: I18n.t('Description'), content: description })} {this.renderItem({ label: I18n.t('Topic'), content: topic })} {this.renderItem({ label: I18n.t('Announcement'), content: announcement })} {room.broadcast ? this.renderBroadcast() : null} </> ); } renderDirect = () => { const { roomUser } = this.state; return ( <> {this.renderRoles()} {this.renderTimezone()} {this.renderCustomFields(roomUser._id)} </> ); } render() { const { room, roomUser } = this.state; const { theme } = this.props; const isDirect = this.isDirect(); if (!room) { return <View />; } return ( <ScrollView style={[styles.scroll, { backgroundColor: themes[theme].backgroundColor }]}> <StatusBar theme={theme} /> <SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]} forceInset={{ vertical: 'never' }} testID='room-info-view' > <View style={[styles.avatarContainer, isDirect && styles.avatarContainerDirectRoom, { backgroundColor: themes[theme].auxiliaryBackground }]}> {this.renderAvatar(room, roomUser)} <View style={styles.roomTitleContainer}>{ getRoomTitle(room, this.t, roomUser && roomUser.name, roomUser && roomUser.username, roomUser && roomUser.statusText, theme) }</View> {isDirect ? this.renderButtons() : null} </View> {isDirect ? this.renderDirect() : this.renderChannel()} </SafeAreaView> </ScrollView> ); } } const mapStateToProps = state => ({ baseUrl: state.server.server, user: getUserSelector(state), Message_TimeFormat: state.settings.Message_TimeFormat }); export default connect(mapStateToProps)(withTheme(RoomInfoView));