diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 07ca9a4c..efacd963 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -10,9 +10,10 @@ function createRequestTypes(base, types = defaultTypes) { } // Login events -export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN']); +export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN', 'SUBMIT']); export const ROOMS = createRequestTypes('ROOMS'); export const MESSAGES = createRequestTypes('MESSAGES'); +export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']); export const SERVER = createRequestTypes('SERVER', ['SELECT', 'CHANGED']); export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']); export const LOGOUT = 'LOGOUT'; // logout is always success diff --git a/app/actions/login.js b/app/actions/login.js index 4f8c248d..cffc3cdd 100644 --- a/app/actions/login.js +++ b/app/actions/login.js @@ -1,9 +1,15 @@ import * as types from './actionsTypes'; +export function loginSubmit(credentials) { + return { + type: types.LOGIN.SUBMIT, + credentials + }; +} export function loginRequest(credentials) { return { type: types.LOGIN.REQUEST, - ...credentials + credentials }; } @@ -22,10 +28,11 @@ export function loginFailure(err) { }; } -export function setToken(token) { +export function setToken(user) { return { type: types.LOGIN.SET_TOKEN, - token + token: user.token, + user }; } diff --git a/app/actions/navigator.js b/app/actions/navigator.js new file mode 100644 index 00000000..b328a235 --- /dev/null +++ b/app/actions/navigator.js @@ -0,0 +1,8 @@ +import * as types from './actionsTypes'; + +export default function setNavigation(navigator = {}) { + return { + type: types.NAVIGATION.SET, + navigator + }; +} diff --git a/app/animations/fade.js b/app/animations/fade.js new file mode 100644 index 00000000..725ccbaa --- /dev/null +++ b/app/animations/fade.js @@ -0,0 +1,53 @@ +import React from 'react'; +import { Animated, Text } from 'react-native'; + +export default class Fade extends React.Component { + constructor(props) { + super(props); + this.state = { + visible: props.visible + }; + } + + componentWillMount() { + this._visibility = new Animated.Value(this.props.visible ? 1 : 0); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.visible) { + this.setState({ visible: true }); + } + Animated.timing(this._visibility, { + toValue: nextProps.visible ? 1 : 0, + duration: 300 + }).start(() => { + this.setState({ visible: nextProps.visible }); + }); + } + + render() { + const { visible, style, children, ...rest } = this.props; + + const containerStyle = { + opacity: this._visibility.interpolate({ + inputRange: [0, 1], + outputRange: [0, 1] + }), + transform: [ + { + scale: this._visibility.interpolate({ + inputRange: [0, 1], + outputRange: [1.1, 1] + }) + } + ] + }; + + const combinedStyle = [containerStyle, style]; + return ( + + {this.state.visible ? children : null} + + ); + } +} diff --git a/app/components/banner.js b/app/components/banner.js index 77a03a1e..eb159c49 100644 --- a/app/components/banner.js +++ b/app/components/banner.js @@ -5,7 +5,11 @@ import { connect } from 'react-redux'; const styles = StyleSheet.create({ bannerContainer: { - backgroundColor: '#ddd' + backgroundColor: '#ddd', + position: 'absolute', + top: '0%', + zIndex: 10, + width: '100%' }, bannerText: { textAlign: 'center', diff --git a/app/images/logo.png b/app/images/logo.png new file mode 100644 index 00000000..57a423a6 Binary files /dev/null and b/app/images/logo.png differ diff --git a/app/images/logo.svg b/app/images/logo.svg new file mode 100644 index 00000000..33663753 --- /dev/null +++ b/app/images/logo.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/index.js b/app/index.js new file mode 100644 index 00000000..89795d76 --- /dev/null +++ b/app/index.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { Text } from 'react-native'; +import setNavigator from './actions/navigator'; +import LoginView from './views/login'; +import ListServerView from './views/serverList'; + +import store from './lib/createStore'; + +// +// export const authenticated = (view) => { +// if (!store.getState().login.authenticated) { +// return store.getState().navigator.resetTo({ +// screen: 'Login' +// }); +// } +// return view; +// }; + +export const authenticated = WrappedComponent => class _p extends React.PureComponent { + constructor() { + super(); + this.login = store.getState().login; + console.log('this.login.token', this.login.token); + if (!this.login.token || this.login.failure) { + return store.getState().navigator.resetTo({ + screen: 'Login' + }); + } + } + render() { + // Wraps the input component in a container, without mutating it. Good! + return ; + } +}; +// +export class PublicScreen extends React.PureComponent { + // componentWillMount() { + // this.props.setNavigator(this.props.navigator); + // if (this.props.currentServer) { + // return this.props.navigator.navigate('private'); + // } + // } + render() { + return !this.login.isAuthenticated || !this.login.user ? null : (); + } +} + + +@connect(null, dispatch => ({ + setNavigator: navigator => dispatch(setNavigator(navigator)) +})) +export class PrivateScreen extends React.PureComponent { + componentWillMount() { + // this.props.setNavigator(this.props.navigator); + } + render() { + // if (this.props.logged) { + // return (oi); + // } + return (); + } +} +@connect(state => ({ + // logged: state.login.isAuthenticated +}), dispatch => ({ + // navigate: routeName => dispatch(NavigationActions.navigate({ routeName })), + setNavigator: navigator => dispatch(setNavigator(navigator)) +})) +export const HomeScreen = class extends React.PureComponent { + componentWillMount() { + this.props.setNavigator(this.props.navigator); + this.props.navigator.resetTo({ + screen: 'public' + }); + } + render() { + return (oieee); + } +}; diff --git a/app/lib/createStore.js b/app/lib/createStore.js index 7e6599e5..d187127f 100644 --- a/app/lib/createStore.js +++ b/app/lib/createStore.js @@ -13,7 +13,7 @@ let middleware; if (__DEV__) { /* eslint-disable global-require */ const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default(); - middleware = [sagaMiddleware, reduxImmutableStateInvariant, logger]; + middleware = [sagaMiddleware, reduxImmutableStateInvariant]; } else { middleware = [sagaMiddleware]; } diff --git a/app/lib/realm.js b/app/lib/realm.js index b1219a3b..c58b4a75 100644 --- a/app/lib/realm.js +++ b/app/lib/realm.js @@ -1,4 +1,5 @@ import Realm from 'realm'; +import { AsyncStorage } from 'react-native'; const serversSchema = { name: 'servers', @@ -153,9 +154,9 @@ const messagesSchema = { // ] // } }; - +// // Realm.clearTestState(); - +// AsyncStorage.clear(); const realm = new Realm({ schema: [settingsSchema, serversSchema, subscriptionSchema, messagesSchema, usersSchema, attachment] }); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 900cc6a6..a776eebe 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -51,9 +51,9 @@ const RocketChat = { reduxStore.dispatch(connectSuccess()); resolve(); }); - Meteor.ddp.on('loggin', () => { - reduxStore.dispatch(loginSuccess({})); - }); + // Meteor.ddp.on('loggin', () => { + // reduxStore.dispatch(loginSuccess({})); + // }); Meteor.ddp.on('connected', () => { Meteor.call('public-settings/get', (err, data) => { if (err) { @@ -105,11 +105,13 @@ const RocketChat = { }, login(params, callback) { + console.log('login(params, callback)'); return new Promise((resolve, reject) => { Meteor._startLoggingIn(); return Meteor.call('login', params, (err, result) => { Meteor._endLoggingIn(); Meteor._handleLoginCallback(err, result); + console.log('login(params, callback)asdas', err, result); if (err) { reject(err); } else { @@ -224,6 +226,7 @@ const RocketChat = { getMessage(rid, msg = {}) { const _id = Random.id(); + // console.log('reduxStore.getState().login.id ', reduxStore.getState().login); const message = { _id, rid, @@ -233,8 +236,8 @@ const RocketChat = { temp: true, _server: { id: reduxStore.getState().server }, u: { - _id: reduxStore.getState()._id, - username: reduxStore.getState()._id + _id: reduxStore.getState().login.user.id || '1', + username: reduxStore.getState().login.user.id } }; @@ -367,14 +370,15 @@ const RocketChat = { getRooms() { // Meteor.Accounts.onLogin(() => { return Promise.all([call('subscriptions/get'), call('rooms/get')]).then(([subscriptions, rooms]) => { + // console.log('getRooms resolved', reduxStore.getState().server, subscriptions); subscriptions = subscriptions.sort((s1, s2) => (s1.rid > s2.rid ? 1 : -1)); rooms = rooms.sort((s1, s2) => (s1._id > s2._id ? 1 : -1)); const data = subscriptions.map((subscription, index) => { subscription._updatedAt = rooms[index]._updatedAt; return subscription; }); - Meteor.subscribe('stream-notify-user', `${ Meteor.userId() }/subscriptions-changed`, false); // Meteor.subscribe('stream-notify-user', `${ Meteor.userId() }/rooms-changed`, false); + console.log('getRooms resolved', reduxStore.getState().server, data); realm.write(() => { data.forEach((subscription) => { // const subscription = { @@ -388,6 +392,7 @@ const RocketChat = { realm.create('subscriptions', subscription, true); }); }); + Meteor.subscribe('stream-notify-user', `${ reduxStore.getState().user.id }/subscriptions-changed`, false); return data; }).then(data => data); // }); diff --git a/app/navigation.js b/app/navigation.js index 65a87c97..9ee61d2f 100644 --- a/app/navigation.js +++ b/app/navigation.js @@ -9,8 +9,13 @@ import RoomView from './views/room'; import PhotoView from './views/Photo'; import CreateChannel from './views/CreateChannel'; import store from './lib/createStore'; +import { PrivateScreen, HomeScreen, authenticated } from './index'; -Navigation.registerComponent('Rooms', () => RoomsListView, store, Provider); +// console.log('fisateile/', PublicRoute(PublicScreen)); +Navigation.registerComponent('home', () => HomeScreen, store, Provider); +Navigation.registerComponent('private', () => PrivateScreen, store, Provider); +Navigation.registerComponent('public', () => ListServerView, store, Provider); +Navigation.registerComponent('Rooms', () => authenticated(RoomsListView), store, Provider); Navigation.registerComponent('Room', () => RoomView, store, Provider); Navigation.registerComponent('Photo', () => PhotoView, store, Provider); Navigation.registerComponent('ListServer', () => ListServerView, store, Provider); @@ -20,8 +25,8 @@ Navigation.registerComponent('CreateChannel', () => CreateChannel, store, Provid Navigation.startSingleScreenApp({ screen: { - screen: 'ListServer', - title: 'ListServer' + screen: 'home', + title: 'private' }, - animationType: 'none' + animationType: 'slide-up' }); diff --git a/app/reducers/index.js b/app/reducers/index.js index bc162c81..108f9add 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -1,11 +1,12 @@ import { combineReducers } from 'redux'; -import * as reducers from './reducers'; +import settings from './reducers'; import login from './login'; import meteor from './connect'; import messages from './messages'; import server from './server'; +import navigator from './navigator'; export default combineReducers({ - ...reducers, login, meteor, messages, server + settings, login, meteor, messages, server, navigator }); diff --git a/app/reducers/login.js b/app/reducers/login.js index 4aacbd59..72f36d52 100644 --- a/app/reducers/login.js +++ b/app/reducers/login.js @@ -11,16 +11,18 @@ const initialState = { export default function login(state = initialState, action) { switch (action.type) { case types.LOGIN.REQUEST: + console.log('types.LOGIN.REQUEST', action); return { ...state, isFetching: true, - isAuthenticated: false + isAuthenticated: false, + failure: false }; case types.LOGIN.SUCCESS: return { ...state, isFetching: false, isAuthenticated: true, user: action.user, - // token: action.token, + token: action.user.token, failure: false // user: action.user }; @@ -32,13 +34,11 @@ export default function login(state = initialState, action) { errorMessage: action.err }; case types.LOGOUT: - return { ...state, - isFetching: false, - isAuthenticated: false - }; + return initialState; case types.LOGIN.SET_TOKEN: return { ...state, - token: action.token + token: action.token, + user: action.user }; default: return state; diff --git a/app/reducers/navigator.js b/app/reducers/navigator.js new file mode 100644 index 00000000..75c2cf09 --- /dev/null +++ b/app/reducers/navigator.js @@ -0,0 +1,13 @@ +import * as types from '../actions/actionsTypes'; + +const initialState = {}; + +export default function navigations(state = initialState, action) { + switch (action.type) { + case types.NAVIGATION.SET: + return action.navigator + ; + default: + return state; + } +} diff --git a/app/sagas/login.js b/app/sagas/login.js index 6462ff71..b21d2503 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -1,55 +1,102 @@ import { AsyncStorage } from 'react-native'; -import { take, put, call, takeEvery, fork, select, all } from 'redux-saga/effects'; +import { take, put, call, takeEvery, fork, select, all, race } from 'redux-saga/effects'; import * as types from '../actions/actionsTypes'; import { loginRequest, loginSuccess, loginFailure, setToken } from '../actions/login'; import RocketChat from '../lib/rocketchat'; const TOKEN_KEY = 'reactnativemeteor_usertoken'; -const getUser = state => state.login.user; +const getUser = state => state.login; const getServer = state => state.server; const loginCall = args => (args.resume ? RocketChat.login(args) : RocketChat.loginWithPassword(args)); const getToken = function* getToken() { const currentServer = yield select(getServer); - console.log('currentServer', currentServer); - const token = yield call([AsyncStorage, 'getItem'], `${ TOKEN_KEY }-${ currentServer }`); - console.log('currentServer TOKEN', token); - if (token) { yield put(setToken(token)); } - // yield call([AsyncStorage, 'setItem'], TOKEN_KEY, token || ''); - return token; -}; - -const sagaLogin = function* sagaLogin(payload) { - try { - const response = yield call(loginCall, payload); - yield put(loginSuccess(response)); - } catch (err) { - yield put(loginFailure(err)); + const user = yield call([AsyncStorage, 'getItem'], `${ TOKEN_KEY }-${ currentServer }`); + if (user) { + try { + yield put(setToken(JSON.parse(user))); + yield call([AsyncStorage, 'setItem'], TOKEN_KEY, JSON.parse(user).token || ''); + return JSON.parse(user); + } catch (e) { + console.log('getTokenerr', e); + } } }; -const watchLoginRequest = function* watchLoginRequest() { - do { - try { - yield all([take(types.METEOR.SUCCESS), take(types.SERVER.CHANGED)]); - const token = yield call(getToken); - if (token) { - yield put(loginRequest({ resume: token })); - } - } catch (e) { - console.log(e); + +const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() { + // do { + try { + yield take(types.METEOR.SUCCESS); + const { navigator } = yield select(state => state); + navigator.resetTo({ + screen: 'Rooms' + }); + const user = yield select(getUser); + if (user.token) { + yield put(loginRequest({ resume: user.token })); + // console.log('AEEEEEEEEOOOOO'); + // // wait for a response + // const { error } = yield race({ + // success: take(types.LOGIN.SUCCESS), + // error: take(types.LOGIN.FAILURE) + // }); + // console.log('AEEEEEEEEOOOOO', error); + // if (!error) { + // navigator.resetTo({ + // screen: 'Rooms' + // }); + // } } - } while (true); + } catch (e) { + console.log(e); + } + // } while (true); }; const saveToken = function* saveToken() { const [server, user] = yield all([select(getServer), select(getUser)]); yield AsyncStorage.setItem(TOKEN_KEY, user.token); - yield AsyncStorage.setItem(`${ TOKEN_KEY }-${ server }`, user.token); + yield AsyncStorage.setItem(`${ TOKEN_KEY }-${ server }`, JSON.stringify(user)); +}; + +const handleLoginRequest = function* handleLoginRequest() { + while (true) { + const { credentials } = yield take(types.LOGIN.REQUEST); + try { + const response = yield call(loginCall, credentials); + yield put(loginSuccess(response)); + } catch (err) { + // console.log('login failed'); + yield put(loginFailure(err)); + } + } +}; + +const handleLoginSubmit = function* handleLoginSubmit() { + while (true) { + const { credentials } = yield take(types.LOGIN.SUBMIT); + // put a login request + yield put(loginRequest(credentials)); + // wait for a response + const { error } = yield race({ + success: take(types.LOGIN.SUCCESS), + error: take(types.LOGIN.FAILURE) + }); + + if (!error) { + const { navigator } = yield select(state => state); + navigator.resetTo({ + screen: 'Rooms' + }); + } + } }; const root = function* root() { - yield fork(watchLoginRequest); - yield takeEvery(types.LOGIN.REQUEST, sagaLogin); + yield takeEvery(types.SERVER.CHANGED, getToken); + yield takeEvery(types.SERVER.CHANGED, handleLoginWhenServerChanges); + yield fork(handleLoginRequest); yield takeEvery(types.LOGIN.SUCCESS, saveToken); + yield fork(handleLoginSubmit); }; export default root; diff --git a/app/sagas/messages.js b/app/sagas/messages.js index 47774474..e379398f 100644 --- a/app/sagas/messages.js +++ b/app/sagas/messages.js @@ -5,6 +5,7 @@ import RocketChat from '../lib/rocketchat'; const get = function* get({ rid }) { const auth = yield select(state => state.login.isAuthenticated); + console.log('hey now', yield select(state => state.login)); if (!auth) { yield take(LOGIN.SUCCESS); } @@ -19,9 +20,4 @@ const get = function* get({ rid }) { const getData = function* getData() { yield takeLatest(MESSAGES.REQUEST, get); }; - -const getMessages = function* getMessages() { - yield takeEvery(LOGIN.SUCCESS, getData); -}; - -export default getMessages; +export default getData; diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 9cf6c7b9..a751d6b8 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -9,6 +9,7 @@ const getRooms = function* getRooms() { const watchRoomsRequest = function* watchRoomsRequest() { try { + console.log('getRooms'); yield call(getRooms); yield put(roomsSuccess()); } catch (err) { diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index 2e81c25b..f8df5c07 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -5,15 +5,9 @@ import { connectRequest, disconnect } from '../actions/connect'; import { changedServer } from '../actions/server'; const selectServer = function* selectServer(server) { - try { - yield put(disconnect()); - yield put(changedServer(server)); - yield (server && put(connectRequest(server))); - // console.log(Actions.login()); - // Actions.replace('login', {}); - } catch (e) { - console.log(e); - } + yield put(disconnect()); + yield put(changedServer(server)); + yield put(connectRequest(server)); }; const root = function* root() { yield takeEvery(SERVER.SELECT, selectServer); diff --git a/app/views/login.js b/app/views/login.js index 48c71e66..e32e21cd 100644 --- a/app/views/login.js +++ b/app/views/login.js @@ -1,41 +1,78 @@ import React from 'react'; + +import Spinner from 'react-native-loading-spinner-overlay'; + import PropTypes from 'prop-types'; -import { Text, TextInput, StyleSheet } from 'react-native'; +import { Keyboard, Text, TextInput, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; // import * as actions from '../actions'; import * as loginActions from '../actions/login'; import KeyboardView from '../components/KeyboardView'; +// import { Keyboard } from 'react-native' const styles = StyleSheet.create({ view: { flex: 1, flexDirection: 'column', justifyContent: 'center', + padding: 20, alignItems: 'stretch', - backgroundColor: '#fff' + backgroundColor: '#2f343d' + }, + logoContainer: { + flex: 1, + alignItems: 'center', + flexGrow: 1, + justifyContent: 'center' + }, + logo: { + width: 150, + // backgroundColor: 'red' + // height: 150, + resizeMode: 'contain' + }, + formContainer: { + // marginBottom: 20 }, input: { height: 40, - borderColor: '#aaa', - marginLeft: 20, - marginRight: 20, - marginTop: 10, - padding: 5, + marginBottom: 20, + borderRadius: 2, + paddingHorizontal: 10, borderWidth: 0, - backgroundColor: '#f6f6f6' + backgroundColor: 'rgba(255,255,255,.2)', + color: 'white' + }, + buttonContainer: { + paddingVertical: 15, + backgroundColor: '#414852', + marginBottom: 20 + }, + button: { + textAlign: 'center', + color: 'white', + borderRadius: 2, + fontWeight: '700' }, error: { textAlign: 'center', color: 'red', paddingTop: 5 + }, + loading: { + flex: 1, + position: 'absolute', + backgroundColor: 'rgba(255,255,255,.2)', + left: 0, + top: 0 } }); class LoginView extends React.Component { static propTypes = { navigator: PropTypes.object.isRequired, - loginRequest: PropTypes.func.isRequired, + loginSubmit: PropTypes.func.isRequired, server: PropTypes.string.isRequired, Accounts_EmailOrUsernamePlaceholder: PropTypes.string, Accounts_PasswordPlaceholder: PropTypes.string @@ -65,9 +102,8 @@ class LoginView extends React.Component { } submit = () => { const { username, password, code } = this.state; - console.log({ username, password, code }); - this.props.loginRequest({ username, password, code }); - this.props.navigator.dismissModal(); + this.props.loginSubmit({ username, password, code }); + Keyboard.dismiss(); } renderTOTP = () => { @@ -92,29 +128,45 @@ class LoginView extends React.Component { render() { return ( - this.setState({ username })} - keyboardType='email-address' - autoCorrect={false} - returnKeyType='done' - autoCapitalize='none' - autoFocus - onSubmitEditing={this.submit} - placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || 'Email or username'} - /> - this.setState({ password })} - secureTextEntry - autoCorrect={false} - returnKeyType='done' - autoCapitalize='none' - onSubmitEditing={this.submit} - placeholder={this.props.Accounts_PasswordPlaceholder || 'Password'} - /> - {this.renderTOTP()} - {this.state.error} + + + + + this.setState({ username })} + keyboardType='email-address' + autoCorrect={false} + returnKeyType='done' + autoCapitalize='none' + autoFocus + + underlineColorAndroid='transparent' + onSubmitEditing={this.submit} + placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || 'Email or username'} + /> + this.setState({ password })} + secureTextEntry + autoCorrect={false} + returnKeyType='done' + autoCapitalize='none' + + underlineColorAndroid='transparent' + onSubmitEditing={this.submit} + placeholder={this.props.Accounts_PasswordPlaceholder || 'Password'} + /> + {this.renderTOTP()} + + LOGIN + + {this.props.login.error && {this.props.login.error}} + + + ); } diff --git a/app/views/roomsList.js b/app/views/roomsList.js index fc89ea94..98b27260 100644 --- a/app/views/roomsList.js +++ b/app/views/roomsList.js @@ -1,9 +1,9 @@ import ActionButton from 'react-native-action-button'; -// import { Navigation } from 'react-native-navigation'; +import { Navigation } from 'react-native-navigation'; import { ListView } from 'realm/react-native'; import React from 'react'; import PropTypes from 'prop-types'; -import { View, StyleSheet, TextInput } from 'react-native'; +import { View, StyleSheet, TextInput, Platform } from 'react-native'; import { connect } from 'react-redux'; import * as actions from '../actions'; import * as server from '../actions/connect'; @@ -67,7 +67,7 @@ const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); export default class RoomsListView extends React.Component { static propTypes = { - // navigator: PropTypes.object.isRequired, + navigator: PropTypes.object.isRequired, server: PropTypes.string } @@ -81,8 +81,29 @@ export default class RoomsListView extends React.Component { searchText: '', login: false }; + this.data.addListener(this.updateState); + this.props.navigator.setOnNavigatorEvent(event => event.type === 'NavBarButtonPress' && event.id === 'servers' && + Navigation.showModal({ + screen: 'ListServer', + passProps: {}, + navigatorStyle: {}, + navigatorButtons: {}, + animationType: 'slide-up' + })); + this.props.navigator.setSubTitle({ + subtitle: this.props.server + }); + } + componentWillMount() { + const button = Platform.OS === 'ios' ? 'leftButtons' : 'rightButtons'; + this.props.navigator.setButtons({ + [button]: [{ + id: 'servers', + title: 'Servers' + }], + animated: true + }); } - componentWillUnmount() { this.data.removeListener(this.updateState); } @@ -148,16 +169,16 @@ export default class RoomsListView extends React.Component { updateState = () => { this.setState({ - dataSource: ds.cloneWithRows(this.state.data) + dataSource: ds.cloneWithRows(this.data) }); }; _onPressItem = (id, item = {}) => { const navigateToRoom = (room) => { - // this.props.navigator.push({ - // screen: 'Room', - // passProps: room - // }); + this.props.navigator.push({ + screen: 'Room', + passProps: room + }); }; const clearSearch = () => { @@ -218,6 +239,7 @@ export default class RoomsListView extends React.Component { this._onPressItem(item._id, item)} /> ) @@ -234,15 +256,10 @@ export default class RoomsListView extends React.Component { ) renderCreateButtons = () => ( ); - render= () => { - if (this.props.canShowList) { - return ( - - - {this.renderList()} - {this.renderCreateButtons()} - ); - } - return null; - } + render= () => ( + + + {this.renderList()} + {this.renderCreateButtons()} + ) } diff --git a/app/views/serverList.js b/app/views/serverList.js index ab7156cb..421e7bc5 100644 --- a/app/views/serverList.js +++ b/app/views/serverList.js @@ -1,4 +1,6 @@ import React from 'react'; + +import Icon from 'react-native-vector-icons/Ionicons'; import PropTypes from 'prop-types'; import { Navigation } from 'react-native-navigation'; import Zeroconf from 'react-native-zeroconf'; @@ -6,6 +8,8 @@ import { View, Text, SectionList, Platform, StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { setServer } from '../actions/server'; import realm from '../lib/realm'; +import Fade from '../animations/fade'; +import Banner from '../components/banner'; const styles = StyleSheet.create({ view: { @@ -27,11 +31,6 @@ const styles = StyleSheet.create({ textAlign: 'center', color: '#888' }, - listItem: { - lineHeight: 18, - color: '#666', - padding: 14 - }, container: { flex: 1 }, @@ -44,6 +43,21 @@ const styles = StyleSheet.create({ lineHeight: 24, paddingLeft: 14, color: '#888' + }, + serverItem: { + flex: 1, + flexDirection: 'row', + // justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#fff', + padding: 14 + }, + + listItem: { + color: '#666', flexGrow: 1, lineHeight: 30 + }, + serverChecked: { + flexGrow: 0 } }); @@ -166,12 +180,16 @@ export default class ListServerView extends React.Component { } renderItem = ({ item }) => ( - { this.onPressItem(item); }} - > - {item.id} - + + + { this.onPressItem(item); }} + > + {item.id} + + + ); renderSectionHeader = ({ section }) => ( @@ -185,6 +203,7 @@ export default class ListServerView extends React.Component { render() { return ( +