From d55db0fca5cace37fa78696bf5075941f473d989 Mon Sep 17 00:00:00 2001 From: gilmarsquinelato Date: Thu, 21 Sep 2017 14:08:00 -0300 Subject: [PATCH] [NEW] Changed navigation library to react-navigation. (#41) * Changed navigation library to react-navigation. Refactoring on sagas to remove navigation navigation flow do it in views. Added new modal library. Renamed view files to the same name of their classes. Added support to redux-devtools using the chrome extension through Remote devtools menu. Some components was refactored to reduce complexity and more legibility. * Remove react-native-navigation from Android * Adding servers to drawer menu, allowing server switch. * Sidebar component * opss :x fix add server * opss :x fix add server * Fixed add server navigation issues, fixed empty and slow Rooms List and Chat Messages * Disable cleanup everytime * some fixes * some fixes * fix? * . * Fixed logo not displaying when app is loading or signing to server * Fixed logo in loading and login * Update LoginView.js * Update PublicRoutes.js --- .../__snapshots__/Storyshots.test.js.snap | 7 + android/app/build.gradle | 1 - .../java/com/rocketchatrn/MainActivity.java | 12 +- .../com/rocketchatrn/MainApplication.java | 38 +++-- android/settings.gradle | 2 - app/containers/Avatar.js | 3 +- app/containers/Banner.js | 30 +++- app/containers/Message.js | 9 +- app/containers/Routes.js | 57 +++++++ app/containers/Sidebar.js | 114 +++++++++++++ app/containers/message/Card.js | 76 ++++----- app/containers/message/PhotoModal.js | 60 +++++++ app/containers/routes/AuthRoutes.js | 62 +++++++ app/containers/routes/PublicRoutes.js | 46 +++++ app/images/logo.svg | 77 --------- app/index.js | 84 +-------- app/lib/createStore.js | 26 ++- app/lib/realm.js | 58 +------ app/lib/rocketchat.js | 22 +-- app/navigation.js | 32 ---- app/presentation/DrawerMenuButton.js | 18 ++ app/presentation/KeyboardView.js | 4 +- app/reducers/app.js | 22 +++ app/reducers/index.js | 3 +- app/sagas/createChannel.js | 33 ++-- app/sagas/index.js | 22 +-- app/sagas/init.js | 8 +- app/sagas/login.js | 61 +++---- app/sagas/messages.js | 1 - app/sagas/rooms.js | 1 + app/sagas/selectServer.js | 10 +- ...{CreateChannel.js => CreateChannelView.js} | 78 ++++++--- .../{serverList.js => ListServerView.js} | 75 +++------ app/views/{login.js => LoginView.js} | 112 ++++++------ app/views/{serverNew.js => NewServerView.js} | 52 +++--- app/views/Photo.js | 4 +- app/views/{room.js => RoomView.js} | 66 +++----- app/views/{roomsList.js => RoomsListView.js} | 73 ++++---- app/views/Styles.js | 40 ++++- index.android.js | 11 +- index.ios.js | 12 +- ios/RocketChatRN.xcodeproj/project.pbxproj | 63 ------- ios/RocketChatRN/AppDelegate.m | 32 ++-- ios/RocketChatRN/Info.plist | 4 +- package.json | 6 +- yarn.lock | 159 +++++++++++++++--- 46 files changed, 983 insertions(+), 803 deletions(-) create mode 100644 app/containers/Routes.js create mode 100644 app/containers/Sidebar.js create mode 100644 app/containers/message/PhotoModal.js create mode 100644 app/containers/routes/AuthRoutes.js create mode 100644 app/containers/routes/PublicRoutes.js delete mode 100644 app/images/logo.svg delete mode 100644 app/navigation.js create mode 100644 app/presentation/DrawerMenuButton.js create mode 100644 app/reducers/app.js rename app/views/{CreateChannel.js => CreateChannelView.js} (51%) rename app/views/{serverList.js => ListServerView.js} (72%) rename app/views/{login.js => LoginView.js} (50%) rename app/views/{serverNew.js => NewServerView.js} (78%) rename app/views/{room.js => RoomView.js} (81%) rename app/views/{roomsList.js => RoomsListView.js} (82%) diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index c07669703..fb385ea80 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -235,6 +235,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, @@ -338,6 +339,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, @@ -441,6 +443,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, @@ -565,6 +568,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, @@ -689,6 +693,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, @@ -813,6 +818,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, @@ -937,6 +943,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "position": "absolute", }, Object { + "borderRadius": 20, "height": 40, "width": 40, }, diff --git a/android/app/build.gradle b/android/app/build.gradle index 7129e4ac0..a679b6179 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -144,7 +144,6 @@ android { } dependencies { - compile project(':react-native-navigation') compile project(':react-native-svg') compile project(':react-native-image-picker') compile project(':react-native-vector-icons') diff --git a/android/app/src/main/java/com/rocketchatrn/MainActivity.java b/android/app/src/main/java/com/rocketchatrn/MainActivity.java index 57a754e0b..460d2b783 100644 --- a/android/app/src/main/java/com/rocketchatrn/MainActivity.java +++ b/android/app/src/main/java/com/rocketchatrn/MainActivity.java @@ -1,7 +1,15 @@ package com.rocketchatrn; -import com.reactnativenavigation.controllers.SplashActivity; +import com.facebook.react.ReactActivity; -public class MainActivity extends SplashActivity { +public class MainActivity extends ReactActivity { + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "RocketChatRN"; + } } diff --git a/android/app/src/main/java/com/rocketchatrn/MainApplication.java b/android/app/src/main/java/com/rocketchatrn/MainApplication.java index 277cfd500..70eacb0d0 100644 --- a/android/app/src/main/java/com/rocketchatrn/MainApplication.java +++ b/android/app/src/main/java/com/rocketchatrn/MainApplication.java @@ -3,7 +3,6 @@ package com.rocketchatrn; import android.app.Application; import com.facebook.react.ReactApplication; -// import com.reactnativenavigation.NavigationReactPackage; import com.horcrux.svg.SvgPackage; import com.imagepicker.ImagePickerPackage; import com.oblador.vectoricons.VectorIconsPackage; @@ -14,31 +13,40 @@ import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; -import com.reactnativenavigation.NavigationApplication; import java.util.Arrays; import java.util.List; -public class MainApplication extends NavigationApplication { +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override - public boolean isDebug() { - // Make sure you are using BuildConfig from your own application + public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } + @Override protected List getPackages() { return Arrays.asList( - new SvgPackage(), - new ImagePickerPackage(), - new VectorIconsPackage(), - new RNFetchBlobPackage(), - new ZeroconfReactPackage(), - new RealmReactPackage() + new MainReactPackage(), + new SvgPackage(), + new ImagePickerPackage(), + new VectorIconsPackage(), + new RNFetchBlobPackage(), + new ZeroconfReactPackage(), + new RealmReactPackage() ); } + }; - @Override - public List createAdditionalReactPackages() { - return getPackages(); - } + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + } } diff --git a/android/settings.gradle b/android/settings.gradle index aacf704b9..fcc9db102 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,6 +1,4 @@ rootProject.name = 'RocketChatRN' -include ':react-native-navigation' -project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app/') include ':react-native-svg' project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android') include ':react-native-image-picker' diff --git a/app/containers/Avatar.js b/app/containers/Avatar.js index fb7c19f57..fa1a15c40 100644 --- a/app/containers/Avatar.js +++ b/app/containers/Avatar.js @@ -36,7 +36,8 @@ class Avatar extends React.PureComponent { const avatarStyle = { width: size, - height: size + height: size, + borderRadius }; const uri = avatar || `${ baseUrl }/avatar/${ text }`; diff --git a/app/containers/Banner.js b/app/containers/Banner.js index 1192500bc..a49a6aa04 100644 --- a/app/containers/Banner.js +++ b/app/containers/Banner.js @@ -30,9 +30,29 @@ export default class Banner extends React.PureComponent { authenticating: PropTypes.bool, offline: PropTypes.bool } - + componentWillMount() { + this.state = { + slow: false + }; + this.timer = setTimeout(() => this.setState({ slow: true }), 5000); + } + componentWillUnmount() { + clearTimeout(this.timer); + } render() { const { connecting, authenticating, offline } = this.props; + + if (!this.state.slow) { + return null; + } + + if (offline) { + return ( + + offline... + + ); + } if (connecting) { return ( @@ -48,13 +68,7 @@ export default class Banner extends React.PureComponent { ); } - if (offline) { - return ( - - offline... - - ); - } + return null; } } diff --git a/app/containers/Message.js b/app/containers/Message.js index 2158244cb..da39c5f2d 100644 --- a/app/containers/Message.js +++ b/app/containers/Message.js @@ -10,7 +10,8 @@ import User from './message/User'; const styles = StyleSheet.create({ content: { - flexGrow: 1 + flexGrow: 1, + flexShrink: 1 }, message: { padding: 12, @@ -29,7 +30,11 @@ export default class Message extends React.PureComponent { } attachments() { - return this.props.item.attachments.length ? : null; + return this.props.item.attachments.length ? ( + + ) : null; } render() { diff --git a/app/containers/Routes.js b/app/containers/Routes.js new file mode 100644 index 000000000..485dd389d --- /dev/null +++ b/app/containers/Routes.js @@ -0,0 +1,57 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { View, Image } from 'react-native'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import * as Animatable from 'react-native-animatable'; +import { appInit } from '../actions'; + +import styles from '../views/Styles'; + +import AuthRoutes from './routes/AuthRoutes'; +import PublicRoutes from './routes/PublicRoutes'; + +@connect( + state => ({ + login: state.login, + app: state.app + }), + dispatch => bindActionCreators({ + appInit + }, dispatch) +) +export default class Routes extends React.Component { + static propTypes = { + login: PropTypes.object.isRequired, + app: PropTypes.object.isRequired, + appInit: PropTypes.func.isRequired + } + + componentWillMount() { + this.props.appInit(); + } + render() { + const { login, app } = this.props; + + if (app.starting) { + return ( + + + + + + ); + } + + if ((login.token && !login.failure) || app.ready) { + return (); + } + + return (); + } +} diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js new file mode 100644 index 000000000..6e6ed7a19 --- /dev/null +++ b/app/containers/Sidebar.js @@ -0,0 +1,114 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { ScrollView, Text, View, StyleSheet, FlatList, TouchableHighlight } from 'react-native'; +import { DrawerItems } from 'react-navigation'; +import { connect } from 'react-redux'; + +import realm from '../lib/realm'; +import { setServer } from '../actions/server'; + +const styles = StyleSheet.create({ + scrollView: { + paddingTop: 20 + }, + imageContainer: { + width: '100%', + alignItems: 'center' + }, + image: { + width: 200, + height: 200, + borderRadius: 100 + }, + serverTitle: { + fontSize: 16, + color: 'grey', + padding: 10, + width: '100%' + }, + serverItem: { + backgroundColor: 'white', + padding: 10, + flex: 1 + }, + selectedServer: { + backgroundColor: '#eeeeee' + } +}); + +@connect(state => ({ + server: state.server.server +}), dispatch => ({ + selectServer: server => dispatch(setServer(server)) +})) +export default class Sidebar extends Component { + static propTypes = { + server: PropTypes.string.isRequired, + selectServer: PropTypes.func.isRequired, + navigation: PropTypes.object.isRequired + } + + componentWillMount() { + realm.addListener('change', this.updateState); + this.state = this.getState(); + } + + componentWillUnmount() { + realm.removeListener('change', this.updateState); + } + + onItemPress = ({ route, focused }) => { + this.props.navigation.navigate('DrawerClose'); + if (!focused) { + this.props.navigation.navigate(route.routeName, undefined); + } + } + + onPressItem = (item) => { + this.props.selectServer(item.id); + this.props.navigation.navigate('DrawerClose'); + } + + getState = () => ({ + servers: realm.objects('servers') + }) + + updateState = () => { + this.setState(this.getState()); + } + + renderItem = ({ item, separators }) => ( + + { this.onPressItem(item); }} + > + + + {item.id} + + + + ); + + render() { + return ( + + + + + SERVERS + item.id} + /> + + + ); + } +} diff --git a/app/containers/message/Card.js b/app/containers/message/Card.js index 0d71026ff..cc594bb63 100644 --- a/app/containers/message/Card.js +++ b/app/containers/message/Card.js @@ -3,8 +3,7 @@ import React from 'react'; import Meteor from 'react-native-meteor'; import { connect } from 'react-redux'; import { CachedImage } from 'react-native-img-cache'; -import { Text, TouchableOpacity } from 'react-native'; -import { Navigation } from 'react-native-navigation'; +import { Text, TouchableOpacity, View } from 'react-native'; import { Card, CardImage, @@ -14,27 +13,13 @@ import { } from 'react-native-card-view'; import RocketChat from '../../lib/rocketchat'; -const close = () => Navigation.dismissModal({ - animationType: 'slide-down' -}); +import PhotoModal from './PhotoModal'; -const CustomButton = ({ text }) => ( - - {text} - -); - -CustomButton.propTypes = { - text: PropTypes.string -}; - -Navigation.registerComponent('CustomButton', () => CustomButton); @connect(state => ({ base: state.settings.Site_Url, canShowList: state.login.token.length || state.login.user.token })) - export default class Cards extends React.PureComponent { static propTypes = { data: PropTypes.object.isRequired, @@ -44,7 +29,9 @@ export default class Cards extends React.PureComponent { constructor() { super(); const user = Meteor.user(); - this.state = {}; + this.state = { + modalVisible: false + }; RocketChat.getUserToken().then((token) => { this.setState({ img: `${ this.props.base }${ this.props.data.image_url }?rc_uid=${ user._id }&rc_token=${ token }` }); }); @@ -52,20 +39,28 @@ export default class Cards extends React.PureComponent { getImage() { return ( - this._onPressButton()}> - - - - - - {this.props.data.title} - {this.props.data.description} - - - + + this._onPressButton()}> + + + + + + {this.props.data.title} + {this.props.data.description} + + + + this.setState({ modalVisible: false })} + /> + ); } @@ -76,21 +71,8 @@ export default class Cards extends React.PureComponent { } _onPressButton() { - Navigation.showModal({ - screen: 'Photo', - title: this.props.data.title, // title of the screen as appears in the nav bar (optional) - passProps: { image: this.state.img }, - // navigatorStyle: {}, // override the navigator style for the screen, see "Styling the navigator" below (optional) - navigatorButtons: { - leftButtons: [{ - id: 'custom-button', - component: 'CustomButton', - passProps: { - text: 'close' - } - }] - }, // override the nav buttons for the screen, see "Adding buttons to the navigator" below (optional) - animationType: 'slide-up' // 'none' / 'slide-up' , appear animation for the modal (optional, default 'slide-up') + this.setState({ + modalVisible: true }); } diff --git a/app/containers/message/PhotoModal.js b/app/containers/message/PhotoModal.js new file mode 100644 index 000000000..a1cf0ff60 --- /dev/null +++ b/app/containers/message/PhotoModal.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { ScrollView, View, Dimensions, Text } from 'react-native'; +import { CachedImage } from 'react-native-img-cache'; +import PropTypes from 'prop-types'; +import Modal from 'react-native-modal'; + +const deviceWidth = Dimensions.get('window').width; + +const styles = { + imageWrapper: { + alignItems: 'stretch' + }, + image: { + width: deviceWidth, + height: deviceWidth + }, + titleContainer: { + width: '100%', + alignItems: 'center', + marginBottom: 15 + }, + title: { + color: '#ffffff', + fontSize: 16, + fontWeight: '600' + } +}; + +export default class extends React.PureComponent { + static propTypes = { + title: PropTypes.string.isRequired, + image: PropTypes.string.isRequired, + isVisible: PropTypes.bool, + onClose: PropTypes.func.isRequired + } + render() { + const { image, isVisible, onClose, title } = this.props; + return ( + + + {title} + + + + + + + + ); + } +} diff --git a/app/containers/routes/AuthRoutes.js b/app/containers/routes/AuthRoutes.js new file mode 100644 index 000000000..471641924 --- /dev/null +++ b/app/containers/routes/AuthRoutes.js @@ -0,0 +1,62 @@ +import React from 'react'; +import { StackNavigator, DrawerNavigator } from 'react-navigation'; +// import { Platform } from 'react-native'; + +import Sidebar from '../../containers/Sidebar'; +import DrawerMenuButton from '../../presentation/DrawerMenuButton'; + +import RoomsListView from '../../views/RoomsListView'; +import RoomView from '../../views/RoomView'; +import CreateChannelView from '../../views/CreateChannelView'; + +const drawerPosition = 'left'; +const drawerIconPosition = 'headerLeft'; + + +const AuthRoutes = StackNavigator( + { + RoomsList: { + screen: RoomsListView, + navigationOptions({ navigation }) { + return { + title: 'Rooms', + [drawerIconPosition]: () + }; + } + }, + Room: { + screen: RoomView, + navigationOptions({ navigation }) { + return { + title: navigation.state.params.title || 'Room' + // [drawerIconPosition]: ()รท + }; + } + }, + CreateChannel: { + screen: CreateChannelView, + navigationOptions: { + title: 'Create Channel' + } + } + }, + { + } +); + +const Routes = DrawerNavigator({ + Home: { + screen: AuthRoutes, + navigationOptions({ navigation }) { + return { + title: 'Rooms', + [drawerIconPosition]: () + }; + } + } +}, { + contentComponent: Sidebar, + drawerPosition +}); + +export default Routes; diff --git a/app/containers/routes/PublicRoutes.js b/app/containers/routes/PublicRoutes.js new file mode 100644 index 000000000..3cf851f5c --- /dev/null +++ b/app/containers/routes/PublicRoutes.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; +import { StackNavigator } from 'react-navigation'; +import Icon from 'react-native-vector-icons/FontAwesome'; + +import ListServerView from '../../views/ListServerView'; +import NewServerView from '../../views/NewServerView'; +import LoginView from '../../views/LoginView'; + +const PublicRoutes = StackNavigator( + { + ListServer: { + screen: ListServerView, + navigationOptions({ navigation }) { + return { + title: 'Servers', + headerRight: ( + navigation.navigate('AddServer')} + style={{ width: 50, alignItems: 'center' }} + > + + + ) + }; + } + }, + AddServer: { + screen: NewServerView, + navigationOptions: { + title: 'New server' + } + }, + Login: { + screen: LoginView, + navigationOptions: { + title: 'Login' + } + } + }, + { + + } +); + +export default PublicRoutes; diff --git a/app/images/logo.svg b/app/images/logo.svg deleted file mode 100644 index 33663753b..000000000 --- a/app/images/logo.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/index.js b/app/index.js index 01fde6b73..cf9d88777 100644 --- a/app/index.js +++ b/app/index.js @@ -1,82 +1,14 @@ -import PropTypes from 'prop-types'; import React from 'react'; -import { View, Image } from 'react-native'; -import { connect } from 'react-redux'; -import * as Animatable from 'react-native-animatable'; -import setNavigator from './actions/navigator'; -import { appInit } from './actions'; -import LoginView from './views/login'; -import ListServerView from './views/serverList'; - - -import styles from './views/Styles'; +import { Provider } from 'react-redux'; import store from './lib/createStore'; -export const authenticated = WrappedComponent => class _p extends React.PureComponent { - constructor() { - super(); - this.login = store.getState().login; - if (!this.login.token || this.login.failure) { - return store.getState().navigator.resetTo({ - screen: 'Login', - animated: false - }); - } - } - render() { - // Wraps the input component in a container, without mutating it. Good! - return ((this.login.isAuthenticated || this.login.user) && ); - } -}; -// -export class PublicScreen extends React.PureComponent { - render() { - return ((this.login.isAuthenticated || this.login.user) && ); - } -} +import Routes from './containers/Routes'; +const RocketChat = () => ( + + + +); -@connect(null, dispatch => ({ - setNavigator: navigator => dispatch(setNavigator(navigator)) -})) -export class PrivateScreen extends React.PureComponent { - render() { - return (); - } -} -@connect(() => ({ - // logged: state.login.isAuthenticated -}), dispatch => ({ - setNavigator: navigator => dispatch(setNavigator(navigator)), - appInit: () => dispatch(appInit()) -})) -export const HomeScreen = class extends React.PureComponent { - static propTypes = { - appInit: PropTypes.func.isRequired, - setNavigator: PropTypes.func.isRequired, - navigator: PropTypes.object.isRequired - } - static navigatorStyle = { - navBarHidden: true, - - rightButtons: [{ - id: 'close', - title: 'Cancel' - }] - }; - componentWillMount() { - this.props.setNavigator(this.props.navigator); - this.props.appInit(); - // - // this.props.navigator.setDrawerEnabled({ - // side: 'left', // the side of the drawer since you can have two, 'left' / 'right' - // enabled: false // should the drawer be enabled or disabled (locked closed) - // }); - } - render() { - return ( - - ); - } -}; +export default RocketChat; diff --git a/app/lib/createStore.js b/app/lib/createStore.js index 2f1220570..4cfc19130 100644 --- a/app/lib/createStore.js +++ b/app/lib/createStore.js @@ -3,22 +3,34 @@ import 'regenerator-runtime/runtime'; import { createStore, applyMiddleware } from 'redux'; import createSagaMiddleware from 'redux-saga'; +import { composeWithDevTools } from 'remote-redux-devtools'; import reducers from '../reducers'; import sagas from '../sagas'; const sagaMiddleware = createSagaMiddleware(); -let middleware; +let enhacers; if (__DEV__) { /* eslint-disable global-require */ const reduxImmutableStateInvariant = require('redux-immutable-state-invariant').default(); - middleware = [sagaMiddleware, reduxImmutableStateInvariant]; + enhacers = composeWithDevTools( + applyMiddleware(reduxImmutableStateInvariant), + applyMiddleware(sagaMiddleware) + ); } else { - middleware = [sagaMiddleware]; + enhacers = composeWithDevTools( + applyMiddleware(sagaMiddleware) + ); } -export default createStore( - reducers, - applyMiddleware(...middleware) -); +const store = enhacers(createStore)(reducers); sagaMiddleware.run(sagas); + +if (module.hot && typeof module.hot.accept === 'function') { + module.hot.accept(() => { + store.replaceReducer(require('../reducers').default); + sagaMiddleware.run(require('../sagas').default); + }); +} + +export default store; diff --git a/app/lib/realm.js b/app/lib/realm.js index f77882afa..5ae585024 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', @@ -94,63 +95,6 @@ const messagesSchema = { _updatedAt: { type: 'date', optional: true }, temp: { type: 'bool', optional: true } } - - // a: { - // attachments: [ - // { - // color: 'danger', - // fields: [ - // { - // title: 'Bounce Type', - // value: 'Permanent' - // }, - // { - // title: 'Bounce Sub Type', - // value: 'General' - // }, - // { - // title: 'Reporting MTA', - // value: 'dsn; a8-82.smtp-out.amazonses.com' - // }, - // { - // title: 'Timestamp', - // value: 'Tue Apr 19 2016 14:11:08 GMT-0400 (EDT)' - // } - // ] - // }, - // { - // fields: [ - // { - // title: 'Email Address', - // value: 'aaa@asd.at' - // }, - // { - // title: 'Status', - // value: '5.1.1' - // }, - // { - // title: 'Action', - // value: 'failed' - // }, - // { - // title: 'Diagnostic Code', - // value: 'smtp; 550 5.1.1 : Recipient address rejected: User unknown in virtual mailbox table' - // } - // ] - // } - // ], - // bot: { - // i: 'EMQ3S3GGNJrrgJa4Z' - // }, - // u: { - // _id: 'rocket.cat', - // username: 'rocket.cat' - // }, - // roles: [ - // 'bot', - // null - // ] - // } }; // // Realm.clearTestState(); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index a9fd9c8b2..71ddb447d 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -4,7 +4,7 @@ import { AsyncStorage } from 'react-native'; import { hashPassword } from 'react-native-meteor/lib/utils'; import RNFetchBlob from 'react-native-fetch-blob'; -import reduxStore from '../lib/createStore'; +import reduxStore from './createStore'; import settingsType from '../constants/settings'; import realm from './realm'; import * as actions from '../actions'; @@ -366,33 +366,25 @@ 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 { server, login } = reduxStore.getState(); const data = subscriptions.map((subscription, index) => { subscription._updatedAt = rooms[index]._updatedAt; + subscription._server = { id: server.server }; return subscription; }); - // Meteor.subscribe('stream-notify-user', `${ Meteor.userId() }/rooms-changed`, false); + realm.write(() => { data.forEach((subscription) => { - // const subscription = { - // _id: item._id - // }; - // if (typeof item.value === 'string') { - // subscription.value = item.value; - // } - subscription._server = { id: reduxStore.getState().server.server }; - // write('subscriptions', subscription); realm.create('subscriptions', subscription, true); }); }); - Meteor.subscribe('stream-notify-user', `${ reduxStore.getState().user.id }/subscriptions-changed`, false); + Meteor.subscribe('stream-notify-user', `${ login.user.id }/subscriptions-changed`, false); return data; - }).then(data => data); - // }); + }); }, logout() { return AsyncStorage.clear(); diff --git a/app/navigation.js b/app/navigation.js deleted file mode 100644 index 9ee61d2f6..000000000 --- a/app/navigation.js +++ /dev/null @@ -1,32 +0,0 @@ -import { Navigation } from 'react-native-navigation'; -import { Provider } from 'react-redux'; - -import LoginView from './views/login'; -import NewServerView from './views/serverNew'; -import ListServerView from './views/serverList'; -import RoomsListView from './views/roomsList'; -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'; - -// 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); -Navigation.registerComponent('Login', () => LoginView, store, Provider); -Navigation.registerComponent('NewServer', () => NewServerView, store, Provider); -Navigation.registerComponent('CreateChannel', () => CreateChannel, store, Provider); - -Navigation.startSingleScreenApp({ - screen: { - screen: 'home', - title: 'private' - }, - animationType: 'slide-up' -}); diff --git a/app/presentation/DrawerMenuButton.js b/app/presentation/DrawerMenuButton.js new file mode 100644 index 000000000..c1d743a75 --- /dev/null +++ b/app/presentation/DrawerMenuButton.js @@ -0,0 +1,18 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { TouchableOpacity } from 'react-native'; +import Icon from 'react-native-vector-icons/FontAwesome'; + +const DrawerMenuButton = ({ navigation }) => ( + navigation.navigate('DrawerOpen')} + > + + +); + +DrawerMenuButton.propTypes = { + navigation: PropTypes.object.isRequired +}; + +export default DrawerMenuButton; diff --git a/app/presentation/KeyboardView.js b/app/presentation/KeyboardView.js index ba1e50ea0..3a6ea5f66 100644 --- a/app/presentation/KeyboardView.js +++ b/app/presentation/KeyboardView.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { KeyboardAvoidingView, Platform } from 'react-native'; +import { KeyboardAvoidingView } from 'react-native'; export default class KeyboardView extends React.PureComponent { static propTypes = { @@ -14,7 +14,7 @@ export default class KeyboardView extends React.PureComponent { render() { return ( - + {this.props.children} ); diff --git a/app/reducers/app.js b/app/reducers/app.js new file mode 100644 index 000000000..3ae8bb9c5 --- /dev/null +++ b/app/reducers/app.js @@ -0,0 +1,22 @@ +import { APP } from '../actions/actionsTypes'; + +const initialState = { + starting: true +}; + +export default function app(state = initialState, action) { + switch (action.type) { + case APP.INIT: + return { + ...state, + starting: true + }; + case APP.READY: + return { + ...state, + starting: false + }; + default: + return state; + } +} diff --git a/app/reducers/index.js b/app/reducers/index.js index 05ce591fc..3321b8481 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -6,8 +6,9 @@ import messages from './messages'; import server from './server'; import navigator from './navigator'; import createChannel from './createChannel'; +import app from './app'; export default combineReducers({ - settings, login, meteor, messages, server, navigator, createChannel + settings, login, meteor, messages, server, navigator, createChannel, app }); diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js index 169b10dac..74ac67fc9 100644 --- a/app/sagas/createChannel.js +++ b/app/sagas/createChannel.js @@ -1,5 +1,5 @@ import { delay } from 'redux-saga'; -import { select, put, call, fork, take } from 'redux-saga/effects'; +import { select, put, call, take, takeEvery } from 'redux-saga/effects'; import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes'; import { createChannelSuccess, createChannelFailure } from '../actions/createChannel'; import RocketChat from '../lib/rocketchat'; @@ -9,26 +9,23 @@ const create = function* create(data) { return yield RocketChat.createChannel(data); }; -const get = function* get() { - while (true) { - try { - const { data } = yield take(CREATE_CHANNEL.REQUEST); - const auth = yield select(state => state.login.isAuthenticated); - if (!auth) { - yield take(LOGIN.SUCCESS); - } - const result = yield call(create, data); - yield put(createChannelSuccess(result)); - select(({ navigator }) => navigator).dismissModal({ - animationType: 'slide-down' - }); - } catch (err) { - yield delay(2000); - yield put(createChannelFailure(err)); +const get = function* get({ data }) { + try { + const auth = yield select(state => state.login.isAuthenticated); + if (!auth) { + yield take(LOGIN.SUCCESS); } + const result = yield call(create, data); + yield put(createChannelSuccess(result)); + select(({ navigator }) => navigator).dismissModal({ + animationType: 'slide-down' + }); + } catch (err) { + yield delay(2000); + yield put(createChannelFailure(err)); } }; const getData = function* getData() { - yield fork(get); + yield takeEvery(CREATE_CHANNEL.REQUEST, get); }; export default getData; diff --git a/app/sagas/index.js b/app/sagas/index.js index e8b716056..fcc8fd72f 100644 --- a/app/sagas/index.js +++ b/app/sagas/index.js @@ -1,5 +1,4 @@ -import { fork, take } from 'redux-saga/effects'; -import * as types from '../actions/actionsTypes'; +import { all } from 'redux-saga/effects'; import hello from './hello'; import login from './login'; import connect from './connect'; @@ -10,15 +9,16 @@ import createChannel from './createChannel'; import init from './init'; const root = function* root() { - yield fork(init); - yield take(types.APP.READY); - yield fork(createChannel); - yield fork(hello); - yield fork(rooms); - yield fork(login); - yield fork(connect); - yield fork(messages); - yield fork(selectServer); + yield all([ + init(), + createChannel(), + hello(), + rooms(), + login(), + connect(), + messages(), + selectServer() + ]); }; // Consider using takeEvery export default root; diff --git a/app/sagas/init.js b/app/sagas/init.js index 0308610f7..cb53af7de 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -1,5 +1,5 @@ import { AsyncStorage } from 'react-native'; -import { call, put, select, take } from 'redux-saga/effects'; +import { call, put, take } from 'redux-saga/effects'; import * as actions from '../actions'; import { setServer } from '../actions/server'; import { APP } from '../actions/actionsTypes'; @@ -7,16 +7,10 @@ import { APP } from '../actions/actionsTypes'; const restore = function* restore() { try { yield take(APP.INIT); - const { navigator } = yield select(state => state); const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer'); yield put(actions.appReady({})); if (currentServer) { yield put(setServer(currentServer)); - } else { - navigator.resetTo({ - screen: 'ListServer', - animated: false - }); } } catch (e) { console.log(e); diff --git a/app/sagas/login.js b/app/sagas/login.js index b987d4f39..780655e7e 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -1,5 +1,5 @@ import { AsyncStorage } from 'react-native'; -import { take, put, call, takeEvery, fork, select, all, race } from 'redux-saga/effects'; +import { take, put, call, takeEvery, select, all, race } from 'redux-saga/effects'; import * as types from '../actions/actionsTypes'; import { loginRequest, loginSuccess, loginFailure, setToken, logout } from '../actions/login'; import RocketChat from '../lib/rocketchat'; @@ -30,7 +30,7 @@ const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() { try { yield take(types.METEOR.SUCCESS); yield call(getToken); - const { navigator } = yield select(state => state); + // const { navigator } = yield select(state => state); const user = yield select(getUser); if (user.token) { @@ -48,9 +48,9 @@ const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() { // }); // } } - navigator.resetTo({ - screen: 'Rooms' - }); + // navigator.resetTo({ + // screen: 'Rooms' + // }); } catch (e) { console.log(e); } @@ -63,46 +63,33 @@ const saveToken = function* saveToken() { 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) { - if (err.error === 403) { - yield put(logout()); - } else { - yield put(loginFailure(err)); - } +const handleLoginRequest = function* handleLoginRequest({ credentials }) { + try { + const response = yield call(loginCall, credentials); + yield put(loginSuccess(response)); + } catch (err) { + if (err.error === 403) { + yield put(logout()); + } else { + 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 handleLoginSubmit = function* handleLoginSubmit({ credentials }) { + // put a login request + yield put(loginRequest(credentials)); + // wait for a response + yield race({ + success: take(types.LOGIN.SUCCESS), + error: take(types.LOGIN.FAILURE) + }); }; const root = function* root() { yield takeEvery(types.SERVER.CHANGED, handleLoginWhenServerChanges); - yield fork(handleLoginRequest); + yield takeEvery(types.LOGIN.REQUEST, handleLoginRequest); yield takeEvery(types.LOGIN.SUCCESS, saveToken); - yield fork(handleLoginSubmit); + yield takeEvery(types.LOGIN.SUBMIT, handleLoginSubmit); }; export default root; diff --git a/app/sagas/messages.js b/app/sagas/messages.js index 94d4ad4d8..e21b28ce9 100644 --- a/app/sagas/messages.js +++ b/app/sagas/messages.js @@ -5,7 +5,6 @@ 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); } diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index a751d6b8d..472f3e5d3 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -13,6 +13,7 @@ const watchRoomsRequest = function* watchRoomsRequest() { yield call(getRooms); yield put(roomsSuccess()); } catch (err) { + console.log(err); yield put(roomsFailure(err.status)); } }; diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index 88d6a0b8e..f03f2985f 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -1,7 +1,7 @@ import { put, takeEvery, call, takeLatest, race, take } from 'redux-saga/effects'; import { delay } from 'redux-saga'; import { AsyncStorage } from 'react-native'; -import { Navigation } from 'react-native-navigation'; +// import { Navigation } from 'react-native-navigation'; import { SERVER } from '../actions/actionsTypes'; import { connectRequest, disconnect } from '../actions/connect'; import { changedServer, serverSuccess, serverFailure, serverRequest } from '../actions/server'; @@ -17,9 +17,6 @@ const selectServer = function* selectServer({ server }) { yield put(changedServer(server)); yield call([AsyncStorage, 'setItem'], 'currentServer', server); yield put(connectRequest(server)); - yield Navigation.dismissModal({ - animationType: 'slide-down' - }); }; @@ -37,7 +34,7 @@ const validateServer = function* validateServer({ server }) { const addServer = function* addServer({ server }) { yield call(serverRequest, server); - const { error } = race({ + const { error } = yield race({ error: take(SERVER.FAILURE), success: take(SERVER.SUCCESS) }); @@ -45,9 +42,6 @@ const addServer = function* addServer({ server }) { realm.write(() => { realm.create('servers', { id: server, current: false }, true); }); - Navigation.dismissModal({ - animationType: 'slide-down' - }); } }; diff --git a/app/views/CreateChannel.js b/app/views/CreateChannelView.js similarity index 51% rename from app/views/CreateChannel.js rename to app/views/CreateChannelView.js index 1f83d83e9..2970834f2 100644 --- a/app/views/CreateChannel.js +++ b/app/views/CreateChannelView.js @@ -1,4 +1,3 @@ - import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; @@ -19,8 +18,7 @@ export default class CreateChannelView extends React.Component { }); static propTypes = { createChannel: PropTypes.func.isRequired, - result: PropTypes.object.isRequired, - navigator: PropTypes.object.isRequired + result: PropTypes.object.isRequired } constructor(props) { @@ -30,12 +28,6 @@ export default class CreateChannelView extends React.Component { type: true }; this.state = this.default; - this.props.navigator.setTitle({ - title: 'Create Channel' - }); - // this.props.navigator.setSubTitle({ - // subtitle: 'Channels are where your team communicate.' - // }); } submit() { if (!this.state.channelName.trim() || this.props.result.isFetching) { @@ -45,9 +37,36 @@ export default class CreateChannelView extends React.Component { this.props.createChannel({ name: channelName, users, type }); } + renderChannelNameError() { + if (!this.props.result.failure || this.props.result.error.error !== 'error-duplicate-channel-name') { + return null; + } + + return ( + + {this.props.result.error.reason} + + ); + } + + renderTypeSwitch() { + return ( + + this.setState({ type })} + /> + + {this.state.type ? 'Public' : 'Private'} + + + ); + } + render() { return ( - + Channel Name @@ -62,18 +81,33 @@ export default class CreateChannelView extends React.Component { // onSubmitEditing={() => this.textInput.focus()} placeholder='Type the channel name here' /> - {(this.props.result.failure && this.props.result.error.error === 'error-duplicate-channel-name') ? {this.props.result.error.reason} : null} - - this.setState({ type })} - /> - {this.state.type ? 'Public' : 'Private'} - - {this.state.type ? 'Everyone can access this channel' : 'Just invited people can access this channel'} - this.submit()} style={[styles.buttonContainer_white, { backgroundColor: (this.state.channelName.length === 0 || this.props.result.isFetching) ? '#e1e5e8' : '#1d74f5' }]}> - { this.props.result.isFetching ? 'LOADING' : 'CREATE' }! + {this.renderChannelNameError()} + {this.renderTypeSwitch()} + + {this.state.type ? + 'Everyone can access this channel' : + 'Just invited people can access this channel'} + + this.submit()} + style={[ + styles.buttonContainer_white, + (this.state.channelName.length === 0 || this.props.result.isFetching) ? + styles.disabledButton : styles.enabledButton + ]} + > + + {this.props.result.isFetching ? 'LOADING' : 'CREATE'}! + diff --git a/app/views/serverList.js b/app/views/ListServerView.js similarity index 72% rename from app/views/serverList.js rename to app/views/ListServerView.js index 1c71bbf0c..ecb07bc03 100644 --- a/app/views/serverList.js +++ b/app/views/ListServerView.js @@ -2,9 +2,8 @@ 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'; -import { View, Text, SectionList, Platform, StyleSheet } from 'react-native'; +import { View, Text, SectionList, StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { setServer } from '../actions/server'; import realm from '../lib/realm'; @@ -66,16 +65,17 @@ const zeroconf = new Zeroconf(); @connect(state => ({ server: state.server.server, - login: state.login + login: state.login, + connected: state.meteor.connected }), dispatch => ({ selectServer: server => dispatch(setServer(server)) })) export default class ListServerView extends React.Component { static propTypes = { - navigator: PropTypes.object.isRequired, + navigation: PropTypes.object.isRequired, login: PropTypes.object.isRequired, selectServer: PropTypes.func.isRequired, - actions: PropTypes.object, + connected: PropTypes.bool.isRequired, server: PropTypes.string } @@ -84,37 +84,28 @@ export default class ListServerView extends React.Component { this.state = { sections: [] }; - - this.props.navigator.setTitle({ - title: 'Servers' - }); - - this.props.navigator.setButtons({ - rightButtons: [{ - id: 'add', - title: 'Add' - }], - leftButtons: props.login.isAuthenticated && props.server && Platform.select({ - ios: [{ - id: 'close', - title: 'Close' - }] - }), - animated: true - }); - - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); + this.redirected = false; + realm.addListener('change', this.updateState); } componentWillMount() { - realm.addListener('change', this.updateState); zeroconf.on('update', this.updateState); zeroconf.scan('http', 'tcp', 'local.'); this.state = this.getState(); + } - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); + componentDidUpdate() { + if (this.props.connected && + this.props.server && + !this.props.login.token && + !this.redirected) { + this.redirected = true; + this.props.navigation.navigate('Login'); + } else if (!this.props.connected) { + this.redirected = false; + } } componentWillUnmount() { @@ -123,27 +114,6 @@ export default class ListServerView extends React.Component { zeroconf.removeListener('update', this.updateState); } - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'add') { - Navigation.showModal({ - screen: 'NewServer', - animationType: 'slide-up' - // animationType: 'none' - }); - } - if (event.id === 'close') { - Navigation.dismissModal({ - animationType: 'slide-down' - }); - } - } - - if (event.id === 'didDisappear' && this.state.server) { - this.props.actions.setCurrentServer(this.state.server); - } - } - onPressItem = (item) => { this.props.selectServer(item.id); } @@ -191,7 +161,14 @@ export default class ListServerView extends React.Component { > {item.id} - + + + ); diff --git a/app/views/login.js b/app/views/LoginView.js similarity index 50% rename from app/views/login.js rename to app/views/LoginView.js index 2534a45bd..7f182b7cb 100644 --- a/app/views/login.js +++ b/app/views/LoginView.js @@ -15,9 +15,7 @@ import styles from './Styles'; class LoginView extends React.Component { static propTypes = { - navigator: PropTypes.object.isRequired, loginSubmit: PropTypes.func.isRequired, - server: PropTypes.string.isRequired, Accounts_EmailOrUsernamePlaceholder: PropTypes.string, Accounts_PasswordPlaceholder: PropTypes.string, login: PropTypes.object @@ -35,35 +33,13 @@ class LoginView extends React.Component { password: '' }; } - componentWillReceiveProps() { - const { navigator } = this.props; - navigator.setTitle({ - title: 'Login' - }); - navigator.setSubTitle({ - subtitle: this.props.server - }); - navigator.setButtons({ - rightButtons: [{ - id: 'close', - title: 'Cancel' - }] - }); - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); - } - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close') { - this.props.navigator.resetTo({ - screen: 'ListServer', - animated: false - }); - } - } - } submit = () => { const { username, password, code } = this.state; + if (username.trim() === '' || password.trim() === '') { + return; + } + this.props.loginSubmit({ username, password, code }); Keyboard.dismiss(); } @@ -89,45 +65,51 @@ class LoginView extends React.Component { // {this.props.login.isFetching && LOGANDO} render() { return ( - - - - - - 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}} - + + + this.setState({ username })} + keyboardType='email-address' + autoCorrect={false} + returnKeyType='next' + autoCapitalize='none' + autoFocus + + underlineColorAndroid='transparent' + onSubmitEditing={() => { this.password.focus(); }} + placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || 'Email or username'} + /> + { this.password = e; }} + placeholderTextColor={'rgba(255,255,255,.2)'} + style={styles.input} + onChangeText={password => 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/serverNew.js b/app/views/NewServerView.js similarity index 78% rename from app/views/serverNew.js rename to app/views/NewServerView.js index 2999fc5cf..62ce32643 100644 --- a/app/views/serverNew.js +++ b/app/views/NewServerView.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Navigation } from 'react-native-navigation'; import { Text, TextInput, View, StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { serverRequest, addServer } from '../actions/server'; @@ -55,11 +54,11 @@ const styles = StyleSheet.create({ })) export default class NewServerView extends React.Component { static propTypes = { - navigator: PropTypes.object.isRequired, validateServer: PropTypes.func.isRequired, addServer: PropTypes.func.isRequired, validating: PropTypes.bool.isRequired, - validInstance: PropTypes.bool.isRequired + validInstance: PropTypes.bool.isRequired, + navigation: PropTypes.object.isRequired } static navigationOptions = () => ({ @@ -73,33 +72,20 @@ export default class NewServerView extends React.Component { editable: true, text: '' }; - - this.submit = () => { - this.props.addServer(this.completeUrl(this.state.text.trim() || this.state.defaultServer)); - }; + this.adding = false; + this.props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field } - componentDidMount() { - this.props.navigator.setTitle({ - title: 'New server' - }); - - this.props.navigator.setButtons({ - rightButtons: [{ - id: 'close', - title: 'Cancel' - }], - animated: true - }); - - this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); - } - onNavigatorEvent = (event) => { - if (event.type === 'NavBarButtonPress') { - if (event.id === 'close') { - Navigation.dismissModal({ - animationType: 'slide-down' - }); + componentDidUpdate() { + if (this.adding) { + if (!this.props.validInstance) { + /* eslint-disable react/no-did-update-set-state */ + this.setState({ editable: true }); + this.adding = false; + } + if (this.props.validInstance && !this.props.validating) { + this.props.navigation.goBack(); + this.adding = false; } } } @@ -108,10 +94,18 @@ export default class NewServerView extends React.Component { this.setState({ text }); this.props.validateServer(this.completeUrl(text)); } + + submit = () => { + this.setState({ editable: false }); + this.adding = true; + this.props.addServer(this.completeUrl(this.state.text.trim() || this.state.defaultServer)); + } + completeUrl = (url) => { url = url.trim(); - if (/^(\w|[0-9-_]){3,}$/.test(url) && /^(htt(ps?)?)|(loca((l)?|(lh)?|(lho)?|(lhos)?|(lhost:?\d*)?)$)/.test(url) === false) { + if (/^(\w|[0-9-_]){3,}$/.test(url) && + /^(htt(ps?)?)|(loca((l)?|(lh)?|(lho)?|(lhos)?|(lhost:?\d*)?)$)/.test(url) === false) { url = `${ url }.rocket.chat`; } diff --git a/app/views/Photo.js b/app/views/Photo.js index 96360bffa..e457fb6ba 100644 --- a/app/views/Photo.js +++ b/app/views/Photo.js @@ -16,10 +16,10 @@ const styles = { export default class extends React.PureComponent { static propTypes = { - image: PropTypes.string.isRequired + navigation: PropTypes.object.isRequired } render() { - const { image } = this.props; + const { image } = this.props.navigation.state.params; return ( diff --git a/app/views/room.js b/app/views/RoomView.js similarity index 81% rename from app/views/room.js rename to app/views/RoomView.js index 832126cac..b57690721 100644 --- a/app/views/room.js +++ b/app/views/RoomView.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Text, View, StyleSheet, Button } from 'react-native'; +import { Text, View, StyleSheet, Button, InteractionManager } from 'react-native'; import { ListView } from 'realm/react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; @@ -9,12 +9,11 @@ import * as actions from '../actions'; import { messagesRequest } from '../actions/messages'; import realm from '../lib/realm'; import RocketChat from '../lib/rocketchat'; -import debounce from '../utils/throttle'; import Message from '../containers/Message'; import MessageBox from '../containers/MessageBox'; import KeyboardView from '../presentation/KeyboardView'; -const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); +const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1._id !== r2._id }); const styles = StyleSheet.create({ container: { flex: 1, @@ -56,7 +55,7 @@ const styles = StyleSheet.create({ })) export default class RoomView extends React.Component { static propTypes = { - navigator: PropTypes.object.isRequired, + navigation: PropTypes.object.isRequired, getMessages: PropTypes.func.isRequired, rid: PropTypes.string, sid: PropTypes.string, @@ -69,42 +68,37 @@ export default class RoomView extends React.Component { constructor(props) { super(props); - this.rid = props.rid || realm.objectForPrimaryKey('subscriptions', props.sid).rid; - // this.rid = 'GENERAL'; + + this.sid = props.navigation.state.params.room.sid; + this.rid = props.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.state = { - dataSource: ds.cloneWithRows(this.data), + slow: false, + dataSource: [], loaded: true, joined: typeof props.rid === 'undefined' }; - - this.props.navigator.setTitle({ - title: this.props.name || realm.objectForPrimaryKey('subscriptions', this.props.sid).name - }); } componentWillMount() { + this.props.navigation.setParams({ + title: this.props.name || realm.objectForPrimaryKey('subscriptions', this.sid).name + }); + this.timer = setTimeout(() => this.setState({ slow: true }), 5000); this.props.getMessages(this.rid); - // const late = setTimeout(() => this.setState({ - // loaded: false - // }), 1000); - // RocketChat.loadMessagesForRoom(this.rid, null, () => { - // clearTimeout(late); - // this.setState({ - // loaded: true - // }); this.data.addListener(this.updateState); - // }); - // this.updateState(); + this.state.dataSource = ds.cloneWithRows(this.data); } - componentDidMount() { - return RocketChat.readMessages(this.rid); + InteractionManager.runAfterInteractions(() => RocketChat.readMessages(this.rid)); + } + componentDidUpdate() { + return !this.props.loading && clearTimeout(this.timer); } - componentWillUnmount() { - this.data.removeListener(this.updateState); + clearTimeout(this.timer); + this.data.removeAllListeners(); } onEndReached = () => { @@ -126,28 +120,22 @@ export default class RoomView extends React.Component { } } - updateState = debounce(() => { + updateState = () => { this.setState({ dataSource: ds.cloneWithRows(this.data) }); - // RocketChat.readMessages(this.rid); - // this.setState({ - // messages: this.messages - // }); - }, 100); + }; sendMessage = message => RocketChat.sendMessage(this.rid, message); - joinRoom = () => { - RocketChat.joinRoom(this.props.rid) - .then(() => { - this.setState({ - joined: true - }); - }); + joinRoom = async() => { + await RocketChat.joinRoom(this.props.rid); + this.setState({ + joined: true + }); }; - renderBanner = () => (this.props.loading ? + renderBanner = () => (this.state.slow && this.props.loading ? ( Loading new messages... diff --git a/app/views/roomsList.js b/app/views/RoomsListView.js similarity index 82% rename from app/views/roomsList.js rename to app/views/RoomsListView.js index f4c8c6b8e..fe88cc3ef 100644 --- a/app/views/roomsList.js +++ b/app/views/RoomsListView.js @@ -1,10 +1,9 @@ import ActionButton from 'react-native-action-button'; -import { Navigation } from 'react-native-navigation'; 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, Platform } from 'react-native'; +import { View, StyleSheet, TextInput } from 'react-native'; import { connect } from 'react-redux'; import * as actions from '../actions'; import * as server from '../actions/connect'; @@ -24,7 +23,8 @@ const styles = StyleSheet.create({ backgroundColor: '#E7E7E7' }, list: { - width: '100%' + width: '100%', + backgroundColor: '#FFFFFF' }, emptyView: { flexGrow: 1, @@ -67,46 +67,35 @@ const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); export default class RoomsListView extends React.Component { static propTypes = { - navigator: PropTypes.object.isRequired, + navigation: PropTypes.object.isRequired, Site_Url: PropTypes.string, server: PropTypes.string } constructor(props) { super(props); - this.data = realm.objects('subscriptions').filtered('_server.id = $0', this.props.server); + this.state = { - dataSource: ds.cloneWithRows(this.data), + dataSource: [], searching: false, searchDataSource: [], 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 - }); + this.data = realm.objects('subscriptions').filtered('_server.id = $0', this.props.server).sorted('_updatedAt', true); } + componentWillMount() { - const button = Platform.OS === 'ios' ? 'leftButtons' : 'rightButtons'; - this.props.navigator.setButtons({ - [button]: [{ - id: 'servers', - title: 'Servers' - }], - animated: true - }); + this.data.addListener(this.updateState); + + this.state = { + ...this.state, + dataSource: ds.cloneWithRows(this.data) + }; } + componentWillUnmount() { - this.data.removeListener(this.updateState); + this.data.removeAllListeners(); } onSearchChangeText = (text) => { @@ -167,7 +156,6 @@ export default class RoomsListView extends React.Component { }); } - updateState = () => { this.setState({ dataSource: ds.cloneWithRows(this.data) @@ -176,10 +164,7 @@ export default class RoomsListView extends React.Component { _onPressItem = (id, item = {}) => { const navigateToRoom = (room) => { - this.props.navigator.push({ - screen: 'Room', - passProps: room - }); + this.props.navigation.navigate('Room', { room }); }; const clearSearch = () => { @@ -220,16 +205,11 @@ export default class RoomsListView extends React.Component { navigateToRoom({ sid: id }); clearSearch(); } - _createChannel = () => { - Navigation.showModal({ - screen: 'CreateChannel', - title: 'Create a New Channel', - passProps: {}, - navigatorStyle: {}, - navigatorButtons: {}, - animationType: 'slide-up' - }); + + _createChannel() { + this.props.navigation.navigate('CreateChannel'); } + renderSearchBar = () => ( ( this._onPressItem(item._id, item)} /> ) + renderList = () => ( ) + renderCreateButtons = () => ( { this._createChannel(); }} > - ); - render= () => ( + + ); + + render = () => ( {this.renderList()} diff --git a/app/views/Styles.js b/app/views/Styles.js index 1ec446317..c4c040baa 100644 --- a/app/views/Styles.js +++ b/app/views/Styles.js @@ -1,6 +1,16 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Dimensions } from 'react-native'; export default StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#2f343d', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'stretch' + }, + loginView: { + padding: 20 + }, view: { flex: 1, flexDirection: 'column', @@ -24,9 +34,9 @@ export default StyleSheet.create({ justifyContent: 'center' }, logo: { - width: 150, - // backgroundColor: 'red' - // height: 150, + width: Dimensions.get('window').width - 30, + height: Dimensions.get('window').width - 30, + borderRadius: 5, resizeMode: 'contain' }, formContainer: { @@ -46,6 +56,12 @@ export default StyleSheet.create({ marginBottom: 5, color: '#2f343d' }, + label_error: { + color: 'red', + flexGrow: 1, + paddingHorizontal: 0, + marginBottom: 20 + }, input: { height: 45, marginBottom: 20, @@ -101,5 +117,21 @@ export default StyleSheet.create({ backgroundColor: 'rgba(255,255,255,.2)', left: 0, top: 0 + }, + switchContainer: { + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + paddingHorizontal: 0 + }, + switchLabel: { + flexGrow: 1, + paddingHorizontal: 10 + }, + disabledButton: { + backgroundColor: '#e1e5e8' + }, + enabledButton: { + backgroundColor: '#1d74f5' } }); diff --git a/index.android.js b/index.android.js index 6902f2f21..9a965fd5c 100644 --- a/index.android.js +++ b/index.android.js @@ -1 +1,10 @@ -import './index.ios'; +// import 'babel-polyfill'; +// import 'regenerator-runtime/runtime'; +import { AppRegistry } from 'react-native'; + + +import RocketChat from './app/index'; +// import { AppRegistry } from 'react-native'; +// import Routes from './app/routes'; +// +AppRegistry.registerComponent('RocketChatRN', () => RocketChat); diff --git a/index.ios.js b/index.ios.js index dac1db546..4873f8ca1 100644 --- a/index.ios.js +++ b/index.ios.js @@ -1,7 +1,11 @@ -import 'babel-polyfill'; -import 'regenerator-runtime/runtime'; -import './app/navigation'; +// import 'babel-polyfill'; +// import 'regenerator-runtime/runtime'; +import { AppRegistry } from 'react-native'; + + +import RocketChat from './app/index'; // import { AppRegistry } from 'react-native'; // import Routes from './app/routes'; // -// AppRegistry.registerComponent('RocketChatRN', () => Routes); +AppRegistry.registerComponent('RocketChatRN', () => RocketChat); + diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 20ac0d36d..e7d9c0915 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -5,7 +5,6 @@ }; objectVersion = 46; objects = { - /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; @@ -51,10 +50,8 @@ AE5D35882AE04CC29630FB3D /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DC6EE17B5550465E98C70FF0 /* Entypo.ttf */; }; B8E79AF41F3CD167005B464F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB61A68108700A75B9A /* Info.plist */; }; BED2B77AA660460E8BC9F8E0 /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */; }; - C03BD55E219B47C9BCA9CABE /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 841151A0236F49EBBC246374 /* libReactNativeNavigation.a */; }; C758F0BD5C3244E2BA073E61 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B696712EE2345A59F007A88 /* libRNImagePicker.a */; }; CBD0E0A35B174C4DBFED3B31 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E528DE3A405E43B4A37ABA68 /* Zocial.ttf */; }; - D584DAA0EC56473197D4F6BD /* libAutoGrowTextInput.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 91CD29A0CB114F3E9D50596E /* libAutoGrowTextInput.a */; }; D6408D9E4A864FF6BA986857 /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8A2DD67ADD954AD9873F45FC /* SimpleLineIcons.ttf */; }; EF736EF520A64AE8820E684A /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF26CC845883492D8AC8869B /* libRealmReact.a */; }; /* End PBXBuildFile section */ @@ -277,13 +274,6 @@ remoteGlobalIDString = F60690131CA2766F0003FB26; remoteInfo = RealmReact; }; - 60B837611F3F6F4C00677E56 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 09FBCE5FF9B547D69122C25F /* AutoGrowTextInput.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D81C36501CDB667F00777FB9; - remoteInfo = AutoGrowTextInput; - }; 60B837831F3F6F4C00677E56 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 4B38C7E37A8748E0BC665078 /* RNImagePicker.xcodeproj */; @@ -291,13 +281,6 @@ remoteGlobalIDString = 014A3B5C1C6CF33500B6D375; remoteInfo = RNImagePicker; }; - 60B8378C1F3F6F4C00677E56 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6F85FE1D97EC4DDBA7286733 /* ReactNativeNavigation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D8AFADBD1BEE6F3F00A4592D; - remoteInfo = ReactNativeNavigation; - }; 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; @@ -353,7 +336,6 @@ 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* RocketChatRNTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RocketChatRNTests.m; sourceTree = ""; }; 06BB44DD4855498082A744AD /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; - 09FBCE5FF9B547D69122C25F /* AutoGrowTextInput.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = AutoGrowTextInput.xcodeproj; path = "../node_modules/react-native-autogrow-textinput/ios/AutoGrowTextInput.xcodeproj"; sourceTree = ""; }; 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* RocketChatRN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RocketChatRN.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -378,13 +360,10 @@ 5A8684E7C27E426C9206E980 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RealmReact.xcodeproj; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = ""; }; 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = ""; }; - 6F85FE1D97EC4DDBA7286733 /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; - 841151A0236F49EBBC246374 /* libReactNativeNavigation.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libReactNativeNavigation.a; sourceTree = ""; }; 8A2DD67ADD954AD9873F45FC /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; }; - 91CD29A0CB114F3E9D50596E /* libAutoGrowTextInput.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libAutoGrowTextInput.a; sourceTree = ""; }; 9A1E1766CCB84C91A62BD5A6 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = ""; }; A18EFC3B0CFE40E0918A8F0C /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = ""; }; B37C79D9BD0742CE936B6982 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; @@ -429,8 +408,6 @@ 77C35F50C01C43668188886C /* libRNVectorIcons.a in Frameworks */, 8A159EDB97C44E52AF62D69C /* libRNSVG.a in Frameworks */, C758F0BD5C3244E2BA073E61 /* libRNImagePicker.a in Frameworks */, - C03BD55E219B47C9BCA9CABE /* libReactNativeNavigation.a in Frameworks */, - D584DAA0EC56473197D4F6BD /* libAutoGrowTextInput.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -602,22 +579,6 @@ name = Products; sourceTree = ""; }; - 60B8375E1F3F6F4B00677E56 /* Products */ = { - isa = PBXGroup; - children = ( - 60B837621F3F6F4C00677E56 /* libAutoGrowTextInput.a */, - ); - name = Products; - sourceTree = ""; - }; - 60B837891F3F6F4C00677E56 /* Products */ = { - isa = PBXGroup; - children = ( - 60B8378D1F3F6F4C00677E56 /* libReactNativeNavigation.a */, - ); - name = Products; - sourceTree = ""; - }; 78C398B11ACF4ADC00677621 /* Products */ = { isa = PBXGroup; children = ( @@ -647,8 +608,6 @@ 22A8B76C8EBA443BB97CE82D /* RNVectorIcons.xcodeproj */, C23AEF1D9EBE4A38A1A6B97B /* RNSVG.xcodeproj */, 4B38C7E37A8748E0BC665078 /* RNImagePicker.xcodeproj */, - 6F85FE1D97EC4DDBA7286733 /* ReactNativeNavigation.xcodeproj */, - 09FBCE5FF9B547D69122C25F /* AutoGrowTextInput.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -869,10 +828,6 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( - { - ProductGroup = 60B8375E1F3F6F4B00677E56 /* Products */; - ProjectRef = 09FBCE5FF9B547D69122C25F /* AutoGrowTextInput.xcodeproj */; - }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; @@ -917,10 +872,6 @@ ProductGroup = 146834001AC3E56700842450 /* Products */; ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; }, - { - ProductGroup = 60B837891F3F6F4C00677E56 /* Products */; - ProjectRef = 6F85FE1D97EC4DDBA7286733 /* ReactNativeNavigation.xcodeproj */; - }, { ProductGroup = 607D60ED1F325B7D00F639C4 /* Products */; ProjectRef = 5A8684E7C27E426C9206E980 /* RealmReact.xcodeproj */; @@ -1160,13 +1111,6 @@ remoteRef = 607D61151F325B7E00F639C4 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 60B837621F3F6F4C00677E56 /* libAutoGrowTextInput.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libAutoGrowTextInput.a; - remoteRef = 60B837611F3F6F4C00677E56 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 60B837841F3F6F4C00677E56 /* libRNImagePicker.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1174,13 +1118,6 @@ remoteRef = 60B837831F3F6F4C00677E56 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 60B8378D1F3F6F4C00677E56 /* libReactNativeNavigation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReactNativeNavigation.a; - remoteRef = 60B8378C1F3F6F4C00677E56 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/ios/RocketChatRN/AppDelegate.m b/ios/RocketChatRN/AppDelegate.m index e697abcee..0cb97ee64 100644 --- a/ios/RocketChatRN/AppDelegate.m +++ b/ios/RocketChatRN/AppDelegate.m @@ -9,11 +9,6 @@ #import "AppDelegate.h" -// ********************************************** -// *** DON'T MISS: THE NEXT LINE IS IMPORTANT *** -// ********************************************** -#import "RCCManager.h" - #import #import @@ -25,24 +20,17 @@ jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; - // ********************************************** - // *** DON'T MISS: THIS IS HOW WE BOOTSTRAP ***** - // ********************************************** - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - self.window.backgroundColor = [UIColor whiteColor]; - [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"RocketChatRN" + initialProperties:nil + launchOptions:launchOptions]; + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; -// RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation -// moduleName:@"RocketChatRN" -// initialProperties:nil -// launchOptions:launchOptions]; -// rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; -// -// self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; -// UIViewController *rootViewController = [UIViewController new]; -// rootViewController.view = rootView; -// self.window.rootViewController = rootViewController; -// [self.window makeKeyAndVisible]; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; return YES; } diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index 022d4c038..9f1a11793 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -9,7 +9,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) + Rocket.Chat.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -38,7 +38,7 @@ NSCameraUsageDescription Upload images from camera NSLocationWhenInUseUsageDescription - + NSPhotoLibraryUsageDescription Upload images from library UIAppFonts diff --git a/package.json b/package.json index deb8dcec2..65074e984 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "react-native-img-cache": "^1.4.0", "react-native-loading-spinner-overlay": "^0.5.2", "react-native-meteor": "^1.1.0", - "react-native-navigation": "^1.1.193", + "react-native-modal": "^3.1.0", "react-native-optimized-flatlist": "^1.0.1", "react-native-svg": "^5.4.1", "react-native-svg-image": "^1.1.4", @@ -44,8 +44,8 @@ "redux-logger": "^3.0.6", "redux-saga": "^0.15.6", "regenerator-runtime": "^0.11.0", - "strip-ansi": "^4.0.0", - "underscore": "^1.8.3" + "remote-redux-devtools": "^0.5.12", + "strip-ansi": "^4.0.0" }, "devDependencies": { "@storybook/addon-storyshots": "^3.2.6", diff --git a/yarn.lock b/yarn.lock index 3ee65ac64..8ad749b55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1789,6 +1789,10 @@ clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" +clone@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" + clone@^1.0.0, clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" @@ -1865,6 +1869,10 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +component-emitter@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.0.tgz#ccd113a86388d06482d03de3fc7df98526ba8efe" + compressible@~2.0.5: version "2.0.11" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a" @@ -2923,7 +2931,7 @@ fbjs@0.8.12: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -fbjs@^0.8.9: +fbjs@^0.8.12, fbjs@^0.8.9: version "0.8.14" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c" dependencies: @@ -3161,6 +3169,10 @@ get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" +get-params@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/get-params/-/get-params-0.1.2.tgz#bae0dfaba588a0c60d7834c0d8dc2ff60eeef2fe" + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -3360,7 +3372,7 @@ hoist-non-react-statics@1.x.x, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" -hoist-non-react-statics@^2.2.0, hoist-non-react-statics@^2.2.1: +hoist-non-react-statics@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.2.2.tgz#c0eca5a7d5a28c5ada3107eb763b01da6bfa81fb" @@ -4042,6 +4054,10 @@ js-yaml@~3.7.0: argparse "^1.0.7" esprima "^2.6.0" +jsan@^3.1.0, jsan@^3.1.5: + version "3.1.9" + resolved "https://registry.yarnpkg.com/jsan/-/jsan-3.1.9.tgz#2705676c1058f0a7d9ac266ad036a5769cfa7c96" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -4188,6 +4204,10 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +linked-list@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/linked-list/-/linked-list-0.1.0.tgz#798b0ff97d1b92a4fd08480f55aea4e9d49d37bf" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -4357,14 +4377,14 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@4.x.x, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.16.6, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - lodash@^3.10.1, lodash@^3.5.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.16.6, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + lodash@~2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" @@ -5423,19 +5443,19 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@15.5.8: - version "15.5.8" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" - dependencies: - fbjs "^0.8.9" - -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9: +prop-types@15.5.10, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9: version "15.5.10" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" dependencies: fbjs "^0.8.9" loose-envify "^1.3.1" +prop-types@15.5.8: + version "15.5.8" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" + dependencies: + fbjs "^0.8.9" + proxy-addr@~1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" @@ -5674,7 +5694,7 @@ react-native-dismiss-keyboard@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz#32886242b3f2317e121f3aeb9b0a585e2b879b49" -react-native-drawer-layout-polyfill@^1.3.2: +react-native-drawer-layout-polyfill@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/react-native-drawer-layout-polyfill/-/react-native-drawer-layout-polyfill-1.3.2.tgz#192c84d7a5a6b8a6d2be2c7daa5e4164518d0cc7" dependencies: @@ -5728,11 +5748,12 @@ react-native-meteor@^1.1.0: underscore "^1.8.3" wolfy87-eventemitter "^4.3.0" -react-native-navigation@^1.1.193: - version "1.1.198" - resolved "https://registry.yarnpkg.com/react-native-navigation/-/react-native-navigation-1.1.198.tgz#8d4edf44a6ec6b56c9a9c58205f1b136cac2be45" +react-native-modal@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/react-native-modal/-/react-native-modal-3.1.0.tgz#3e6911152e9dcd6e15457cf0c24d7fd833c4a57a" dependencies: - lodash "4.x.x" + prop-types "15.5.10" + react-native-animatable "^1.2.3" react-native-optimized-flatlist@^1.0.1: version "1.0.1" @@ -5749,9 +5770,9 @@ react-native-svg@^5.4.1: color "^0.11.1" lodash "^4.16.6" -react-native-tab-view@^0.0.67: - version "0.0.67" - resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.67.tgz#cdd146fe5e5d4baff6c89f2d5d0b15fa23db39d0" +react-native-tab-view@^0.0.65: + version "0.0.65" + resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.65.tgz#b685ea3081ff7c96486cd997361026c407302c59" dependencies: prop-types "^15.5.8" @@ -5859,15 +5880,16 @@ react-native@0.46.1: yargs "^6.4.0" react-navigation@^1.0.0-beta.11: - version "1.0.0-beta.12" - resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.0.0-beta.12.tgz#cf0f04ffbf9fefe41f5c5a1dab92106b48a29f0a" + version "1.0.0-beta.11" + resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.0.0-beta.11.tgz#4271edb23cdbcc6eb88602f7fde0a77f0ef7a160" dependencies: clamp "^1.0.1" - hoist-non-react-statics "^2.2.0" + fbjs "^0.8.12" + hoist-non-react-statics "^1.2.0" path-to-regexp "^1.7.0" prop-types "^15.5.10" - react-native-drawer-layout-polyfill "^1.3.2" - react-native-tab-view "^0.0.67" + react-native-drawer-layout-polyfill "^1.3.0" + react-native-tab-view "^0.0.65" react-proxy@^1.1.7: version "1.1.8" @@ -6060,6 +6082,13 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" +redux-devtools-instrument@^1.3.3: + version "1.8.2" + resolved "https://registry.yarnpkg.com/redux-devtools-instrument/-/redux-devtools-instrument-1.8.2.tgz#5e91cfe402e790dae3fd2f0d235f7b7d84b09ffe" + dependencies: + lodash "^4.2.0" + symbol-observable "^1.0.2" + redux-immutable-state-invariant@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/redux-immutable-state-invariant/-/redux-immutable-state-invariant-2.0.0.tgz#2954debe7a09ceb65cbe398cff785858c8f30404" @@ -6143,6 +6172,33 @@ regjsparser@^0.1.4: dependencies: jsesc "~0.5.0" +remote-redux-devtools@^0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/remote-redux-devtools/-/remote-redux-devtools-0.5.12.tgz#42cb95dfa9e54c1d9671317c5e7bba41e68caec2" + dependencies: + jsan "^3.1.5" + querystring "^0.2.0" + redux-devtools-instrument "^1.3.3" + remotedev-utils "^0.1.1" + rn-host-detect "^1.0.1" + socketcluster-client "^5.3.1" + +remotedev-serialize@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/remotedev-serialize/-/remotedev-serialize-0.1.0.tgz#074768e98cb7aa806f45994eeb0c8af95120ee32" + dependencies: + jsan "^3.1.0" + +remotedev-utils@^0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/remotedev-utils/-/remotedev-utils-0.1.4.tgz#643700819a943678073c75eb185e81d96620b348" + dependencies: + get-params "^0.1.2" + jsan "^3.1.5" + lodash "^4.0.0" + remotedev-serialize "^0.1.0" + shortid "^2.2.6" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -6266,6 +6322,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" +rn-host-detect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/rn-host-detect/-/rn-host-detect-1.1.3.tgz#242d76e2fa485c48d751416e65b7cce596969e91" + rndm@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/rndm/-/rndm-1.2.0.tgz#f33fe9cfb52bbfd520aa18323bc65db110a1b76c" @@ -6325,6 +6385,26 @@ sax@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240" +sc-channel@~1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/sc-channel/-/sc-channel-1.0.6.tgz#b38bd47a993e78290fbc53467867f6b2a0a08639" + dependencies: + sc-emitter "1.x.x" + +sc-emitter@1.x.x, sc-emitter@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/sc-emitter/-/sc-emitter-1.1.0.tgz#ef119d4222f4c64f887b486964ef11116cdd0e75" + dependencies: + component-emitter "1.2.0" + +sc-errors@~1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/sc-errors/-/sc-errors-1.3.3.tgz#c00bc4c766a970cc8d5937d08cd58e931d7dae05" + +sc-formatter@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-3.0.0.tgz#c91b1fe56c260abd5a6a2e6af98c724bc7998a38" + schema-utils@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" @@ -6467,6 +6547,10 @@ shellwords@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14" +shortid@^2.2.6: + version "2.2.8" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.8.tgz#033b117d6a2e975804f6f0969dbe7d3d0b355131" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -6505,6 +6589,20 @@ sntp@1.x.x: dependencies: hoek "2.x.x" +socketcluster-client@^5.3.1: + version "5.5.2" + resolved "https://registry.yarnpkg.com/socketcluster-client/-/socketcluster-client-5.5.2.tgz#9d4369e0e722ff7e55e5422c2d44f5afe1aff128" + dependencies: + base-64 "0.1.0" + clone "2.1.1" + linked-list "0.1.0" + querystring "0.2.0" + sc-channel "~1.0.6" + sc-emitter "~1.1.0" + sc-errors "~1.3.0" + sc-formatter "~3.0.0" + ws "3.0.0" + sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -6719,7 +6817,7 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -symbol-observable@^1.0.3: +symbol-observable@^1.0.2, symbol-observable@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" @@ -7276,6 +7374,13 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" +ws@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.0.0.tgz#98ddb00056c8390cb751e7788788497f99103b6c" + dependencies: + safe-buffer "~5.0.1" + ultron "~1.1.0" + ws@^1.1.0: version "1.1.4" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.4.tgz#57f40d036832e5f5055662a397c4de76ed66bf61"