add server saga
This commit is contained in:
parent
93f839bfc3
commit
ff75ed6578
|
@ -12,11 +12,11 @@ function createRequestTypes(base, types = defaultTypes) {
|
|||
// Login events
|
||||
export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN', 'SUBMIT']);
|
||||
export const ROOMS = createRequestTypes('ROOMS');
|
||||
export const APP = createRequestTypes('APP', ['READY']);
|
||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
||||
export const MESSAGES = createRequestTypes('MESSAGES');
|
||||
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL');
|
||||
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes, 'REQUEST_USERS', 'SUCCESS_USERS', 'FAILURE_USERS', 'SET_USERS']);
|
||||
export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
|
||||
export const SERVER = createRequestTypes('SERVER', ['SELECT', 'CHANGED']);
|
||||
export const SERVER = createRequestTypes('SERVER', [...defaultTypes, 'SELECT', 'CHANGED', 'ADD']);
|
||||
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
|
||||
export const LOGOUT = 'LOGOUT'; // logout is always success
|
||||
|
||||
|
|
|
@ -20,3 +20,32 @@ export function createChannelFailure(err) {
|
|||
err
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function createChannelRequestUsers(data) {
|
||||
return {
|
||||
type: types.CREATE_CHANNEL.REQUEST_USERS,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
export function createChannelSetUsers(data) {
|
||||
return {
|
||||
type: types.CREATE_CHANNEL.SET_USERS,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
export function createChannelSuccessUsers(data) {
|
||||
return {
|
||||
type: types.CREATE_CHANNEL.SUCCESS_USERS,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
export function createChannelFailureUsers(err) {
|
||||
return {
|
||||
type: types.CREATE_CHANNEL.FAILURE_USERS,
|
||||
err
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,6 +6,12 @@ export function appReady() {
|
|||
type: APP.READY
|
||||
};
|
||||
}
|
||||
|
||||
export function appInit() {
|
||||
return {
|
||||
type: APP.INIT
|
||||
};
|
||||
}
|
||||
export function setCurrentServer(server) {
|
||||
return {
|
||||
type: types.SET_CURRENT_SERVER,
|
||||
|
|
|
@ -28,7 +28,7 @@ export function loginFailure(err) {
|
|||
};
|
||||
}
|
||||
|
||||
export function setToken(user) {
|
||||
export function setToken(user = {}) {
|
||||
return {
|
||||
type: types.LOGIN.SET_TOKEN,
|
||||
token: user.token,
|
||||
|
|
|
@ -6,6 +6,37 @@ export function setServer(server) {
|
|||
server
|
||||
};
|
||||
}
|
||||
export function serverRequest(server) {
|
||||
console.log(server);
|
||||
return {
|
||||
type: SERVER.REQUEST,
|
||||
server
|
||||
};
|
||||
}
|
||||
|
||||
export function addServer(server) {
|
||||
console.log(server);
|
||||
return {
|
||||
type: SERVER.ADD,
|
||||
server
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function serverSuccess() {
|
||||
return {
|
||||
type: SERVER.SUCCESS
|
||||
};
|
||||
}
|
||||
|
||||
export function serverFailure(err) {
|
||||
return {
|
||||
type: SERVER.FAILURE,
|
||||
err
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function changedServer(server) {
|
||||
return {
|
||||
type: SERVER.CHANGED,
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { CachedImage } from 'react-native-img-cache';
|
||||
import { emojify } from 'react-emojione';
|
||||
import Markdown from 'react-native-easy-markdown';
|
||||
import moment from 'moment';
|
||||
|
||||
import avatarInitialsAndColor from '../utils/avatarInitialsAndColor';
|
||||
import Avatar from './avatar';
|
||||
import Card from './message/card';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -20,26 +18,6 @@ const styles = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
transform: [{ scaleY: -1 }]
|
||||
},
|
||||
avatarContainer: {
|
||||
backgroundColor: '#eee',
|
||||
width: 40,
|
||||
height: 40,
|
||||
marginRight: 10,
|
||||
borderRadius: 5
|
||||
},
|
||||
avatar: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 5,
|
||||
position: 'absolute'
|
||||
},
|
||||
avatarInitials: {
|
||||
margin: 2,
|
||||
textAlign: 'center',
|
||||
lineHeight: 36,
|
||||
fontSize: 22,
|
||||
color: '#ffffff'
|
||||
},
|
||||
texts: {
|
||||
flex: 1
|
||||
},
|
||||
|
@ -87,33 +65,17 @@ export default class Message extends React.PureComponent {
|
|||
|
||||
const username = item.alias || item.u.username;
|
||||
|
||||
let { initials, color } = avatarInitialsAndColor(username);
|
||||
|
||||
const avatar = item.avatar || `${ this.props.baseUrl }/avatar/${ item.u.username }`;
|
||||
if (item.avatar) {
|
||||
initials = '';
|
||||
color = 'transparent';
|
||||
}
|
||||
|
||||
let aliasUsername;
|
||||
if (item.alias) {
|
||||
aliasUsername = <Text style={styles.alias}>@{item.u.username}</Text>;
|
||||
}
|
||||
|
||||
const time = moment(item.ts).format(this.props.Message_TimeFormat);
|
||||
|
||||
return (
|
||||
<View style={[styles.message, extraStyle]}>
|
||||
<View style={[styles.avatarContainer, { backgroundColor: color }]}>
|
||||
<Text style={styles.avatarInitials}>{initials}</Text>
|
||||
<CachedImage style={styles.avatar} source={{ uri: avatar }} />
|
||||
</View>
|
||||
<Avatar style={{ marginRight: 10 }} text={item.avatar ? '' : username} size={40} baseUrl={this.props.baseUrl} avatar={item.avatar} />
|
||||
<View style={[styles.content]}>
|
||||
<View style={styles.usernameView}>
|
||||
<Text onPress={this._onPress} style={styles.username}>
|
||||
{username}
|
||||
</Text>
|
||||
{aliasUsername}<Text style={styles.time}>{time}</Text>
|
||||
{item.alias && <Text style={styles.alias}>@{item.u.username}</Text>}<Text style={styles.time}>{time}</Text>
|
||||
</View>
|
||||
{this.attachments()}
|
||||
<Markdown>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import React from 'react';
|
||||
import { CachedImage } from 'react-native-img-cache';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
|
||||
import Avatar from './avatar';
|
||||
import avatarInitialsAndColor from '../utils/avatarInitialsAndColor';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -81,12 +80,8 @@ export default class RoomItem extends React.PureComponent {
|
|||
}
|
||||
|
||||
if (type === 'd') {
|
||||
const { initials, color } = avatarInitialsAndColor(name);
|
||||
return (
|
||||
<View style={[styles.iconContainer, { backgroundColor: color }]}>
|
||||
<Text style={styles.avatarInitials}>{initials}</Text>
|
||||
<CachedImage style={styles.avatar} source={{ uri: `${ baseUrl }/avatar/${ name }` }} />
|
||||
</View>
|
||||
<Avatar text={name} baseUrl={baseUrl} size={40} borderRadius={20} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,49 +4,48 @@ import { StyleSheet, Text, View } from 'react-native';
|
|||
import { CachedImage } from 'react-native-img-cache';
|
||||
import avatarInitialsAndColor from '../utils/avatarInitialsAndColor';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
iconContainer: {
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
avatar: {
|
||||
position: 'absolute'
|
||||
},
|
||||
avatarInitials: {
|
||||
color: '#ffffff'
|
||||
} });
|
||||
|
||||
const styles = StyleSheet.create({ iconContainer: {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
},
|
||||
icon: {
|
||||
fontSize: 20,
|
||||
color: '#fff'
|
||||
},
|
||||
avatar: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
position: 'absolute'
|
||||
},
|
||||
avatarInitials: {
|
||||
fontSize: 20,
|
||||
color: '#ffffff'
|
||||
} });
|
||||
class Avatar extends React.PureComponent {
|
||||
render() {
|
||||
const { text = '', size = 25, baseUrl = this.props.baseUrl,
|
||||
borderRadius = 5, style, avatar } = this.props;
|
||||
const { initials, color } = avatarInitialsAndColor(`${ text }`);
|
||||
return (
|
||||
<View style={[styles.iconContainer, {
|
||||
backgroundColor: color,
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius
|
||||
}, style]}
|
||||
>
|
||||
<Text style={[styles.avatarInitials, { fontSize: size / 2 }]}>{initials}</Text>
|
||||
{ (avatar || baseUrl) && <CachedImage
|
||||
style={[styles.avatar, { width: size,
|
||||
height: size }]}
|
||||
source={{ uri: avatar || `${ baseUrl }/avatar/${ text }` }}
|
||||
/>}
|
||||
</View>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const avatar = ({ text = '', width = 25, height = 25, fontSize = 12, baseUrl,
|
||||
borderRadius = 2, style }) => {
|
||||
const { initials, color } = avatarInitialsAndColor(`${ text }`);
|
||||
return (
|
||||
<View style={[styles.iconContainer, { backgroundColor: color }, { width,
|
||||
height,
|
||||
borderRadius }, style]}
|
||||
>
|
||||
<Text style={[styles.avatarInitials, { fontSize }]}>{initials}</Text>
|
||||
{ baseUrl ? <CachedImage style={styles.avatar} source={{ uri: `${ baseUrl }/avatar/${ name }` }} /> : null}
|
||||
</View>);
|
||||
};
|
||||
|
||||
avatar.propTypes = {
|
||||
Avatar.propTypes = {
|
||||
style: PropTypes.object,
|
||||
baseUrl: PropTypes.string,
|
||||
text: PropTypes.string.isRequired,
|
||||
width: PropTypes.number,
|
||||
fontSize: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
avatar: PropTypes.string,
|
||||
size: PropTypes.number,
|
||||
borderRadius: PropTypes.number
|
||||
};
|
||||
export default avatar;
|
||||
export default Avatar;
|
||||
|
|
|
@ -74,8 +74,8 @@ const styles = StyleSheet.create({
|
|||
|
||||
const renderTag = (item, index) => (<Item key={item} label={item} onPress={() => this.props.onPress(item, index)} />);
|
||||
|
||||
const renderItemResult = ({ item = '' }) => (
|
||||
<View style={styles.result_item}>
|
||||
const renderItemResult = ({ item = '', onPress }) => (
|
||||
<View style={styles.result_item} onPress={onPress}>
|
||||
<Avatar text={item} width={40} height={40} fontSize={20} style={{ marginRight: 5 }} />
|
||||
<Text>@{item}</Text>
|
||||
</View>);
|
||||
|
@ -102,11 +102,11 @@ export default class tags extends React.PureComponent {
|
|||
onChangeText={this.props.onChangeText}
|
||||
/>
|
||||
</View>
|
||||
{this.props.result ? <FlatList
|
||||
{this.props.result && this.props.result.length ? <FlatList
|
||||
keyExtractor={item => item}
|
||||
style={styles.result}
|
||||
data={this.props.result || []}
|
||||
renderItem={this.props.renderItemResult || renderItemResult}
|
||||
renderItem={e => (this.props.renderItemResult || renderItemResult)(e, this.props.onSelect)}
|
||||
/> : null}
|
||||
</View>);
|
||||
}
|
||||
|
|
35
app/index.js
35
app/index.js
|
@ -1,11 +1,16 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Text } from 'react-native';
|
||||
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 { Keyboard, Text, TextInput, View, Image, TouchableOpacity } from 'react-native';
|
||||
|
||||
import styles from './views/Styles';
|
||||
|
||||
import store from './lib/createStore';
|
||||
|
||||
//
|
||||
|
@ -22,16 +27,16 @@ export const authenticated = WrappedComponent => class _p extends React.PureComp
|
|||
constructor() {
|
||||
super();
|
||||
this.login = store.getState().login;
|
||||
console.log('this.login.token', this.login.token);
|
||||
if (!this.login.token || this.login.failure) {
|
||||
return store.getState().navigator.resetTo({
|
||||
screen: 'Login'
|
||||
screen: 'Login',
|
||||
animationType: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
// Wraps the input component in a container, without mutating it. Good!
|
||||
return <WrappedComponent {...this.props} />;
|
||||
return ((this.login.isAuthenticated || this.login.user) && <WrappedComponent {...this.props} />);
|
||||
}
|
||||
};
|
||||
//
|
||||
|
@ -43,7 +48,7 @@ export class PublicScreen extends React.PureComponent {
|
|||
// }
|
||||
// }
|
||||
render() {
|
||||
return !this.login.isAuthenticated || !this.login.user ? null : (<ListServerView {...this.props} />);
|
||||
return ((this.login.isAuthenticated || this.login.user) && <ListServerView {...this.props} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,21 +71,29 @@ export class PrivateScreen extends React.PureComponent {
|
|||
// logged: state.login.isAuthenticated
|
||||
}), dispatch => ({
|
||||
// navigate: routeName => dispatch(NavigationActions.navigate({ routeName })),
|
||||
setNavigator: navigator => dispatch(setNavigator(navigator))
|
||||
setNavigator: navigator => dispatch(setNavigator(navigator)),
|
||||
appInit: () => dispatch(appInit())
|
||||
}))
|
||||
export const HomeScreen = class extends React.PureComponent {
|
||||
static propTypes = {
|
||||
setNavigator: PropTypes.func.isRequired,
|
||||
navigator: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static navigatorStyle = {
|
||||
navBarHidden: true
|
||||
};
|
||||
componentWillMount() {
|
||||
this.props.setNavigator(this.props.navigator);
|
||||
this.props.navigator.resetTo({
|
||||
screen: 'public'
|
||||
});
|
||||
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 (<Text>oieee</Text>);
|
||||
return (<View style={styles.logoContainer}><Animatable.Text animation='pulse' easing='ease-out' iterationCount='infinite' style={{ textAlign: 'center' }}>
|
||||
<Image style={styles.logo} source={require('./images/logo.png')} />
|
||||
</Animatable.Text></View>);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,12 +23,8 @@ const call = (method, ...params) => new Promise((resolve, reject) => {
|
|||
|
||||
const RocketChat = {
|
||||
createChannel({ name, users, type }) {
|
||||
console.log('FISTALEU', { name, users, type });
|
||||
return new Promise((resolve, reject) => {
|
||||
Meteor.call(type ? 'createChannel' : 'createPrivateGroup', name, users, type, (err, res) => {
|
||||
console.log(err, res);
|
||||
return (err ? reject(err) : resolve(res));
|
||||
});
|
||||
Meteor.call(type ? 'createChannel' : 'createPrivateGroup', name, users, type, (err, res) => (err ? reject(err) : resolve(res)));
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -40,7 +36,15 @@ const RocketChat = {
|
|||
console.warn(`AsyncStorage error: ${ error.message }`);
|
||||
}
|
||||
},
|
||||
|
||||
async testServer(url) {
|
||||
if (/^(https?:\/\/)?(((\w|[0-9])+(\.(\w|[0-9-_])+)+)|localhost)(:\d+)?$/.test(url)) {
|
||||
const response = await fetch(url, { method: 'HEAD' });
|
||||
if (response.status === 200 && response.headers.get('x-instance-id') != null && response.headers.get('x-instance-id').length) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
throw new Error({ error: 'invalid server' });
|
||||
},
|
||||
connect(_url) {
|
||||
return new Promise((resolve) => {
|
||||
const url = `${ _url }/websocket`;
|
||||
|
@ -69,7 +73,7 @@ const RocketChat = {
|
|||
const setting = {
|
||||
_id: item._id
|
||||
};
|
||||
setting._server = { id: reduxStore.getState().server };
|
||||
setting._server = { id: reduxStore.getState().server.server };
|
||||
if (settingsType[item.type]) {
|
||||
setting[settingsType[item.type]] = item.value;
|
||||
realm.create('settings', setting, true);
|
||||
|
@ -87,7 +91,7 @@ const RocketChat = {
|
|||
realm.write(() => {
|
||||
const message = ddbMessage.fields.args[0];
|
||||
message.temp = false;
|
||||
message._server = { id: reduxStore.getState().server };
|
||||
message._server = { id: reduxStore.getState().server.server };
|
||||
// write('messages', message);
|
||||
realm.create('messages', message, true);
|
||||
});
|
||||
|
@ -97,7 +101,7 @@ const RocketChat = {
|
|||
// console.log(ddbMessage);
|
||||
realm.write(() => {
|
||||
const data = ddbMessage.fields.args[1];
|
||||
data._server = { id: reduxStore.getState().server };
|
||||
data._server = { id: reduxStore.getState().server.server };
|
||||
realm.create('subscriptions', data, true);
|
||||
});
|
||||
}
|
||||
|
@ -108,13 +112,11 @@ const RocketChat = {
|
|||
},
|
||||
|
||||
login(params, callback) {
|
||||
console.log('login(params, callback)');
|
||||
return new Promise((resolve, reject) => {
|
||||
Meteor._startLoggingIn();
|
||||
return Meteor.call('login', params, (err, result) => {
|
||||
Meteor._endLoggingIn();
|
||||
Meteor._handleLoginCallback(err, result);
|
||||
console.log('login(params, callback)asdas', err, result);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
@ -183,7 +185,7 @@ const RocketChat = {
|
|||
// if (typeof item.value === 'string') {
|
||||
// subscription.value = item.value;
|
||||
// }
|
||||
subscription._server = { id: reduxStore.getState().server };
|
||||
subscription._server = { id: reduxStore.getState().server.server };
|
||||
// write('subscriptions', subscription);
|
||||
realm.create('subscriptions', subscription, true);
|
||||
});
|
||||
|
@ -207,7 +209,7 @@ const RocketChat = {
|
|||
realm.write(() => {
|
||||
data.messages.forEach((message) => {
|
||||
message.temp = false;
|
||||
message._server = { id: reduxStore.getState().server };
|
||||
message._server = { id: reduxStore.getState().server.server };
|
||||
// write('messages', message);
|
||||
realm.create('messages', message, true);
|
||||
});
|
||||
|
@ -237,7 +239,7 @@ const RocketChat = {
|
|||
ts: new Date(),
|
||||
_updatedAt: new Date(),
|
||||
temp: true,
|
||||
_server: { id: reduxStore.getState().server },
|
||||
_server: { id: reduxStore.getState().server.server },
|
||||
u: {
|
||||
_id: reduxStore.getState().login.user.id || '1',
|
||||
username: reduxStore.getState().login.user.id
|
||||
|
@ -381,7 +383,6 @@ const RocketChat = {
|
|||
return subscription;
|
||||
});
|
||||
// Meteor.subscribe('stream-notify-user', `${ Meteor.userId() }/rooms-changed`, false);
|
||||
console.log('getRooms resolved', reduxStore.getState().server, data);
|
||||
realm.write(() => {
|
||||
data.forEach((subscription) => {
|
||||
// const subscription = {
|
||||
|
@ -390,7 +391,7 @@ const RocketChat = {
|
|||
// if (typeof item.value === 'string') {
|
||||
// subscription.value = item.value;
|
||||
// }
|
||||
subscription._server = { id: reduxStore.getState().server };
|
||||
subscription._server = { id: reduxStore.getState().server.server };
|
||||
// write('subscriptions', subscription);
|
||||
realm.create('subscriptions', subscription, true);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,36 @@
|
|||
import { SERVER } from '../actions/actionsTypes';
|
||||
|
||||
export default function server(state = '', action) {
|
||||
const initialState = {
|
||||
connecting: false,
|
||||
connected: false,
|
||||
errorMessage: '',
|
||||
failure: false,
|
||||
server: ''
|
||||
};
|
||||
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case SERVER.REQUEST:
|
||||
return { ...state,
|
||||
connecting: true,
|
||||
failure: false
|
||||
};
|
||||
case SERVER.SUCCESS:
|
||||
return { ...state,
|
||||
connecting: false,
|
||||
connected: true,
|
||||
failure: false
|
||||
};
|
||||
case SERVER.FAILURE:
|
||||
return { ...state,
|
||||
connecting: false,
|
||||
connected: false,
|
||||
failure: true,
|
||||
errorMessage: action.err
|
||||
};
|
||||
case SERVER.SELECT:
|
||||
return action.server;
|
||||
return { ...state, server: action.server };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { take, put, call, fork, takeLatest, select } from 'redux-saga/effects';
|
||||
import { put, call, takeLatest, select } from 'redux-saga/effects';
|
||||
import { METEOR } from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
import { connectSuccess, connectFailure } from '../actions/connect';
|
||||
|
||||
const getServer = ({ server }) => server;
|
||||
const getServer = ({ server }) => server.server;
|
||||
|
||||
|
||||
const connect = url => RocketChat.connect(url);
|
||||
|
@ -17,14 +17,11 @@ const test = function* test() {
|
|||
yield put(connectFailure(err.status));
|
||||
}
|
||||
};
|
||||
const watchConnect = function* watchConnect() {
|
||||
yield takeLatest(METEOR.REQUEST, test);
|
||||
while (true) {
|
||||
yield take(METEOR.DISCONNECT);
|
||||
}
|
||||
};
|
||||
// const watchConnect = function* watchConnect() {
|
||||
// };
|
||||
const root = function* root() {
|
||||
yield fork(watchConnect);
|
||||
yield takeLatest(METEOR.REQUEST, test);
|
||||
// yield fork(watchConnect);
|
||||
// yield fork(auto);
|
||||
};
|
||||
export default root;
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
import { call, put } from 'redux-saga/effects';
|
||||
import { call, put, select, take } from 'redux-saga/effects';
|
||||
import * as actions from '../actions';
|
||||
import { setServer } from '../actions/server';
|
||||
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)); }
|
||||
if (currentServer) {
|
||||
yield put(setServer(currentServer));
|
||||
} else {
|
||||
navigator.resetTo({
|
||||
screen: 'ListServer'
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
import { take, put, call, takeEvery, fork, select, all, race } from 'redux-saga/effects';
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import { loginRequest, loginSuccess, loginFailure, setToken } from '../actions/login';
|
||||
import { loginRequest, loginSuccess, loginFailure, setToken, logout } from '../actions/login';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
||||
const getUser = state => state.login;
|
||||
const getServer = state => state.server;
|
||||
const getServer = state => state.server.server;
|
||||
const loginCall = args => (args.resume ? RocketChat.login(args) : RocketChat.loginWithPassword(args));
|
||||
|
||||
const getToken = function* getToken() {
|
||||
|
@ -20,6 +20,8 @@ const getToken = function* getToken() {
|
|||
} catch (e) {
|
||||
console.log('getTokenerr', e);
|
||||
}
|
||||
} else {
|
||||
yield put(setToken());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,10 +29,9 @@ const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() {
|
|||
// do {
|
||||
try {
|
||||
yield take(types.METEOR.SUCCESS);
|
||||
yield call(getToken);
|
||||
const { navigator } = yield select(state => state);
|
||||
navigator.resetTo({
|
||||
screen: 'Rooms'
|
||||
});
|
||||
|
||||
const user = yield select(getUser);
|
||||
if (user.token) {
|
||||
yield put(loginRequest({ resume: user.token }));
|
||||
|
@ -47,6 +48,9 @@ const handleLoginWhenServerChanges = function* handleLoginWhenServerChanges() {
|
|||
// });
|
||||
// }
|
||||
}
|
||||
navigator.resetTo({
|
||||
screen: 'Rooms'
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
@ -66,8 +70,11 @@ const handleLoginRequest = function* handleLoginRequest() {
|
|||
const response = yield call(loginCall, credentials);
|
||||
yield put(loginSuccess(response));
|
||||
} catch (err) {
|
||||
// console.log('login failed');
|
||||
yield put(loginFailure(err));
|
||||
if (err.error === 403) {
|
||||
yield put(logout());
|
||||
} else {
|
||||
yield put(loginFailure(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -93,7 +100,6 @@ const handleLoginSubmit = function* handleLoginSubmit() {
|
|||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeEvery(types.SERVER.CHANGED, getToken);
|
||||
yield takeEvery(types.SERVER.CHANGED, handleLoginWhenServerChanges);
|
||||
yield fork(handleLoginRequest);
|
||||
yield takeEvery(types.LOGIN.SUCCESS, saveToken);
|
||||
|
|
|
@ -7,7 +7,6 @@ import { changedServer } from '../actions/server';
|
|||
const selectServer = function* selectServer({ server }) {
|
||||
yield put(disconnect());
|
||||
yield put(changedServer(server));
|
||||
yield console.log('SERVER->', server);
|
||||
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
|
||||
yield put(connectRequest(server));
|
||||
};
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { AVATAR_COLORS } from '../constants/colors';
|
||||
|
||||
export default function(username = '') {
|
||||
if (username === '') {
|
||||
return {
|
||||
initials: '',
|
||||
colors: 'transparent'
|
||||
};
|
||||
}
|
||||
const position = username.length % AVATAR_COLORS.length;
|
||||
|
||||
const color = AVATAR_COLORS[position];
|
||||
|
|
|
@ -119,7 +119,7 @@ class LoginView extends React.Component {
|
|||
function mapStateToProps(state) {
|
||||
// console.log(Object.keys(state));
|
||||
return {
|
||||
server: state.server,
|
||||
server: state.server.server,
|
||||
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
|
||||
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
|
||||
login: state.login
|
||||
|
|
|
@ -46,7 +46,7 @@ const styles = StyleSheet.create({
|
|||
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server,
|
||||
server: state.server.server,
|
||||
Site_Url: state.settings.Site_Url,
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
loading: state.messages.isFetching
|
||||
|
|
|
@ -56,10 +56,10 @@ const styles = StyleSheet.create({
|
|||
|
||||
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
|
||||
@connect(state => ({
|
||||
server: state.server,
|
||||
server: state.server.server,
|
||||
login: state.login,
|
||||
Site_Url: state.settings.Site_Url,
|
||||
canShowList: state.login.token.length || state.login.user.token
|
||||
canShowList: state.login.token || state.login.user.token
|
||||
}), dispatch => ({
|
||||
login: () => dispatch(actions.login()),
|
||||
connect: () => dispatch(server.connectRequest())
|
||||
|
|
|
@ -65,7 +65,7 @@ const zeroconf = new Zeroconf();
|
|||
|
||||
|
||||
@connect(state => ({
|
||||
server: state.server
|
||||
server: state.server.server
|
||||
}), dispatch => ({
|
||||
selectServer: server => dispatch(setServer(server))
|
||||
}))
|
||||
|
|
|
@ -2,9 +2,8 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { Navigation } from 'react-native-navigation';
|
||||
import { Text, TextInput, View, StyleSheet } from 'react-native';
|
||||
import _ from 'underscore';
|
||||
import realm from '../lib/realm';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { serverRequest, addServer } from '../actions/server';
|
||||
import KeyboardView from '../components/KeyboardView';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -47,10 +46,17 @@ const styles = StyleSheet.create({
|
|||
flexGrow: 1
|
||||
}
|
||||
});
|
||||
|
||||
@connect(state => ({
|
||||
validInstance: !state.server.failure,
|
||||
validating: state.server.connecting
|
||||
}), dispatch => ({
|
||||
validateServer: url => dispatch(serverRequest(url)),
|
||||
addServer: url => dispatch(addServer(url))
|
||||
}))
|
||||
export default class NewServerView extends React.Component {
|
||||
static propTypes = {
|
||||
navigator: PropTypes.object.isRequired
|
||||
navigator: PropTypes.object.isRequired,
|
||||
validateServer: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
static navigationOptions = () => ({
|
||||
|
@ -66,37 +72,11 @@ export default class NewServerView extends React.Component {
|
|||
};
|
||||
|
||||
this.submit = () => {
|
||||
let url = this.state.text.trim();
|
||||
if (!url) {
|
||||
url = this.state.defaultServer;
|
||||
} else {
|
||||
url = this.completeUrl(this.state.text);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
editable: false
|
||||
});
|
||||
|
||||
this.inputElement.blur();
|
||||
this.validateServer(url).then(() => {
|
||||
realm.write(() => {
|
||||
realm.create('servers', { id: url, current: false }, true);
|
||||
});
|
||||
Navigation.dismissModal({
|
||||
animationType: 'slide-down'
|
||||
});
|
||||
}).catch(() => {
|
||||
this.setState({
|
||||
editable: true
|
||||
});
|
||||
this.inputElement.focus();
|
||||
});
|
||||
this.props.addServer(this.completeUrl(this.state.text.trim() || this.state.defaultServer));
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._mounted = true;
|
||||
|
||||
this.props.navigator.setTitle({
|
||||
title: 'New server'
|
||||
});
|
||||
|
@ -111,11 +91,6 @@ export default class NewServerView extends React.Component {
|
|||
|
||||
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._mounted = false;
|
||||
}
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
if (event.id === 'close') {
|
||||
|
@ -128,62 +103,8 @@ export default class NewServerView extends React.Component {
|
|||
|
||||
onChangeText = (text) => {
|
||||
this.setState({ text });
|
||||
|
||||
this.validateServerDebounced(text);
|
||||
this.props.validateServer(this.completeUrl(text));
|
||||
}
|
||||
|
||||
validateServer = url => new Promise((resolve, reject) => {
|
||||
url = this.completeUrl(url);
|
||||
|
||||
this.setState({
|
||||
validating: false,
|
||||
url
|
||||
});
|
||||
|
||||
if (/^(https?:\/\/)?(((\w|[0-9])+(\.(\w|[0-9-_])+)+)|localhost)(:\d+)?$/.test(url)) {
|
||||
this.setState({
|
||||
validating: true
|
||||
});
|
||||
|
||||
fetch(url, { method: 'HEAD' })
|
||||
.then((response) => {
|
||||
if (!this._mounted) {
|
||||
return;
|
||||
}
|
||||
if (response.status === 200 && response.headers.get('x-instance-id') != null && response.headers.get('x-instance-id').length) {
|
||||
this.setState({
|
||||
validInstance: true,
|
||||
validating: false
|
||||
});
|
||||
resolve(url);
|
||||
} else {
|
||||
this.setState({
|
||||
validInstance: false,
|
||||
validating: false
|
||||
});
|
||||
reject(url);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!this._mounted) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
validInstance: false,
|
||||
validating: false
|
||||
});
|
||||
reject(url);
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
validInstance: undefined
|
||||
});
|
||||
reject(url);
|
||||
}
|
||||
})
|
||||
|
||||
validateServerDebounced = _.debounce(this.validateServer, 1000)
|
||||
|
||||
completeUrl = (url) => {
|
||||
url = url.trim();
|
||||
|
||||
|
@ -203,7 +124,10 @@ export default class NewServerView extends React.Component {
|
|||
}
|
||||
|
||||
renderValidation = () => {
|
||||
if (this.state.validating) {
|
||||
if (!this.state.text.trim()) {
|
||||
return null;
|
||||
}
|
||||
if (this.props.validating) {
|
||||
return (
|
||||
<Text style={[styles.validateText, styles.validatingText]}>
|
||||
Validating {this.state.url} ...
|
||||
|
@ -211,7 +135,7 @@ export default class NewServerView extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.state.validInstance) {
|
||||
if (this.props.validInstance) {
|
||||
return (
|
||||
<Text style={[styles.validateText, styles.validText]}>
|
||||
{this.state.url} is a valid Rocket.Chat instance
|
||||
|
@ -219,7 +143,7 @@ export default class NewServerView extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (this.state.validInstance === false) {
|
||||
if (!this.state.validInstance) {
|
||||
return (
|
||||
<Text style={[styles.validateText, styles.invalidText]}>
|
||||
{this.state.url} is not a valid Rocket.Chat instance
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"react-emojione": "^3.1.10",
|
||||
"react-native": "0.46.1",
|
||||
"react-native-action-button": "^2.7.2",
|
||||
"react-native-animatable": "^1.2.3",
|
||||
"react-native-card-view": "0.0.3",
|
||||
"react-native-easy-markdown": "git+https://github.com/lappalj4/react-native-easy-markdown.git",
|
||||
"react-native-fetch-blob": "^0.10.8",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import { ScrollView } from 'react-native';
|
||||
|
||||
import Avatar from '../../app/components/avatar';
|
||||
|
||||
export default (
|
||||
<ScrollView>
|
||||
<Avatar text={'test'} />
|
||||
<Avatar size={40} text={'aa'} />
|
||||
<Avatar size={30} text={'bb'} />
|
||||
<Avatar text={'test'} borderRadius={2} />
|
||||
</ScrollView>
|
||||
);
|
|
@ -8,6 +8,9 @@ import { storiesOf } from '@storybook/react-native';
|
|||
|
||||
import DirectMessage from './Channels/DirectMessage';
|
||||
import TagInput from './TagInput';
|
||||
import Avatar from './Avatar';
|
||||
|
||||
storiesOf('Avatar', module).add('avatar', () => Avatar);
|
||||
|
||||
storiesOf('Tag Input', module).add('Tag Input', () => TagInput);
|
||||
storiesOf('Channel Cell', module).add('Direct Messages', () => DirectMessage);
|
||||
|
|
Loading…
Reference in New Issue