diff --git a/app/api/gitter.js b/app/api/gitter.js index b661e82..7c29572 100644 --- a/app/api/gitter.js +++ b/app/api/gitter.js @@ -50,6 +50,11 @@ export function getToken(code) { /** * Authed user stuff */ + +export function me(token) { + return callApi('user/me', token) +} + export function currentUser(token) { return callApi('user', token) } diff --git a/app/components/Loading/index.js b/app/components/Loading/index.js index f5ae856..3c04b7d 100644 --- a/app/components/Loading/index.js +++ b/app/components/Loading/index.js @@ -1,10 +1,10 @@ import PropTypes from 'prop-types' import React from 'react' -import {ActivityIndicator, View} from 'react-native'; +import {ActivityIndicator, View, Text} from 'react-native'; import {THEMES} from '../../constants' const {colors} = THEMES.gitterDefault -const Loading = ({size, height, color}) => ( +const Loading = ({size, height, color, text}) => ( ( animating size={size} color={color || colors.raspberry}/> + {text && ( + + {text} + + )} ) diff --git a/app/components/LoadingOverlay/index.js b/app/components/LoadingOverlay/index.js new file mode 100644 index 0000000..149409c --- /dev/null +++ b/app/components/LoadingOverlay/index.js @@ -0,0 +1,18 @@ +import React from 'react' +import {View, Text} from 'react-native' +import Loading from '../Loading' +import s from './styles' + +const LoadingOverlay = ({text}) => ( + + + + + +) + +LoadingOverlay.propTypes = { + +} + +export default LoadingOverlay diff --git a/app/components/LoadingOverlay/styles.js b/app/components/LoadingOverlay/styles.js new file mode 100644 index 0000000..8814e97 --- /dev/null +++ b/app/components/LoadingOverlay/styles.js @@ -0,0 +1,17 @@ +import {StyleSheet} from 'react-native' + +const styles = StyleSheet.create({ + loadingContainer: { + flex: 1, + position: 'absolute', + bottom: 0, + top: 0, + left: 0, + right: 0, + backgroundColor: 'rgba(0,0,0,0.7)', + justifyContent: 'center', + alignItems: 'center' + } +}) + +export default styles diff --git a/app/constants.js b/app/constants.js index 6eb8c67..cc97130 100644 --- a/app/constants.js +++ b/app/constants.js @@ -53,5 +53,6 @@ export const icons = { 'info-outline': {icon: 'info-outline', color: 'white', size: 24}, 'back': {icon: iOS ? 'chevron-left' : 'arrow-back', color: 'white', size: 24}, 'forward': iOS ? {icon: 'chevron-right', color: 'white', size: 40} : {icon: 'arrow-forward', color: 'white', size: 24}, - 'expand-more': {icon: 'expand-more', color: 'white', size: 24} + 'expand-more': {icon: 'expand-more', color: 'white', size: 24}, + 'checkmark': {icon: 'check', color: 'white', size: 24} } diff --git a/app/modules/auth.js b/app/modules/auth.js index 7681fb0..6735bc7 100644 --- a/app/modules/auth.js +++ b/app/modules/auth.js @@ -8,17 +8,46 @@ import {setItem, removeItem} from '../utils/storage' * Constants */ +const TOKEN_REGEX = /^[a-z\d]*$/i + export const LOGINING = 'auth/LOGINING' export const LOGINED_IN_SUCCESS = 'auth/LOGINED_IN_SUCCESS' export const LOGIN_USER = 'auth/LOGIN_USER' export const LOGIN_USER_BY_TOKEN = 'auth/LOGIN_USER_BY_TOKEN' export const UNEXPECTED_ERROR = 'auth/UNEXPECTED_ERROR' export const LOGOUT = 'auth/LOGOUT' +export const CHECK_TOKEN = 'auth/CHECK_TOKEN' +export const CHECK_TOKEN_OK = 'auth/CHECK_TOKEN_OK' +export const CHECK_TOKEN_ERROR = 'auth/CHECK_TOKEN_ERROR' /** * Action Creators */ +export function checkToken({token}) { + return async dispatch => { + try { + dispatch({type: CHECK_TOKEN}) + + if (!TOKEN_REGEX.test(token)) { + throw new Error('Bad token.') + } + + const user = await Api.me(token) + + if (!!user.error) { + throw new Error('Unable to authenticate. Please try again.') + } + + dispatch({type: CHECK_TOKEN_OK}) + + await dispatch(loginByToken({token})) + } catch (err) { + dispatch({type: CHECK_TOKEN_ERROR, error: err.message}) + } + } +} + export function loginByToken({token, code}) { return async dispatch => { try { @@ -37,9 +66,9 @@ export function loginByToken({token, code}) { rootNavigator.startAppWithScreen({screen: 'gm.Launch'}) await dispatch(init()) - dispatch({LOGINED_IN_SUCCESS}) + dispatch({type: LOGINED_IN_SUCCESS}) } catch (err) { - dispatch({type: UNEXPECTED_ERROR, error: err}) + dispatch({type: UNEXPECTED_ERROR, error: err.message}) } } } @@ -66,7 +95,7 @@ const initialState = { loginedIn: false, token: '', error: false, - errors: {} + errors: '' } export default function auth(state = initialState, action) { @@ -75,7 +104,8 @@ export default function auth(state = initialState, action) { if (!!action.token) { return {...state, loginedIn: true, - token: action.token + token: action.token, + error: false } } else { return {...state, @@ -84,12 +114,18 @@ export default function auth(state = initialState, action) { } } } + + case CHECK_TOKEN: case LOGINING: { return {...state, - logining: true + logining: true, + error: false, + errors: '' } } + + case CHECK_TOKEN_OK: case LOGINED_IN_SUCCESS: { return {...state, logining: false @@ -103,6 +139,14 @@ export default function auth(state = initialState, action) { } } + case CHECK_TOKEN_ERROR: + case UNEXPECTED_ERROR: + return {...state, + error: true, + logining: false, + errors: action.error + } + case LOGOUT: { return initialState } diff --git a/app/screens/Drawer/ChannelListItem/index.js b/app/screens/Drawer/ChannelListItem/index.js index fc16822..b6702fb 100644 --- a/app/screens/Drawer/ChannelListItem/index.js +++ b/app/screens/Drawer/ChannelListItem/index.js @@ -37,15 +37,20 @@ const ChannelListItem = ({ size={30} /> - {name} + + {name} + - - - {(!!unreadItems || !!mentions || !!lurk) && - } + + ) } diff --git a/app/screens/Drawer/ChannelListItem/styles.js b/app/screens/Drawer/ChannelListItem/styles.js index 60b3e8d..337f80f 100644 --- a/app/screens/Drawer/ChannelListItem/styles.js +++ b/app/screens/Drawer/ChannelListItem/styles.js @@ -11,7 +11,8 @@ const styles = StyleSheet.create({ backgroundColor: 'white' }, headingContainer: { - marginLeft: 10 + marginLeft: 10, + flex: 1 }, heading: { fontSize: 12, diff --git a/app/screens/Home/HomeRoomItem/index.js b/app/screens/Home/HomeRoomItem/index.js index c044ea1..dc2b63b 100644 --- a/app/screens/Home/HomeRoomItem/index.js +++ b/app/screens/Home/HomeRoomItem/index.js @@ -19,9 +19,13 @@ const HomeRoomItem = ({id, name, userCount, oneToOne, onPress, ...props}) => { - - {name} + + {name} + {userCount} people diff --git a/app/screens/Home/HomeRoomItem/styles.js b/app/screens/Home/HomeRoomItem/styles.js index 1ca1541..cfdce69 100644 --- a/app/screens/Home/HomeRoomItem/styles.js +++ b/app/screens/Home/HomeRoomItem/styles.js @@ -2,6 +2,7 @@ import {StyleSheet} from 'react-native' const styles = StyleSheet.create({ container: { + flex: 1, height: 80, padding: 20, paddingLeft: 20, @@ -11,6 +12,7 @@ const styles = StyleSheet.create({ alignItems: 'center' }, infoContainer: { + flex: 1, flexDirection: 'column', marginLeft: 16 }, diff --git a/app/screens/Home/HomeRoomItemMy/index.js b/app/screens/Home/HomeRoomItemMy/index.js index ba129e3..95c0295 100644 --- a/app/screens/Home/HomeRoomItemMy/index.js +++ b/app/screens/Home/HomeRoomItemMy/index.js @@ -26,17 +26,22 @@ const HomeRoomItemMy = ({ size={50} /> - {name} + + {name} + {userCount} people - {(!!unreadItems || !!mentions || !!lurk) && - - } + + } + ) } diff --git a/app/screens/Home/index.js b/app/screens/Home/index.js index 1797ad6..051aac1 100644 --- a/app/screens/Home/index.js +++ b/app/screens/Home/index.js @@ -22,7 +22,7 @@ class HomeScreen extends Component { homeNavigator = this.props.navigator - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); + this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)) this.props.navigator.setButtons({ leftButtons: [{ diff --git a/app/screens/Login/index.js b/app/screens/Login/index.js index 87f35a0..58940fb 100644 --- a/app/screens/Login/index.js +++ b/app/screens/Login/index.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' -import {Text, Image, View, ToastAndroid} from 'react-native'; +import {Text, View} from 'react-native'; import Button from '../../components/Button' import s from './styles' import {connect} from 'react-redux' @@ -12,35 +12,29 @@ class LoginScreen extends Component { render() { const {navigator} = this.props return ( - + GitterMobile - - To start using Gitter mobile you should login first. - You can login by oauth2 through WebView or just - copy/paste authentication token. - - + ) } } diff --git a/app/screens/Login/styles.js b/app/screens/Login/styles.js index e8691fd..106cad9 100644 --- a/app/screens/Login/styles.js +++ b/app/screens/Login/styles.js @@ -1,13 +1,13 @@ import {StyleSheet, Dimensions} from 'react-native' -import backgroundImage from '../../styles/common/BackgroundImage' import {THEMES} from '../../constants' const {colors} = THEMES.gitterDefault const styles = StyleSheet.create({ container: { - ...backgroundImage, - justifyContent: 'space-around', - alignItems: 'center', + flex: 1, + backgroundColor: colors.raspberry, + justifyContent: 'center', + alignItems: 'center' }, logo: { fontSize: 40, @@ -23,21 +23,22 @@ const styles = StyleSheet.create({ backgroundColor: 'transparent' }, buttonGroup: { - flexDirection: 'row' + position: 'absolute', + bottom: 20 }, buttonStyle: { - margin: 10, - backgroundColor: colors.primaryButton, - width: 150, - height: 40, + backgroundColor: 'white', + marginBottom: 16, + height: 35, borderRadius: 2, justifyContent: 'center', alignItems: 'center', - elevation: 2 + elevation: 4 }, buttonText: { - color: 'white', - fontWeight: 'bold' + fontWeight: 'bold', + paddingHorizontal: 40, + color: colors.raspberry } }) diff --git a/app/screens/LoginByToken/index.js b/app/screens/LoginByToken/index.js index 7edf3fc..7db1555 100644 --- a/app/screens/LoginByToken/index.js +++ b/app/screens/LoginByToken/index.js @@ -1,11 +1,12 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' -import {TextInput, Text, Image} from 'react-native'; +import {TextInput, Text, View, Linking, ScrollView} from 'react-native'; import s from './styles' import {connect} from 'react-redux' -import {loginByToken} from '../../modules/auth' +import {checkToken} from '../../modules/auth' +import iconsMap from '../../utils/iconsMap' -import Link from '../../components/Link' +import LoadingOverlay from '../../components/LoadingOverlay' import Button from '../../components/Button' import {THEMES} from '../../constants' const {colors} = THEMES.gitterDefault @@ -14,48 +15,90 @@ class LoginByTokenScreen extends Component { constructor(props) { super(props) this.handleLogin = this.handleLogin.bind(this) + this.handleChangeText = this.handleChangeText.bind(this) + + this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)) this.state = { token: '' } } + onNavigatorEvent(event) { + if (event.type === 'NavBarButtonPress') { + if (event.id === 'submit') { + this.handleLogin() + } + } + } + handleLogin() { const {dispatch, navigator} = this.props const {token} = this.state if (!token.trim()) { return } - dispatch(loginByToken({token}, navigator)) + dispatch(checkToken({token}, navigator)) } - render() { + handleChangeText(e) { + let rightButtons = [] + if (e.nativeEvent.text.trim().length) { + rightButtons = [{ + title: 'Submit', + id: 'submit', + icon: iconsMap.checkmark, + showAsAction: 'always' + }] + } + this.props.navigator.setButtons({rightButtons}) + this.setState({token: e.nativeEvent.text.trim()}) + } + + renderLoading() { return ( - - - Sign in to Gitter - to get your authentication token. Copy it and paste into the textinput below. - + + ) + } - this.setState({token: e.nativeEvent.text})} /> - + + + + + - + {logining && this.renderLoading()} + ) } } @@ -73,4 +116,14 @@ LoginByTokenScreen.propTypes = { dispatch: PropTypes.func } -export default connect()(LoginByTokenScreen) +function mapStateToProps(state) { + const {logining, errors, error} = state.auth + + return { + logining, + errors, + error + } +} + +export default connect(mapStateToProps)(LoginByTokenScreen) diff --git a/app/screens/LoginByToken/styles.js b/app/screens/LoginByToken/styles.js index 9be2221..0f0b75d 100644 --- a/app/screens/LoginByToken/styles.js +++ b/app/screens/LoginByToken/styles.js @@ -1,40 +1,23 @@ import {StyleSheet} from 'react-native' -import backgroundImage from '../../styles/common/BackgroundImage' import {THEMES} from '../../constants' const {colors} = THEMES.gitterDefault const styles = StyleSheet.create({ container: { - ...backgroundImage, flex: 1, - // justifyContent: 'space-around', - // alignItems: 'center', - // padding: 16 - }, - logo: { - marginTop: 40, - fontSize: 40, - color: 'white', - textAlign: 'center' - }, - hero: { - marginTop: 40, - marginHorizontal: 20, - fontSize: 24, - color: 'white', - lineHeight: 40, - backgroundColor: 'transparent' + backgroundColor: 'white', + paddingHorizontal: 16 }, - group: { - // flexDirection: 'column' + buttonContainer: { + alignSelf: 'center' }, buttonStyle: { - margin: 10, - backgroundColor: colors.primaryButton, - width: 150, - height: 40, + margin: 16, + backgroundColor: colors.raspberry, + paddingHorizontal: 40, + marginBottom: 40, + height: 35, borderRadius: 2, - alignSelf: 'center', justifyContent: 'center', alignItems: 'center', elevation: 2 @@ -44,13 +27,23 @@ const styles = StyleSheet.create({ fontWeight: 'bold' }, textfield: { - marginVertical: 10, - marginHorizontal: 20, - // width: 250, height: 40, backgroundColor: 'white', - borderRadius: 5, - paddingLeft: 8 + paddingLeft: 0, + color: colors.raspberry + }, + textfieldContainer: { + marginVertical: 16, + borderBottomWidth: 1, + borderBottomColor: colors.raspberry + }, + hint: { + color: colors.secondaryFont + }, + error: { + color: colors.red, + marginBottom: 16, + fontSize: 18 } }) diff --git a/app/screens/LoginByWebView/index.js b/app/screens/LoginByWebView/index.js index ee06262..6968b32 100644 --- a/app/screens/LoginByWebView/index.js +++ b/app/screens/LoginByWebView/index.js @@ -2,13 +2,11 @@ import PropTypes from 'prop-types' import React, { Component } from 'react' import { WebView, - Image, - Text, View } from 'react-native' import {connect} from 'react-redux' -import Loading from '../../components/Loading' +import LoadingOverlay from '../../components/LoadingOverlay' import {THEMES} from '../../constants' const {colors} = THEMES.gitterDefault @@ -46,34 +44,23 @@ class LoginByWebView extends Component { renderLoading() { return ( - - - - Signing in... - - + ) } render() { const {loading} = this.state return ( - - {loading - ? ( - this.renderLoading() - ) : ( - - ) - } - + + + {true && this.renderLoading()} + ) } } diff --git a/app/screens/User/UserInfo/styles.js b/app/screens/User/UserInfo/styles.js index 6adbb9c..1267bfd 100644 --- a/app/screens/User/UserInfo/styles.js +++ b/app/screens/User/UserInfo/styles.js @@ -5,7 +5,8 @@ const {colors} = THEMES.gitterDefault const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: 'white' + backgroundColor: 'white', + paddingBottom: 16 }, item: { paddingHorizontal: 20, diff --git a/app/screens/User/index.js b/app/screens/User/index.js index 6cb22eb..3f94cf5 100644 --- a/app/screens/User/index.js +++ b/app/screens/User/index.js @@ -106,7 +106,7 @@ class UserScreen extends Component { ) } return ( - +