diff --git a/app/constants/settings.js b/app/constants/settings.js index 8e8e9b8c8..f22949ea0 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -14,6 +14,9 @@ export default { CROWD_Enable: { type: 'valueAsBoolean' }, + FEDERATION_Enabled: { + type: 'valueAsBoolean' + }, LDAP_Enable: { type: 'valueAsBoolean' }, diff --git a/app/containers/Check.js b/app/containers/Check.js new file mode 100644 index 000000000..30c9cbd31 --- /dev/null +++ b/app/containers/Check.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { StyleSheet } from 'react-native'; + +import { CustomIcon } from '../lib/Icons'; +import sharedStyles from '../views/Styles'; + +const styles = StyleSheet.create({ + icon: { + width: 22, + height: 22, + marginHorizontal: 15, + ...sharedStyles.textColorDescription + } +}); + +const Check = React.memo(() => ); + +export default Check; diff --git a/app/containers/SearchBox.js b/app/containers/SearchBox.js index 65a41d3ee..87a3cd8e3 100644 --- a/app/containers/SearchBox.js +++ b/app/containers/SearchBox.js @@ -34,7 +34,7 @@ const styles = StyleSheet.create({ } }); -const SearchBox = ({ onChangeText, testID }) => ( +const SearchBox = ({ onChangeText, onSubmitEditing, testID }) => ( @@ -49,6 +49,7 @@ const SearchBox = ({ onChangeText, testID }) => ( testID={testID} underlineColorAndroid='transparent' onChangeText={onChangeText} + onSubmitEditing={onSubmitEditing} /> @@ -56,6 +57,7 @@ const SearchBox = ({ onChangeText, testID }) => ( SearchBox.propTypes = { onChangeText: PropTypes.func.isRequired, + onSubmitEditing: PropTypes.func, testID: PropTypes.string }; diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index 62b4f117a..a426f0ddb 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -142,9 +142,10 @@ export default { DELETE: 'DELETE', description: 'description', Description: 'Description', + Directory: 'Directory', + Direct_Messages: 'Direct Messages', Disable_notifications: 'Disable notifications', Discussions: 'Discussions', - Direct_Messages: 'Direct Messages', Dont_Have_An_Account: 'Don\'t have an account?', Do_you_really_want_to_key_this_room_question_mark: 'Do you really want to {{key}} this room?', edit: 'edit', @@ -294,6 +295,9 @@ export default { saving_settings: 'saving settings', Search_Messages: 'Search Messages', Search: 'Search', + Search_by: 'Search by', + Search_global_users: 'Search for global users', + Search_global_users_description: 'If you turn-on, you can search for any user from others companies or servers.', Select_Avatar: 'Select Avatar', Select_Users: 'Select Users', Send: 'Send', @@ -348,6 +352,7 @@ export default { Updating: 'Updating...', Uploading: 'Uploading', Upload_file_question_mark: 'Upload file?', + Users: 'Users', User_added_by: 'User {{userAdded}} added by {{userBy}}', User_has_been_key: 'User has been {{key}}!', User_is_no_longer_role_by_: '{{user}} is no longer {{role}} by {{userBy}}', diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index 859b4f299..ef13d05cf 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -146,11 +146,12 @@ export default { delete: 'excluir', Delete: 'Excluir', DELETE: 'EXCLUIR', + Direct_Messages: 'Mensagens Diretas', + Directory: 'Diretório', description: 'descrição', Description: 'Descrição', Disable_notifications: 'Desabilitar notificações', Discussions: 'Discussões', - Direct_Messages: 'Mensagens Diretas', Dont_Have_An_Account: 'Não tem uma conta?', Do_you_really_want_to_key_this_room_question_mark: 'Você quer realmente {{key}} esta sala?', edit: 'editar', @@ -293,6 +294,9 @@ export default { saving_settings: 'salvando configurações', Search_Messages: 'Buscar Mensagens', Search: 'Buscar', + Search_by: 'Buscar por', + Search_global_users: 'Busca por usuários globais', + Search_global_users_description: 'Caso ativado, busca por usuários de outras empresas ou servidores.', Select_Avatar: 'Selecionar Avatar', Select_Users: 'Selecionar Usuários', Send: 'Enviar', @@ -344,6 +348,7 @@ export default { Updating: 'Atualizando...', Uploading: 'Subindo arquivo', Upload_file_question_mark: 'Enviar arquivo?', + Users: 'Usuários', User_added_by: 'Usuário {{userAdded}} adicionado por {{userBy}}', User_has_been_key: 'Usuário foi {{key}}!', User_is_no_longer_role_by_: '{{user}} não pertence mais à {{role}} por {{userBy}}', diff --git a/app/index.js b/app/index.js index 9ee8cc9f8..33c491561 100644 --- a/app/index.js +++ b/app/index.js @@ -16,6 +16,7 @@ import AuthLoadingView from './views/AuthLoadingView'; import RoomsListView from './views/RoomsListView'; import RoomView from './views/RoomView'; import NewMessageView from './views/NewMessageView'; +import DirectoryView from './views/DirectoryView'; import LoginView from './views/LoginView'; import Navigation from './lib/Navigation'; import Sidebar from './views/SidebarView'; @@ -110,7 +111,8 @@ const ChatsStack = createStackNavigator({ SearchMessagesView, SelectedUsersView, ThreadMessagesView, - MessagesView + MessagesView, + DirectoryView }, { defaultNavigationOptions: defaultHeader }); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 9b7e16e2c..3c7f622e5 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -5,7 +5,7 @@ import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk'; import reduxStore from './createStore'; import defaultSettings from '../constants/settings'; import messagesStatus from '../constants/messagesStatus'; -import database, { safeAddListener } from './realm'; +import database from './realm'; import log from '../utils/log'; import { isIOS, getBundleId } from '../utils/deviceInfo'; import EventEmitter from '../utils/events'; @@ -57,23 +57,6 @@ const RocketChat = { // RC 0.51.0 return this.sdk.methodCall(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast }); }, - async createDirectMessageAndWait(username) { - const room = await RocketChat.createDirectMessage(username); - return new Promise((resolve) => { - const data = database.objects('subscriptions') - .filtered('rid = $1', room.rid); - - if (data.length) { - return resolve(data[0]); - } - safeAddListener(data, () => { - if (!data.length) { return; } - data.removeAllListeners(); - resolve(data[0]); - }); - }); - }, - async getUserToken() { try { return await AsyncStorage.getItem(TOKEN_KEY); @@ -849,6 +832,14 @@ const RocketChat = { this.sdk.subscribe('stream-notify-logged', 'user-status'); } } + }, + getDirectory({ + query, count, offset, sort + }) { + // RC 1.0 + return this.sdk.get('directory', { + query, count, offset, sort + }); } }; diff --git a/app/views/DirectoryView/DirectoryItem.js b/app/views/DirectoryView/DirectoryItem.js new file mode 100644 index 000000000..620f5dae0 --- /dev/null +++ b/app/views/DirectoryView/DirectoryItem.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { Text, View } from 'react-native'; +import PropTypes from 'prop-types'; + +import Avatar from '../../containers/Avatar'; +import Touch from '../../utils/touch'; +import RoomTypeIcon from '../../containers/RoomTypeIcon'; +import styles from './styles'; + +const DirectoryItemLabel = React.memo(({ text }) => { + if (!text) { + return null; + } + return {text}; +}); + +const DirectoryItem = ({ + title, description, avatar, onPress, testID, style, baseUrl, user, rightLabel, type +}) => ( + + + + + + + {title} + + {description} + + + + +); + +DirectoryItem.propTypes = { + title: PropTypes.string.isRequired, + description: PropTypes.string, + avatar: PropTypes.string, + type: PropTypes.string, + user: PropTypes.shape({ + id: PropTypes.string, + token: PropTypes.string + }), + baseUrl: PropTypes.string.isRequired, + onPress: PropTypes.func.isRequired, + testID: PropTypes.string.isRequired, + style: PropTypes.any, + rightLabel: PropTypes.string +}; + +DirectoryItemLabel.propTypes = { + text: PropTypes.string +}; + +export default DirectoryItem; diff --git a/app/views/DirectoryView/Options.js b/app/views/DirectoryView/Options.js new file mode 100644 index 000000000..841484152 --- /dev/null +++ b/app/views/DirectoryView/Options.js @@ -0,0 +1,121 @@ +import React, { PureComponent } from 'react'; +import { + View, Text, Animated, Easing, TouchableWithoutFeedback, Switch +} from 'react-native'; +import PropTypes from 'prop-types'; + +import Touch from '../../utils/touch'; +import styles from './styles'; +import { CustomIcon } from '../../lib/Icons'; +import Check from '../../containers/Check'; +import I18n from '../../i18n'; + +const ANIMATION_DURATION = 200; +const ANIMATION_PROPS = { + duration: ANIMATION_DURATION, + easing: Easing.inOut(Easing.quad), + useNativeDriver: true +}; + +export default class DirectoryOptions extends PureComponent { + static propTypes = { + type: PropTypes.string, + globalUsers: PropTypes.bool, + isFederationEnabled: PropTypes.bool, + close: PropTypes.func, + changeType: PropTypes.func, + toggleWorkspace: PropTypes.func + } + + constructor(props) { + super(props); + this.animatedValue = new Animated.Value(0); + } + + componentDidMount() { + Animated.timing( + this.animatedValue, + { + toValue: 1, + ...ANIMATION_PROPS + }, + ).start(); + } + + close = () => { + const { close } = this.props; + Animated.timing( + this.animatedValue, + { + toValue: 0, + ...ANIMATION_PROPS + }, + ).start(() => close()); + } + + renderItem = (itemType) => { + const { changeType, type: propType } = this.props; + let text = 'Users'; + let icon = 'user'; + if (itemType === 'channels') { + text = 'Channels'; + icon = 'hashtag'; + } + + return ( + changeType(itemType)}> + + + {I18n.t(text)} + {propType === itemType ? : null} + + + ); + } + + render() { + const translateY = this.animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [-326, 0] + }); + const backdropOpacity = this.animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [0, 0.3] + }); + const { globalUsers, toggleWorkspace, isFederationEnabled } = this.props; + return ( + + + + + + + + {I18n.t('Search_by')} + + + + {this.renderItem('channels')} + {this.renderItem('users')} + {isFederationEnabled + ? ( + + + + + {I18n.t('Search_global_users')} + {I18n.t('Search_global_users_description')} + + + + + ) + : null} + + + ); + } +} diff --git a/app/views/DirectoryView/index.js b/app/views/DirectoryView/index.js new file mode 100644 index 000000000..60a12932e --- /dev/null +++ b/app/views/DirectoryView/index.js @@ -0,0 +1,248 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + View, FlatList, Text +} from 'react-native'; +import { connect } from 'react-redux'; +import { SafeAreaView } from 'react-navigation'; + +import RocketChat from '../../lib/rocketchat'; +import DirectoryItem from './DirectoryItem'; +import sharedStyles from '../Styles'; +import I18n from '../../i18n'; +import Touch from '../../utils/touch'; +import SearchBox from '../../containers/SearchBox'; +import { CustomIcon } from '../../lib/Icons'; +import StatusBar from '../../containers/StatusBar'; +import RCActivityIndicator from '../../containers/ActivityIndicator'; +import debounce from '../../utils/debounce'; +import log from '../../utils/log'; +import Options from './Options'; +import styles from './styles'; + +@connect(state => ({ + baseUrl: state.settings.Site_Url || state.server ? state.server.server : '', + user: { + id: state.login.user && state.login.user.id, + token: state.login.user && state.login.user.token + }, + isFederationEnabled: state.settings.FEDERATION_Enabled +})) +export default class DirectoryView extends React.Component { + static navigationOptions = () => ({ + title: I18n.t('Directory') + }) + + static propTypes = { + navigation: PropTypes.object, + baseUrl: PropTypes.string, + isFederationEnabled: PropTypes.bool, + user: PropTypes.shape({ + id: PropTypes.string, + token: PropTypes.string + }) + }; + + constructor(props) { + super(props); + this.state = { + data: [], + loading: false, + text: '', + total: -1, + showOptionsDropdown: false, + globalUsers: true, + type: 'channels' + }; + } + + componentDidMount() { + this.load({}); + } + + onSearchChangeText = (text) => { + this.setState({ text }); + } + + onPressItem = (item) => { + const { navigation } = this.props; + try { + const onPressItem = navigation.getParam('onPressItem', () => {}); + onPressItem(item); + } catch (error) { + console.log('DirectoryView -> onPressItem -> error', error); + } + } + + // eslint-disable-next-line react/sort-comp + load = debounce(async({ newSearch = false }) => { + if (newSearch) { + this.setState({ data: [], total: -1, loading: false }); + } + + const { + loading, text, total, data: { length } + } = this.state; + if (loading || length === total) { + return; + } + + this.setState({ loading: true }); + + try { + const { data, type, globalUsers } = this.state; + const query = { text, type, workspace: globalUsers ? 'all' : 'local' }; + const directories = await RocketChat.getDirectory({ + query, + offset: data.length, + count: 50, + sort: (type === 'users') ? { username: 1 } : { usersCount: -1 } + }); + if (directories.success) { + this.setState({ + data: [...data, ...directories.result], + loading: false, + total: directories.total + }); + } else { + this.setState({ loading: false }); + } + } catch (error) { + log('err_load_directory', error); + this.setState({ loading: false }); + } + }, 200) + + search = () => { + this.load({ newSearch: true }); + } + + changeType = (type) => { + this.setState({ type, data: [] }, () => this.search()); + } + + toggleWorkspace = () => { + this.setState(({ globalUsers }) => ({ globalUsers: !globalUsers, data: [] }), () => this.search()); + } + + toggleDropdown = () => { + this.setState(({ showOptionsDropdown }) => ({ showOptionsDropdown: !showOptionsDropdown })); + } + + goRoom = async({ rid, name, t }) => { + const { navigation } = this.props; + await navigation.navigate('RoomsListView'); + navigation.navigate('RoomView', { rid, name, t }); + } + + onPressItem = async(item) => { + const { type } = this.state; + if (type === 'users') { + const result = await RocketChat.createDirectMessage(item.username); + if (result.success) { + this.goRoom({ rid: result.room._id, name: item.username, t: 'd' }); + } + } else { + this.goRoom({ rid: item._id, name: item.name, t: 'c' }); + } + } + + renderHeader = () => { + const { type } = this.state; + return ( + + + + + + {type === 'users' ? I18n.t('Users') : I18n.t('Channels')} + + + + + ); + } + + renderSeparator = () => ; + + renderItem = ({ item, index }) => { + const { data, type } = this.state; + const { baseUrl, user } = this.props; + + let style; + if (index === data.length - 1) { + style = sharedStyles.separatorBottom; + } + + const commonProps = { + title: item.name, + onPress: () => this.onPressItem(item), + baseUrl, + testID: `federation-view-item-${ item.name }`, + style, + user + }; + + if (type === 'users') { + return ( + + ); + } + return ( + + ); + } + + render = () => { + const { + data, loading, showOptionsDropdown, type, globalUsers + } = this.state; + const { isFederationEnabled } = this.props; + return ( + + + item._id} + ListHeaderComponent={this.renderHeader} + renderItem={this.renderItem} + ItemSeparatorComponent={this.renderSeparator} + keyboardShouldPersistTaps='always' + ListFooterComponent={loading ? : null} + onEndReached={() => this.load({})} + /> + {showOptionsDropdown + ? ( + + ) + : null} + + ); + } +} diff --git a/app/views/DirectoryView/styles.js b/app/views/DirectoryView/styles.js new file mode 100644 index 000000000..59e60da2b --- /dev/null +++ b/app/views/DirectoryView/styles.js @@ -0,0 +1,151 @@ +import { StyleSheet } from 'react-native'; + +import { COLOR_WHITE, COLOR_SEPARATOR, COLOR_PRIMARY } from '../../constants/colors'; +import { isIOS } from '../../utils/deviceInfo'; +import sharedStyles from '../Styles'; + +export default StyleSheet.create({ + safeAreaView: { + flex: 1, + backgroundColor: isIOS ? '#F7F8FA' : '#E1E5E8' + }, + list: { + flex: 1 + }, + listContainer: { + paddingBottom: 30 + }, + separator: { + marginLeft: 60 + }, + toggleDropdownContainer: { + height: 47, + backgroundColor: COLOR_WHITE, + flexDirection: 'row', + alignItems: 'center' + }, + toggleDropdownIcon: { + color: COLOR_PRIMARY, + marginLeft: 20, + marginRight: 17 + }, + toggleDropdownText: { + flex: 1, + color: COLOR_PRIMARY, + fontSize: 17, + ...sharedStyles.textRegular + }, + toggleDropdownArrow: { + ...sharedStyles.textColorDescription, + marginRight: 15 + }, + dropdownContainer: { + backgroundColor: COLOR_WHITE, + width: '100%', + position: 'absolute', + top: 0 + }, + backdrop: { + ...StyleSheet.absoluteFill, + backgroundColor: '#000000' + }, + dropdownContainerHeader: { + height: 47, + borderBottomWidth: StyleSheet.hairlineWidth, + borderColor: COLOR_SEPARATOR, + alignItems: 'center', + backgroundColor: isIOS ? COLOR_WHITE : '#54585E', + flexDirection: 'row' + }, + dropdownItemButton: { + height: 57, + justifyContent: 'center' + }, + dropdownItemContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center' + }, + dropdownItemText: { + fontSize: 18, + flex: 1, + ...sharedStyles.textColorNormal, + ...sharedStyles.textRegular + }, + dropdownItemDescription: { + fontSize: 14, + flex: 1, + marginTop: 2, + ...sharedStyles.textColorDescription, + ...sharedStyles.textRegular + }, + dropdownToggleText: { + fontSize: 15, + flex: 1, + marginLeft: 15, + ...sharedStyles.textColorDescription, + ...sharedStyles.textRegular + }, + dropdownItemIcon: { + width: 22, + height: 22, + marginHorizontal: 15, + ...sharedStyles.textColorDescription + }, + dropdownSeparator: { + height: StyleSheet.hairlineWidth, + backgroundColor: COLOR_SEPARATOR, + marginHorizontal: 15, + flex: 1 + }, + directoryItemButton: { + height: 54, + backgroundColor: COLOR_WHITE + }, + directoryItemContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 15 + }, + directoryItemAvatar: { + marginRight: 12 + }, + directoryItemTextTitle: { + flexDirection: 'row', + alignItems: 'center' + }, + directoryItemTextContainer: { + flex: 1, + flexDirection: 'column', + justifyContent: 'center' + }, + directoryItemName: { + flex: 1, + fontSize: 17, + ...sharedStyles.textMedium, + ...sharedStyles.textColorNormal + }, + directoryItemUsername: { + fontSize: 14, + ...sharedStyles.textRegular, + ...sharedStyles.textColorDescription + }, + directoryItemLabel: { + fontSize: 14, + paddingLeft: 10, + ...sharedStyles.textRegular, + ...sharedStyles.textColorDescription + }, + inverted: { + transform: [{ scaleY: -1 }] + }, + globalUsersContainer: { + padding: 15 + }, + globalUsersTextContainer: { + flex: 1, + flexDirection: 'column' + } +}); diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js index c2f495ea0..cfadd6094 100644 --- a/app/views/NewMessageView.js +++ b/app/views/NewMessageView.js @@ -40,7 +40,8 @@ const styles = StyleSheet.create({ }, createChannelIcon: { color: COLOR_PRIMARY, - marginHorizontal: 18 + marginLeft: 18, + marginRight: 15 }, createChannelText: { color: COLOR_PRIMARY, diff --git a/app/views/RoomsListView/Check.js b/app/views/RoomsListView/Check.js deleted file mode 100644 index 42685ba0a..000000000 --- a/app/views/RoomsListView/Check.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; - -import { CustomIcon } from '../../lib/Icons'; -import styles from './styles'; - -const Check = React.memo(() => ); - -export default Check; diff --git a/app/views/RoomsListView/ListHeader/Directory.js b/app/views/RoomsListView/ListHeader/Directory.js new file mode 100644 index 000000000..0e83ec175 --- /dev/null +++ b/app/views/RoomsListView/ListHeader/Directory.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { View, Text } from 'react-native'; +import PropTypes from 'prop-types'; + +import { CustomIcon } from '../../../lib/Icons'; +import I18n from '../../../i18n'; +import Touch from '../../../utils/touch'; +import styles from '../styles'; +import DisclosureIndicator from '../../../containers/DisclosureIndicator'; + + +const Directory = React.memo(({ goDirectory }) => ( + + + + {I18n.t('Directory')} + + + +)); + +Directory.propTypes = { + goDirectory: PropTypes.func +}; + +export default Directory; diff --git a/app/views/RoomsListView/ListHeader/index.js b/app/views/RoomsListView/ListHeader/index.js index 92743b39d..fd35f0b57 100644 --- a/app/views/RoomsListView/ListHeader/index.js +++ b/app/views/RoomsListView/ListHeader/index.js @@ -2,13 +2,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import SearchBar from './SearchBar'; +import Directory from './Directory'; import Sort from './Sort'; const ListHeader = React.memo(({ - searchLength, sortBy, onChangeSearchText, toggleSort + searchLength, sortBy, onChangeSearchText, toggleSort, goDirectory }) => ( + )); @@ -17,7 +19,8 @@ ListHeader.propTypes = { searchLength: PropTypes.number, sortBy: PropTypes.string, onChangeSearchText: PropTypes.func, - toggleSort: PropTypes.func + toggleSort: PropTypes.func, + goDirectory: PropTypes.func }; export default ListHeader; diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js index 795344609..4bc8db9d1 100644 --- a/app/views/RoomsListView/ServerDropdown.js +++ b/app/views/RoomsListView/ServerDropdown.js @@ -16,7 +16,7 @@ import Touch from '../../utils/touch'; import RocketChat from '../../lib/rocketchat'; import I18n from '../../i18n'; import EventEmitter from '../../utils/events'; -import Check from './Check'; +import Check from '../../containers/Check'; const ROW_HEIGHT = 68; const ANIMATION_DURATION = 200; diff --git a/app/views/RoomsListView/SortDropdown.js b/app/views/RoomsListView/SortDropdown.js index ea7efaefe..163b4d1e7 100644 --- a/app/views/RoomsListView/SortDropdown.js +++ b/app/views/RoomsListView/SortDropdown.js @@ -12,7 +12,7 @@ import { setPreference } from '../../actions/sortPreferences'; import log from '../../utils/log'; import I18n from '../../i18n'; import { CustomIcon } from '../../lib/Icons'; -import Check from './Check'; +import Check from '../../containers/Check'; const ANIMATION_DURATION = 200; @@ -106,7 +106,7 @@ export default class Sort extends PureComponent { render() { const translateY = this.animatedValue.interpolate({ inputRange: [0, 1], - outputRange: [-245, 41] + outputRange: [-326, 0] }); const backdropOpacity = this.animatedValue.interpolate({ inputRange: [0, 1], @@ -117,14 +117,24 @@ export default class Sort extends PureComponent { } = this.props; return ( - [ + - , + + + + {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })} + + + @@ -161,18 +171,8 @@ export default class Sort extends PureComponent { {showUnread ? : null} - , - - - {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })} - - - - ] + + ); } } diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index d3574c339..f29d62348 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -379,6 +379,11 @@ export default class RoomsListView extends React.Component { }, 100); } + goDirectory = () => { + const { navigation } = this.props; + navigation.navigate('DirectoryView'); + } + getScrollRef = ref => this.scroll = ref renderListHeader = () => { @@ -390,6 +395,7 @@ export default class RoomsListView extends React.Component { sortBy={sortBy} onChangeSearchText={this.search} toggleSort={this.toggleSort} + goDirectory={this.goDirectory} /> ); } diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js index 95c111664..0c19c11ee 100644 --- a/app/views/RoomsListView/styles.js +++ b/app/views/RoomsListView/styles.js @@ -1,7 +1,7 @@ import { StyleSheet } from 'react-native'; import { isIOS } from '../../utils/deviceInfo'; import { - COLOR_SEPARATOR, COLOR_TEXT, COLOR_PRIMARY, COLOR_WHITE + COLOR_SEPARATOR, COLOR_TEXT, COLOR_PRIMARY, COLOR_WHITE, COLOR_TEXT_DESCRIPTION } from '../../constants/colors'; import sharedStyles from '../Styles'; @@ -147,5 +147,17 @@ export default StyleSheet.create({ height: StyleSheet.hairlineWidth, backgroundColor: COLOR_SEPARATOR, marginLeft: 72 + }, + directoryIcon: { + width: 22, + height: 22, + marginHorizontal: 15, + color: isIOS ? COLOR_PRIMARY : COLOR_TEXT_DESCRIPTION + }, + directoryText: { + fontSize: 15, + flex: 1, + color: isIOS ? COLOR_PRIMARY : COLOR_TEXT_DESCRIPTION, + ...sharedStyles.textRegular } });