Create room (#42)
* Added select users view * create room working * - Show photo on avatar * Switched state for redux * Navigating to created room
This commit is contained in:
parent
d55db0fca5
commit
e0777a969e
|
@ -1,11 +1,10 @@
|
||||||
|
|
||||||
const REQUEST = 'REQUEST';
|
const REQUEST = 'REQUEST';
|
||||||
const SUCCESS = 'SUCCESS';
|
const SUCCESS = 'SUCCESS';
|
||||||
const FAILURE = 'FAILURE';
|
const FAILURE = 'FAILURE';
|
||||||
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
||||||
function createRequestTypes(base, types = defaultTypes) {
|
function createRequestTypes(base, types = defaultTypes) {
|
||||||
const res = {};
|
const res = {};
|
||||||
types.forEach(type => res[type] = `${ base }_${ type }`);
|
types.forEach(type => (res[type] = `${ base }_${ type }`));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +13,16 @@ export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN',
|
||||||
export const ROOMS = createRequestTypes('ROOMS');
|
export const ROOMS = createRequestTypes('ROOMS');
|
||||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
||||||
export const MESSAGES = createRequestTypes('MESSAGES');
|
export const MESSAGES = createRequestTypes('MESSAGES');
|
||||||
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes, 'REQUEST_USERS', 'SUCCESS_USERS', 'FAILURE_USERS', 'SET_USERS']);
|
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [
|
||||||
|
...defaultTypes,
|
||||||
|
'REQUEST_USERS',
|
||||||
|
'SUCCESS_USERS',
|
||||||
|
'FAILURE_USERS',
|
||||||
|
'SET_USERS',
|
||||||
|
'ADD_USER',
|
||||||
|
'REMOVE_USER',
|
||||||
|
'RESET'
|
||||||
|
]);
|
||||||
export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
|
export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
|
||||||
export const SERVER = createRequestTypes('SERVER', [...defaultTypes, 'SELECT', 'CHANGED', 'ADD']);
|
export const SERVER = createRequestTypes('SERVER', [...defaultTypes, 'SELECT', 'CHANGED', 'ADD']);
|
||||||
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
|
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
|
||||||
|
|
|
@ -21,7 +21,6 @@ export function createChannelFailure(err) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function createChannelRequestUsers(data) {
|
export function createChannelRequestUsers(data) {
|
||||||
return {
|
return {
|
||||||
type: types.CREATE_CHANNEL.REQUEST_USERS,
|
type: types.CREATE_CHANNEL.REQUEST_USERS,
|
||||||
|
@ -49,3 +48,23 @@ export function createChannelFailureUsers(err) {
|
||||||
err
|
err
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addUser(user) {
|
||||||
|
return {
|
||||||
|
type: types.CREATE_CHANNEL.ADD_USER,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeUser(user) {
|
||||||
|
return {
|
||||||
|
type: types.CREATE_CHANNEL.REMOVE_USER,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reset() {
|
||||||
|
return {
|
||||||
|
type: types.CREATE_CHANNEL.RESET
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StackNavigator, DrawerNavigator } from 'react-navigation';
|
import { Button } from 'react-native';
|
||||||
|
import { StackNavigator, DrawerNavigator, NavigationActions } from 'react-navigation';
|
||||||
// import { Platform } from 'react-native';
|
// import { Platform } from 'react-native';
|
||||||
|
|
||||||
import Sidebar from '../../containers/Sidebar';
|
import Sidebar from '../../containers/Sidebar';
|
||||||
|
@ -8,10 +9,18 @@ import DrawerMenuButton from '../../presentation/DrawerMenuButton';
|
||||||
import RoomsListView from '../../views/RoomsListView';
|
import RoomsListView from '../../views/RoomsListView';
|
||||||
import RoomView from '../../views/RoomView';
|
import RoomView from '../../views/RoomView';
|
||||||
import CreateChannelView from '../../views/CreateChannelView';
|
import CreateChannelView from '../../views/CreateChannelView';
|
||||||
|
import SelectUsersView from '../../views/SelectUsersView';
|
||||||
|
|
||||||
const drawerPosition = 'left';
|
const drawerPosition = 'left';
|
||||||
const drawerIconPosition = 'headerLeft';
|
const drawerIconPosition = 'headerLeft';
|
||||||
|
|
||||||
|
const backToScreen = (navigation, routeName) => {
|
||||||
|
const action = NavigationActions.reset({
|
||||||
|
index: 0,
|
||||||
|
actions: [NavigationActions.navigate({ routeName })]
|
||||||
|
});
|
||||||
|
navigation.dispatch(action);
|
||||||
|
};
|
||||||
|
|
||||||
const AuthRoutes = StackNavigator(
|
const AuthRoutes = StackNavigator(
|
||||||
{
|
{
|
||||||
|
@ -20,7 +29,7 @@ const AuthRoutes = StackNavigator(
|
||||||
navigationOptions({ navigation }) {
|
navigationOptions({ navigation }) {
|
||||||
return {
|
return {
|
||||||
title: 'Rooms',
|
title: 'Rooms',
|
||||||
[drawerIconPosition]: (<DrawerMenuButton navigation={navigation} />)
|
[drawerIconPosition]: <DrawerMenuButton navigation={navigation} />
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -28,7 +37,10 @@ const AuthRoutes = StackNavigator(
|
||||||
screen: RoomView,
|
screen: RoomView,
|
||||||
navigationOptions({ navigation }) {
|
navigationOptions({ navigation }) {
|
||||||
return {
|
return {
|
||||||
title: navigation.state.params.title || 'Room'
|
title: navigation.state.params.title || 'Room',
|
||||||
|
headerLeft: (
|
||||||
|
<Button title={'Back'} onPress={() => backToScreen(navigation, 'RoomsList')} />
|
||||||
|
)
|
||||||
// [drawerIconPosition]: (<DrawerMenuButton navigation={navigation} />)÷
|
// [drawerIconPosition]: (<DrawerMenuButton navigation={navigation} />)÷
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -38,25 +50,33 @@ const AuthRoutes = StackNavigator(
|
||||||
navigationOptions: {
|
navigationOptions: {
|
||||||
title: 'Create Channel'
|
title: 'Create Channel'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
SelectUsers: {
|
||||||
|
screen: SelectUsersView,
|
||||||
|
navigationOptions: {
|
||||||
|
title: 'Select Users'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const Routes = DrawerNavigator(
|
||||||
|
{
|
||||||
|
Home: {
|
||||||
|
screen: AuthRoutes,
|
||||||
|
navigationOptions({ navigation }) {
|
||||||
|
return {
|
||||||
|
title: 'Rooms',
|
||||||
|
[drawerIconPosition]: <DrawerMenuButton navigation={navigation} />
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
contentComponent: Sidebar,
|
||||||
|
drawerPosition
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const Routes = DrawerNavigator({
|
|
||||||
Home: {
|
|
||||||
screen: AuthRoutes,
|
|
||||||
navigationOptions({ navigation }) {
|
|
||||||
return {
|
|
||||||
title: 'Rooms',
|
|
||||||
[drawerIconPosition]: (<DrawerMenuButton navigation={navigation} />)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
contentComponent: Sidebar,
|
|
||||||
drawerPosition
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Routes;
|
export default Routes;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'regenerator-runtime/runtime';
|
||||||
|
|
||||||
import { createStore, applyMiddleware } from 'redux';
|
import { createStore, applyMiddleware } from 'redux';
|
||||||
import createSagaMiddleware from 'redux-saga';
|
import createSagaMiddleware from 'redux-saga';
|
||||||
|
import logger from 'redux-logger';
|
||||||
import { composeWithDevTools } from 'remote-redux-devtools';
|
import { composeWithDevTools } from 'remote-redux-devtools';
|
||||||
import reducers from '../reducers';
|
import reducers from '../reducers';
|
||||||
import sagas from '../sagas';
|
import sagas from '../sagas';
|
||||||
|
@ -13,9 +14,11 @@ let enhacers;
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default();
|
||||||
|
|
||||||
enhacers = composeWithDevTools(
|
enhacers = composeWithDevTools(
|
||||||
applyMiddleware(reduxImmutableStateInvariant),
|
applyMiddleware(reduxImmutableStateInvariant),
|
||||||
applyMiddleware(sagaMiddleware)
|
applyMiddleware(sagaMiddleware),
|
||||||
|
applyMiddleware(logger)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
enhacers = composeWithDevTools(
|
enhacers = composeWithDevTools(
|
||||||
|
|
|
@ -2,29 +2,45 @@ import { CREATE_CHANNEL } from '../actions/actionsTypes';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
failure: false
|
failure: false,
|
||||||
|
users: []
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function messages(state = initialState, action) {
|
export default function messages(state = initialState, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case CREATE_CHANNEL.REQUEST:
|
case CREATE_CHANNEL.REQUEST:
|
||||||
return { ...state,
|
return {
|
||||||
|
...state,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
failure: false,
|
failure: false,
|
||||||
isFetching: true
|
isFetching: true
|
||||||
};
|
};
|
||||||
case CREATE_CHANNEL.SUCCESS:
|
case CREATE_CHANNEL.SUCCESS:
|
||||||
return { ...state,
|
return {
|
||||||
|
...state,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
failure: false,
|
failure: false,
|
||||||
result: action.data
|
result: action.data
|
||||||
};
|
};
|
||||||
case CREATE_CHANNEL.FAILURE:
|
case CREATE_CHANNEL.FAILURE:
|
||||||
return { ...state,
|
return {
|
||||||
|
...state,
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
failure: true,
|
failure: true,
|
||||||
error: action.err
|
error: action.err
|
||||||
};
|
};
|
||||||
|
case CREATE_CHANNEL.ADD_USER:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
users: state.users.concat(action.user)
|
||||||
|
};
|
||||||
|
case CREATE_CHANNEL.REMOVE_USER:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
users: state.users.filter(item => item.name !== action.user.name)
|
||||||
|
};
|
||||||
|
case CREATE_CHANNEL.RESET:
|
||||||
|
return initialState;
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,29 @@
|
||||||
import { delay } from 'redux-saga';
|
import { delay } from 'redux-saga';
|
||||||
import { select, put, call, take, takeEvery } from 'redux-saga/effects';
|
import { select, put, call, take, takeLatest } from 'redux-saga/effects';
|
||||||
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
||||||
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
|
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
|
||||||
|
|
||||||
const create = function* create(data) {
|
const create = function* create(data) {
|
||||||
return yield RocketChat.createChannel(data);
|
return yield RocketChat.createChannel(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const get = function* get({ data }) {
|
const get = function* get({ data }) {
|
||||||
try {
|
try {
|
||||||
|
yield delay(1000);
|
||||||
const auth = yield select(state => state.login.isAuthenticated);
|
const auth = yield select(state => state.login.isAuthenticated);
|
||||||
if (!auth) {
|
if (!auth) {
|
||||||
yield take(LOGIN.SUCCESS);
|
yield take(LOGIN.SUCCESS);
|
||||||
}
|
}
|
||||||
const result = yield call(create, data);
|
const result = yield call(create, data);
|
||||||
yield put(createChannelSuccess(result));
|
yield put(createChannelSuccess(result));
|
||||||
select(({ navigator }) => navigator).dismissModal({
|
|
||||||
animationType: 'slide-down'
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
yield delay(2000);
|
|
||||||
yield put(createChannelFailure(err));
|
yield put(createChannelFailure(err));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getData = function* getData() {
|
const getData = function* getData() {
|
||||||
yield takeEvery(CREATE_CHANNEL.REQUEST, get);
|
yield takeLatest(CREATE_CHANNEL.REQUEST, get);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getData;
|
export default getData;
|
||||||
|
|
|
@ -6,20 +6,25 @@ import { createChannelRequest } from '../actions/createChannel';
|
||||||
import styles from './Styles';
|
import styles from './Styles';
|
||||||
import KeyboardView from '../presentation/KeyboardView';
|
import KeyboardView from '../presentation/KeyboardView';
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(
|
||||||
result: state.createChannel
|
state => ({
|
||||||
}), dispatch => ({
|
result: state.createChannel,
|
||||||
createChannel: data => dispatch(createChannelRequest(data))
|
users: state.createChannel.users
|
||||||
}))
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
createChannel: data => dispatch(createChannelRequest(data))
|
||||||
|
})
|
||||||
|
)
|
||||||
export default class CreateChannelView extends React.Component {
|
export default class CreateChannelView extends React.Component {
|
||||||
static navigationOptions = () => ({
|
static navigationOptions = () => ({
|
||||||
title: 'Create a New Channel'
|
title: 'Create a New Channel'
|
||||||
});
|
});
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
createChannel: PropTypes.func.isRequired,
|
createChannel: PropTypes.func.isRequired,
|
||||||
result: PropTypes.object.isRequired
|
result: PropTypes.object.isRequired,
|
||||||
}
|
users: PropTypes.array.isRequired,
|
||||||
|
navigation: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -29,23 +34,42 @@ export default class CreateChannelView extends React.Component {
|
||||||
};
|
};
|
||||||
this.state = this.default;
|
this.state = this.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
if (!this.adding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.props.result.result && !this.props.result.failure) {
|
||||||
|
this.props.navigation.navigate('Room', { room: this.props.result.result });
|
||||||
|
this.adding = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
|
this.adding = true;
|
||||||
if (!this.state.channelName.trim() || this.props.result.isFetching) {
|
if (!this.state.channelName.trim() || this.props.result.isFetching) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { channelName, users = [], type = true } = this.state;
|
const { channelName, type = true } = this.state;
|
||||||
|
let { users } = this.props;
|
||||||
|
|
||||||
|
// transform users object into array of usernames
|
||||||
|
users = users.map(user => user.name);
|
||||||
|
|
||||||
|
// create channel
|
||||||
this.props.createChannel({ name: channelName, users, type });
|
this.props.createChannel({ name: channelName, users, type });
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChannelNameError() {
|
renderChannelNameError() {
|
||||||
if (!this.props.result.failure || this.props.result.error.error !== 'error-duplicate-channel-name') {
|
if (
|
||||||
|
!this.props.result.failure ||
|
||||||
|
this.props.result.error.error !== 'error-duplicate-channel-name'
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.label_white, styles.label_error]}>
|
<Text style={[styles.label_white, styles.label_error]}>{this.props.result.error.reason}</Text>
|
||||||
{this.props.result.error.reason}
|
|
||||||
</Text>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,18 +115,22 @@ export default class CreateChannelView extends React.Component {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
paddingHorizontal: 0,
|
paddingHorizontal: 0,
|
||||||
marginBottom: 20
|
marginBottom: 20
|
||||||
}]}
|
}
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{this.state.type ?
|
{this.state.type ? (
|
||||||
'Everyone can access this channel' :
|
'Everyone can access this channel'
|
||||||
'Just invited people can access this channel'}
|
) : (
|
||||||
|
'Just invited people can access this channel'
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => this.submit()}
|
onPress={() => this.submit()}
|
||||||
style={[
|
style={[
|
||||||
styles.buttonContainer_white,
|
styles.buttonContainer_white,
|
||||||
(this.state.channelName.length === 0 || this.props.result.isFetching) ?
|
this.state.channelName.length === 0 || this.props.result.isFetching
|
||||||
styles.disabledButton : styles.enabledButton
|
? styles.disabledButton
|
||||||
|
: styles.enabledButton
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Text style={styles.button_white}>
|
<Text style={styles.button_white}>
|
||||||
|
|
|
@ -43,16 +43,18 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@connect(
|
||||||
@connect(state => ({
|
state => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Site_Url: state.settings.Site_Url,
|
Site_Url: state.settings.Site_Url,
|
||||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||||
loading: state.messages.isFetching
|
loading: state.messages.isFetching
|
||||||
}), dispatch => ({
|
}),
|
||||||
actions: bindActionCreators(actions, dispatch),
|
dispatch => ({
|
||||||
getMessages: rid => dispatch(messagesRequest({ rid }))
|
actions: bindActionCreators(actions, dispatch),
|
||||||
}))
|
getMessages: rid => dispatch(messagesRequest({ rid }))
|
||||||
|
})
|
||||||
|
)
|
||||||
export default class RoomView extends React.Component {
|
export default class RoomView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object.isRequired,
|
navigation: PropTypes.object.isRequired,
|
||||||
|
@ -64,15 +66,21 @@ export default class RoomView extends React.Component {
|
||||||
Site_Url: PropTypes.string,
|
Site_Url: PropTypes.string,
|
||||||
Message_TimeFormat: PropTypes.string,
|
Message_TimeFormat: PropTypes.string,
|
||||||
loading: PropTypes.bool
|
loading: PropTypes.bool
|
||||||
}
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.sid = props.navigation.state.params.room.sid;
|
this.sid = props.navigation.state.params.room.sid;
|
||||||
this.rid = props.rid || realm.objectForPrimaryKey('subscriptions', this.sid).rid;
|
this.rid =
|
||||||
|
props.rid ||
|
||||||
|
props.navigation.state.params.room.rid ||
|
||||||
|
realm.objectForPrimaryKey('subscriptions', this.sid).rid;
|
||||||
|
|
||||||
this.data = realm.objects('messages').filtered('_server.id = $0 AND rid = $1', this.props.server, this.rid).sorted('ts', true);
|
this.data = realm
|
||||||
|
.objects('messages')
|
||||||
|
.filtered('_server.id = $0 AND rid = $1', this.props.server, this.rid)
|
||||||
|
.sorted('ts', true);
|
||||||
this.state = {
|
this.state = {
|
||||||
slow: false,
|
slow: false,
|
||||||
dataSource: [],
|
dataSource: [],
|
||||||
|
@ -83,7 +91,10 @@ export default class RoomView extends React.Component {
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.props.navigation.setParams({
|
this.props.navigation.setParams({
|
||||||
title: this.props.name || realm.objectForPrimaryKey('subscriptions', this.sid).name
|
title:
|
||||||
|
this.props.name ||
|
||||||
|
this.props.navigation.state.params.room.name ||
|
||||||
|
realm.objectForPrimaryKey('subscriptions', this.sid).name
|
||||||
});
|
});
|
||||||
this.timer = setTimeout(() => this.setState({ slow: true }), 5000);
|
this.timer = setTimeout(() => this.setState({ slow: true }), 5000);
|
||||||
this.props.getMessages(this.rid);
|
this.props.getMessages(this.rid);
|
||||||
|
@ -103,7 +114,12 @@ export default class RoomView extends React.Component {
|
||||||
|
|
||||||
onEndReached = () => {
|
onEndReached = () => {
|
||||||
const rowCount = this.state.dataSource.getRowCount();
|
const rowCount = this.state.dataSource.getRowCount();
|
||||||
if (rowCount && this.state.loaded && this.state.loadingMore !== true && this.state.end !== true) {
|
if (
|
||||||
|
rowCount &&
|
||||||
|
this.state.loaded &&
|
||||||
|
this.state.loadingMore !== true &&
|
||||||
|
this.state.end !== true
|
||||||
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
// ...this.state,
|
// ...this.state,
|
||||||
loadingMore: true
|
loadingMore: true
|
||||||
|
@ -118,7 +134,7 @@ export default class RoomView extends React.Component {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
updateState = () => {
|
updateState = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -135,13 +151,12 @@ export default class RoomView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderBanner = () => (this.state.slow && this.props.loading ?
|
renderBanner = () =>
|
||||||
(
|
(this.state.slow && this.props.loading ? (
|
||||||
<View style={styles.bannerContainer}>
|
<View style={styles.bannerContainer}>
|
||||||
<Text style={styles.bannerText}>Loading new messages...</Text>
|
<Text style={styles.bannerText}>Loading new messages...</Text>
|
||||||
</View>
|
</View>
|
||||||
) : null)
|
) : null);
|
||||||
|
|
||||||
|
|
||||||
renderItem = ({ item }) => (
|
renderItem = ({ item }) => (
|
||||||
<Message
|
<Message
|
||||||
|
@ -152,9 +167,7 @@ export default class RoomView extends React.Component {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
renderSeparator = () => (
|
renderSeparator = () => <View style={styles.separator} />;
|
||||||
<View style={styles.separator} />
|
|
||||||
);
|
|
||||||
|
|
||||||
renderFooter = () => {
|
renderFooter = () => {
|
||||||
if (!this.state.joined) {
|
if (!this.state.joined) {
|
||||||
|
@ -165,14 +178,8 @@ export default class RoomView extends React.Component {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return <MessageBox ref={box => (this.box = box)} onSubmit={this.sendMessage} rid={this.rid} />;
|
||||||
<MessageBox
|
};
|
||||||
ref={box => this.box = box}
|
|
||||||
onSubmit={this.sendMessage}
|
|
||||||
rid={this.rid}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderHeader = () => {
|
renderHeader = () => {
|
||||||
if (this.state.loadingMore) {
|
if (this.state.loadingMore) {
|
||||||
|
@ -182,7 +189,7 @@ export default class RoomView extends React.Component {
|
||||||
if (this.state.end) {
|
if (this.state.end) {
|
||||||
return <Text style={styles.header}>Start of conversation</Text>;
|
return <Text style={styles.header}>Start of conversation</Text>;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -207,7 +207,7 @@ export default class RoomsListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createChannel() {
|
_createChannel() {
|
||||||
this.props.navigation.navigate('CreateChannel');
|
this.props.navigation.navigate('SelectUsers');
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSearchBar = () => (
|
renderSearchBar = () => (
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
import ActionButton from 'react-native-action-button';
|
||||||
|
import { ListView } from 'realm/react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Icon from 'react-native-vector-icons/Ionicons';
|
||||||
|
import { View, StyleSheet, TextInput, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import * as actions from '../actions';
|
||||||
|
import * as server from '../actions/connect';
|
||||||
|
import * as createChannelActions from '../actions/createChannel';
|
||||||
|
import realm from '../lib/realm';
|
||||||
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
import RoomItem from '../presentation/RoomItem';
|
||||||
|
import Banner from '../containers/Banner';
|
||||||
|
import Avatar from '../containers/Avatar';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'stretch',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: '#FFFFFF'
|
||||||
|
},
|
||||||
|
actionButtonIcon: {
|
||||||
|
fontSize: 20,
|
||||||
|
height: 22,
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
searchBoxView: {
|
||||||
|
backgroundColor: '#eee'
|
||||||
|
},
|
||||||
|
searchBox: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
margin: 5,
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 5,
|
||||||
|
paddingLeft: 10,
|
||||||
|
color: '#aaa'
|
||||||
|
},
|
||||||
|
selectItemView: {
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
padding: 8,
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
|
||||||
|
@connect(
|
||||||
|
state => ({
|
||||||
|
server: state.server.server,
|
||||||
|
login: state.login,
|
||||||
|
Site_Url: state.settings.Site_Url,
|
||||||
|
users: state.createChannel.users
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
login: () => dispatch(actions.login()),
|
||||||
|
connect: () => dispatch(server.connectRequest()),
|
||||||
|
addUser: user => dispatch(createChannelActions.addUser(user)),
|
||||||
|
removeUser: user => dispatch(createChannelActions.removeUser(user)),
|
||||||
|
resetCreateChannel: () => dispatch(createChannelActions.reset())
|
||||||
|
})
|
||||||
|
)
|
||||||
|
export default class RoomsListView extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
navigation: PropTypes.object.isRequired,
|
||||||
|
Site_Url: PropTypes.string,
|
||||||
|
server: PropTypes.string,
|
||||||
|
addUser: PropTypes.func.isRequired,
|
||||||
|
removeUser: PropTypes.func.isRequired,
|
||||||
|
resetCreateChannel: PropTypes.func.isRequired,
|
||||||
|
users: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.data = realm
|
||||||
|
.objects('subscriptions')
|
||||||
|
.filtered('_server.id = $0 AND t = $1', this.props.server, 'd');
|
||||||
|
this.state = {
|
||||||
|
dataSource: ds.cloneWithRows(this.data),
|
||||||
|
searching: false,
|
||||||
|
searchDataSource: [],
|
||||||
|
searchText: '',
|
||||||
|
login: false
|
||||||
|
};
|
||||||
|
this.data.addListener(this.updateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.data.removeListener(this.updateState);
|
||||||
|
this.props.resetCreateChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchChangeText = (text) => {
|
||||||
|
const searchText = text.trim();
|
||||||
|
this.setState({
|
||||||
|
searchText: text,
|
||||||
|
searching: searchText !== ''
|
||||||
|
});
|
||||||
|
if (searchText === '') {
|
||||||
|
return this.setState({
|
||||||
|
dataSource: ds.cloneWithRows(this.data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = this.data.filtered('name CONTAINS[c] $0', searchText).slice();
|
||||||
|
|
||||||
|
const usernames = [];
|
||||||
|
const dataSource = data.map((sub) => {
|
||||||
|
if (sub.t === 'd') {
|
||||||
|
usernames.push(sub.name);
|
||||||
|
}
|
||||||
|
return sub;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dataSource.length < 7) {
|
||||||
|
if (this.oldPromise) {
|
||||||
|
this.oldPromise();
|
||||||
|
}
|
||||||
|
Promise.race([
|
||||||
|
RocketChat.spotlight(searchText, usernames),
|
||||||
|
new Promise((resolve, reject) => (this.oldPromise = reject))
|
||||||
|
])
|
||||||
|
.then(
|
||||||
|
(results) => {
|
||||||
|
results.users.forEach((user) => {
|
||||||
|
dataSource.push({
|
||||||
|
...user,
|
||||||
|
name: user.username,
|
||||||
|
t: 'd',
|
||||||
|
search: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
dataSource: ds.cloneWithRows(dataSource)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
() => console.log('spotlight stopped')
|
||||||
|
)
|
||||||
|
.then(() => delete this.oldPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
dataSource: ds.cloneWithRows(dataSource)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
updateState = () => {
|
||||||
|
this.setState({
|
||||||
|
dataSource: ds.cloneWithRows(this.data)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleUser = (user) => {
|
||||||
|
const index = this.props.users.findIndex(el => el.name === user.name);
|
||||||
|
if (index === -1) {
|
||||||
|
this.props.addUser(user);
|
||||||
|
} else {
|
||||||
|
this.props.removeUser(user);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_onPressItem = (id, item = {}) => {
|
||||||
|
if (item.search) {
|
||||||
|
this.toggleUser({ _id: item._id, name: item.username });
|
||||||
|
} else {
|
||||||
|
this.toggleUser({ _id: item._id, name: item.name });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_onPressSelectedItem = item => this.toggleUser(item);
|
||||||
|
|
||||||
|
_createChannel = () => {
|
||||||
|
this.props.navigation.navigate('CreateChannel');
|
||||||
|
};
|
||||||
|
|
||||||
|
renderHeader = () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{this.renderSearchBar()}
|
||||||
|
{this.renderSelected()}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
renderSearchBar = () => (
|
||||||
|
<View style={styles.searchBoxView}>
|
||||||
|
<TextInput
|
||||||
|
underlineColorAndroid='transparent'
|
||||||
|
style={styles.searchBox}
|
||||||
|
value={this.state.searchText}
|
||||||
|
onChangeText={this.onSearchChangeText}
|
||||||
|
returnKeyType='search'
|
||||||
|
placeholder='Search'
|
||||||
|
clearButtonMode='while-editing'
|
||||||
|
blurOnSubmit
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
renderSelected = () => {
|
||||||
|
if (this.props.users.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const usersDataSource = ds.cloneWithRows(this.props.users);
|
||||||
|
return (
|
||||||
|
<ListView
|
||||||
|
dataSource={usersDataSource}
|
||||||
|
style={styles.list}
|
||||||
|
renderRow={this.renderSelectedItem}
|
||||||
|
enableEmptySections
|
||||||
|
keyboardShouldPersistTaps='always'
|
||||||
|
horizontal
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
renderSelectedItem = item => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={item._id}
|
||||||
|
style={styles.selectItemView}
|
||||||
|
onPress={() => this._onPressSelectedItem(item)}
|
||||||
|
>
|
||||||
|
<Avatar text={item.name} baseUrl={this.props.Site_Url} size={40} borderRadius={20} />
|
||||||
|
<Text ellipsizeMode='tail' numberOfLines={1} style={{ fontSize: 10 }}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
renderItem = item => (
|
||||||
|
<RoomItem
|
||||||
|
key={item._id}
|
||||||
|
name={item.name}
|
||||||
|
type={item.t}
|
||||||
|
baseUrl={this.props.Site_Url}
|
||||||
|
onPress={() => this._onPressItem(item._id, item)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
renderList = () => (
|
||||||
|
<ListView
|
||||||
|
dataSource={this.state.dataSource}
|
||||||
|
style={styles.list}
|
||||||
|
renderRow={this.renderItem}
|
||||||
|
renderHeader={this.renderHeader}
|
||||||
|
contentOffset={{ x: 0, y: this.props.users.length > 0 ? 40 : 20 }}
|
||||||
|
enableEmptySections
|
||||||
|
keyboardShouldPersistTaps='always'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
renderCreateButton = () => {
|
||||||
|
if (this.props.users.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ActionButton
|
||||||
|
buttonColor='rgba(67, 165, 71, 1)'
|
||||||
|
onPress={() => this._createChannel()}
|
||||||
|
icon={<Icon name='md-arrow-forward' style={styles.actionButtonIcon} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
render = () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Banner />
|
||||||
|
{this.renderList()}
|
||||||
|
{this.renderCreateButton()}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
|
@ -800,7 +800,6 @@
|
||||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||||
};
|
};
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
DevelopmentTeam = S6UPZG7ZR3;
|
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
2D02E47A1E0B4A5D006451C7 = {
|
2D02E47A1E0B4A5D006451C7 = {
|
||||||
|
@ -1379,7 +1378,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEAD_CODE_STRIPPING = NO;
|
DEAD_CODE_STRIPPING = NO;
|
||||||
DEVELOPMENT_TEAM = S6UPZG7ZR3;
|
DEVELOPMENT_TEAM = "";
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
|
@ -1412,7 +1411,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = S6UPZG7ZR3;
|
DEVELOPMENT_TEAM = "";
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(SRCROOT)/../node_modules/realm/src/**",
|
"$(SRCROOT)/../node_modules/realm/src/**",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue