diff --git a/.expo/packager-info.json b/.expo/packager-info.json
new file mode 100644
index 000000000..24e8ec399
--- /dev/null
+++ b/.expo/packager-info.json
@@ -0,0 +1,5 @@
+{
+ "expoServerPort": null,
+ "packagerPort": null,
+ "packagerPid": null
+}
\ No newline at end of file
diff --git a/.expo/settings.json b/.expo/settings.json
new file mode 100644
index 000000000..5eee78b49
--- /dev/null
+++ b/.expo/settings.json
@@ -0,0 +1,9 @@
+{
+ "hostType": "tunnel",
+ "lanType": "ip",
+ "dev": true,
+ "strict": false,
+ "minify": false,
+ "urlType": "exp",
+ "urlRandomness": null
+}
\ No newline at end of file
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 52102d67d..ba368a144 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -154,6 +154,39 @@ const RocketChat = {
return resolve(result);
});
});
+ },
+
+ spotlight(search, usernames) {
+ return new Promise((resolve, reject) => {
+ Meteor.call('spotlight', search, usernames, (error, result) => {
+ if (error) {
+ return reject(error);
+ }
+ return resolve(result);
+ });
+ });
+ },
+
+ createDirectMessage(username) {
+ return new Promise((resolve, reject) => {
+ Meteor.call('createDirectMessage', username, (error, result) => {
+ if (error) {
+ return reject(error);
+ }
+ return resolve(result);
+ });
+ });
+ },
+
+ joinRoom(rid) {
+ return new Promise((resolve, reject) => {
+ Meteor.call('joinRoom', rid, (error, result) => {
+ if (error) {
+ return reject(error);
+ }
+ return resolve(result);
+ });
+ });
}
};
diff --git a/app/views/room.js b/app/views/room.js
index d8d52f527..69619f2a4 100644
--- a/app/views/room.js
+++ b/app/views/room.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Text, View, FlatList, StyleSheet } from 'react-native';
+import { Text, View, FlatList, StyleSheet, Button } from 'react-native';
import realm from '../lib/realm';
import RocketChat from '../lib/rocketchat';
@@ -36,17 +36,18 @@ export default class RoomView extends React.Component {
}
static navigationOptions = ({ navigation }) => ({
- title: realm.objectForPrimaryKey('subscriptions', navigation.state.params.sid).name
+ title: navigation.state.params.name || realm.objectForPrimaryKey('subscriptions', navigation.state.params.sid).name
});
constructor(props) {
super(props);
- this.rid = realm.objectForPrimaryKey('subscriptions', props.navigation.state.params.sid).rid;
+ this.rid = props.navigation.state.params.rid || realm.objectForPrimaryKey('subscriptions', props.navigation.state.params.sid).rid;
// this.rid = 'GENERAL';
this.state = {
dataSource: this.getMessages(),
- loaded: false
+ loaded: false,
+ joined: typeof props.navigation.state.params.rid === 'undefined'
};
this.url = realm.objectForPrimaryKey('settings', 'Site_Url').value;
@@ -77,13 +78,14 @@ export default class RoomView extends React.Component {
sendMessage = message => RocketChat.sendMessage(this.rid, message);
- renderItem = ({ item }) => (
-
- );
+ joinRoom = () => {
+ RocketChat.joinRoom(this.props.navigation.state.params.rid)
+ .then(() => {
+ this.setState({
+ joined: true
+ });
+ });
+ };
renderBanner = () => {
if (this.state.loaded === false) {
@@ -93,6 +95,34 @@ export default class RoomView extends React.Component {
);
}
+ };
+
+ renderItem = ({ item }) => (
+
+ );
+
+ renderSeparator = () => (
+
+ );
+
+ renderFooter = () => {
+ if (!this.state.joined) {
+ return (
+
+ You are in preview mode.
+
+
+ );
+ }
+ return (
+
+ );
}
render() {
@@ -107,9 +137,7 @@ export default class RoomView extends React.Component {
renderItem={this.renderItem}
keyExtractor={item => item._id}
/>
-
+ {this.renderFooter()}
);
}
diff --git a/app/views/roomsList.js b/app/views/roomsList.js
index e9d69be4f..bdbc9ff18 100644
--- a/app/views/roomsList.js
+++ b/app/views/roomsList.js
@@ -2,7 +2,7 @@ import ActionButton from 'react-native-action-button';
import Icon from 'react-native-vector-icons/Ionicons';
import React from 'react';
import PropTypes from 'prop-types';
-import { Text, View, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
+import { Text, View, FlatList, StyleSheet, TouchableOpacity, Platform, TextInput } from 'react-native';
import Meteor from 'react-native-meteor';
import realm from '../lib/realm';
import RocketChat from '../lib/rocketchat';
@@ -80,8 +80,13 @@ export default class RoomsListView extends React.Component {
constructor(props) {
super(props);
- this._listViewOffset = 0;
- this.state = this.getState();
+
+ this.state = {
+ dataSource: this.getSubscriptions(),
+ searching: false,
+ searchDataSource: [],
+ searchText: ''
+ };
}
componentWillMount() {
@@ -100,28 +105,97 @@ export default class RoomsListView extends React.Component {
realm.removeListener('change', this.updateState);
}
- getState = () => ({
- dataSource: realm.objects('subscriptions').filtered('_server.id = $0', RocketChat.currentServer).sorted('name').slice()
- .sort((a, b) => {
- if (a.unread < b.unread) {
- return 1;
+ onSearchChangeText = (text) => {
+ const searchText = text.trim();
+ this.setState({
+ searchText: text,
+ searching: searchText !== ''
+ });
+
+ if (searchText !== '') {
+ const dataSource = [];
+ const usernames = [];
+ realm.objects('subscriptions').filtered('_server.id = $0 AND name CONTAINS[c] $1', RocketChat.currentServer, searchText).forEach((sub) => {
+ dataSource.push(sub);
+
+ if (sub.t === 'd') {
+ usernames.push(sub.name);
}
+ });
- if (a.unread > b.unread) {
- return -1;
- }
+ if (dataSource.length < 5) {
+ RocketChat.spotlight(searchText, usernames)
+ .then((results) => {
+ results.users.forEach((user) => {
+ dataSource.push({
+ ...user,
+ name: user.username,
+ t: 'd',
+ search: true
+ });
+ });
- return 0;
- })
- })
+ results.rooms.forEach((room) => {
+ dataSource.push({
+ ...room,
+ search: true
+ });
+ });
- updateState = () => {
- this.setState(this.getState());
+ this.setState({
+ searchDataSource: dataSource
+ });
+ });
+ }
+ }
}
- _onPressItem = (id) => {
+ getSubscriptions = () => realm.objects('subscriptions').filtered('_server.id = $0', RocketChat.currentServer).sorted('name').slice()
+ .sort((a, b) => {
+ if (a.unread < b.unread) {
+ return 1;
+ }
+
+ if (a.unread > b.unread) {
+ return -1;
+ }
+
+ return 0;
+ });
+
+ updateState = () => {
+ this.setState({
+ dataSource: this.getSubscriptions()
+ });
+ }
+
+ _onPressItem = (id, item) => {
const { navigate } = this.props.navigation;
+
+ const clearSearch = () => {
+ this.setState({
+ searchText: '',
+ searching: false,
+ searchDataSource: []
+ });
+ };
+
+ // if user is using the search we need first to join/create room
+ if (item.search) {
+ if (item.t === 'd') {
+ RocketChat.createDirectMessage(item.username)
+ .then(room => realm.objects('subscriptions').filtered('_server.id = $0 AND rid = $1', RocketChat.currentServer, room.rid))
+ .then(subs => navigate('Room', { sid: subs[0]._id }))
+ .then(() => clearSearch());
+ } else {
+ navigate('Room', { rid: item._id, name: item.name });
+ clearSearch();
+ }
+ return;
+ }
+
navigate('Room', { sid: id });
+ clearSearch();
}
_createChannel = () => {
const { navigate } = this.props.navigation;
@@ -160,23 +234,33 @@ export default class RoomsListView extends React.Component {
);
+ renderSearchBar = () => (
+
+ );
+
renderList = () => {
- if (this.state.dataSource.length) {
+ if (!this.state.searching && !this.state.dataSource.length) {
return (
- item._id}
- ItemSeparatorComponent={this.renderSeparator}
- />
+
+ No rooms
+
);
}
return (
-
- No rooms
-
+ item._id}
+ ItemSeparatorComponent={this.renderSeparator}
+ />
);
}
renderCreateButtons() {
@@ -191,6 +275,7 @@ export default class RoomsListView extends React.Component {
return (
{this.renderBanner()}
+ {this.renderSearchBar()}
{this.renderList()}
{this.renderCreateButtons()}
diff --git a/package.json b/package.json
index 2af10f30a..c459c63b0 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"react": "16.0.0-alpha.12",
"react-emojione": "^3.1.10",
"react-native": "0.46.1",
+ "react-native-action-button": "^2.7.2",
"react-native-easy-markdown": "git+https://github.com/lappalj4/react-native-easy-markdown.git",
"react-native-fetch-blob": "^0.10.8",
"react-native-form-generator": "^0.9.9",