import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation, SafeAreaView } from 'react-native'; import { connect } from 'react-redux'; import Icon from 'react-native-vector-icons/MaterialIcons'; import equal from 'deep-equal'; import Navigation from '../lib/Navigation'; import { setStackRoot as setStackRootAction } from '../actions'; import { logout as logoutAction } from '../actions/login'; import Avatar from '../containers/Avatar'; import Status from '../containers/status'; import Touch from '../utils/touch'; import { STATUS_COLORS } from '../constants/colors'; import RocketChat from '../lib/rocketchat'; import log from '../utils/log'; import I18n from '../i18n'; import scrollPersistTaps from '../utils/scrollPersistTaps'; import { getReadableVersion } from '../utils/deviceInfo'; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff' }, item: { flexDirection: 'row', alignItems: 'center' }, itemLeft: { marginHorizontal: 10, width: 30, alignItems: 'center' }, itemText: { marginVertical: 16, fontWeight: 'bold', color: '#292E35' }, itemSelected: { backgroundColor: '#F7F8FA' }, separator: { borderBottomWidth: StyleSheet.hairlineWidth, borderColor: '#ddd', marginVertical: 4 }, header: { paddingVertical: 16, flexDirection: 'row', alignItems: 'center' }, headerTextContainer: { flex: 1, flexDirection: 'column', alignItems: 'flex-start' }, headerUsername: { flexDirection: 'row', alignItems: 'center' }, avatar: { marginHorizontal: 10 }, status: { borderRadius: 12, width: 12, height: 12, marginRight: 5 }, currentServerText: { fontWeight: 'bold' }, version: { marginHorizontal: 5, marginBottom: 5, fontWeight: '600', color: '#292E35', fontSize: 13 } }); const keyExtractor = item => item.id; @connect(state => ({ Site_Name: state.settings.Site_Name, stackRoot: state.app.stackRoot, user: { id: state.login.user && state.login.user.id, language: state.login.user && state.login.user.language, status: state.login.user && state.login.user.status, username: state.login.user && state.login.user.username, token: state.login.user && state.login.user.token }, baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' }), dispatch => ({ logout: () => dispatch(logoutAction()), setStackRoot: stackRoot => dispatch(setStackRootAction(stackRoot)) })) export default class Sidebar extends Component { static propTypes = { baseUrl: PropTypes.string, componentId: PropTypes.string, Site_Name: PropTypes.string.isRequired, stackRoot: PropTypes.string.isRequired, user: PropTypes.object, logout: PropTypes.func.isRequired, setStackRoot: PropTypes.func } constructor(props) { super(props); this.state = { showStatus: false, status: [] }; Navigation.events().bindComponent(this); } componentDidMount() { this.setStatus(); } componentWillReceiveProps(nextProps) { const { user } = this.props; if (nextProps.user && user && user.language !== nextProps.user.language) { this.setStatus(); } } shouldComponentUpdate(nextProps, nextState) { const { status, showStatus } = this.state; const { Site_Name, stackRoot, user, baseUrl } = this.props; if (nextState.showStatus !== showStatus) { return true; } if (nextProps.Site_Name !== Site_Name) { return true; } if (nextProps.stackRoot !== stackRoot) { return true; } if (nextProps.Site_Name !== Site_Name) { return true; } if (nextProps.baseUrl !== baseUrl) { return true; } if (nextProps.user && user) { if (nextProps.user.language !== user.language) { return true; } if (nextProps.user.status !== user.status) { return true; } if (nextProps.user.username !== user.username) { return true; } } if (!equal(nextState.status, status)) { return true; } return false; } handleChangeStack = (event) => { const { stack } = event; this.setStack(stack); } navigationButtonPressed = ({ buttonId }) => { if (buttonId === 'cancel') { const { componentId } = this.props; Navigation.dismissModal(componentId); } } setStatus = () => { this.setState({ status: [{ id: 'online', name: I18n.t('Online') }, { id: 'busy', name: I18n.t('Busy') }, { id: 'away', name: I18n.t('Away') }, { id: 'offline', name: I18n.t('Invisible') }] }); } setStack = async(stack) => { const { stackRoot, setStackRoot } = this.props; if (stackRoot !== stack) { await Navigation.setStackRoot('AppRoot', { component: { id: stack, name: stack } }); setStackRoot(stack); } } closeDrawer = () => { Navigation.toggleDrawer(); } toggleStatus = () => { LayoutAnimation.easeInEaseOut(); this.setState(prevState => ({ showStatus: !prevState.showStatus })); } sidebarNavigate = (stack) => { this.closeDrawer(); this.setStack(stack); } renderSeparator = key => <View key={key} style={styles.separator} />; renderItem = ({ text, left, onPress, testID, current }) => ( <Touch key={text} onPress={onPress} underlayColor='rgba(255, 255, 255, 0.5)' activeOpacity={0.3} testID={testID} > <View style={[styles.item, current && styles.itemSelected]}> <View style={styles.itemLeft}> {left} </View> <Text style={styles.itemText}> {text} </Text> </View> </Touch> ) renderStatusItem = ({ item }) => { const { user } = this.props; return ( this.renderItem({ text: item.name, left: <View style={[styles.status, { backgroundColor: STATUS_COLORS[item.id] }]} />, current: user.status === item.id, onPress: () => { this.closeDrawer(); this.toggleStatus(); if (user.status !== item.id) { try { RocketChat.setUserPresenceDefaultStatus(item.id); } catch (e) { log('setUserPresenceDefaultStatus', e); } } } }) ); } renderNavigation = () => { const { stackRoot } = this.props; const { logout } = this.props; return ( [ this.renderItem({ text: I18n.t('Chats'), left: <Icon name='chat-bubble' size={20} />, onPress: () => this.sidebarNavigate('RoomsListView'), testID: 'sidebar-chats', current: stackRoot === 'RoomsListView' }), this.renderItem({ text: I18n.t('Profile'), left: <Icon name='person' size={20} />, onPress: () => this.sidebarNavigate('ProfileView'), testID: 'sidebar-profile', current: stackRoot === 'ProfileView' }), this.renderItem({ text: I18n.t('Settings'), left: <Icon name='settings' size={20} />, onPress: () => this.sidebarNavigate('SettingsView'), testID: 'sidebar-settings', current: stackRoot === 'SettingsView' }), this.renderSeparator('separator-logout'), this.renderItem({ text: I18n.t('Logout'), left: <Icon name='exit-to-app' size={20} />, onPress: () => logout(), testID: 'sidebar-logout' }) ] ); } renderStatus = () => { const { status } = this.state; const { user } = this.props; return ( <FlatList key='status-list' data={status} extraData={user} renderItem={this.renderStatusItem} keyExtractor={keyExtractor} /> ); } render() { const { showStatus } = this.state; const { user, Site_Name, baseUrl } = this.props; if (!user) { return null; } return ( <SafeAreaView testID='sidebar-view' style={styles.container}> <ScrollView style={styles.container} {...scrollPersistTaps}> <Touch onPress={() => this.toggleStatus()} underlayColor='rgba(255, 255, 255, 0.5)' activeOpacity={0.3} testID='sidebar-toggle-status' > <View style={styles.header}> <Avatar text={user.username} size={30} style={styles.avatar} baseUrl={baseUrl} user={user} /> <View style={styles.headerTextContainer}> <View style={styles.headerUsername}> <Status style={styles.status} id={user.id} /> <Text numberOfLines={1}>{user.username}</Text> </View> <Text style={styles.currentServerText} numberOfLines={1}>{Site_Name}</Text> </View> <Icon name={showStatus ? 'keyboard-arrow-up' : 'keyboard-arrow-down'} size={30} style={{ paddingHorizontal: 10 }} /> </View> </Touch> {this.renderSeparator('separator-header')} {!showStatus ? this.renderNavigation() : null} {showStatus ? this.renderStatus() : null} </ScrollView> <Text style={styles.version}> {getReadableVersion} </Text> </SafeAreaView> ); } }