Improve code organization a bit

This commit is contained in:
Diego Sampaio 2017-08-08 22:40:55 -03:00
parent 783e564c10
commit 2eee28797a
No known key found for this signature in database
GPG Key ID: E060152B30502562
14 changed files with 156 additions and 143 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
__tests__

43
app/components/Message.js Normal file
View File

@ -0,0 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text, StyleSheet, Image } from 'react-native';
const styles = StyleSheet.create({
message: {
borderColor: '#aaa',
padding: 14,
flexDirection: 'row',
transform: [{ scaleY: -1 }]
}
});
export default class Message extends React.PureComponent {
static propTypes = {
item: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired
}
render() {
const extraStyle = {};
if (this.props.item.temp) {
extraStyle.opacity = 0.3;
}
return (
<View style={[styles.message, extraStyle]}>
<Image style={styles.avatar} source={{ uri: `${ this.props.baseUrl }/avatar/${ this.props.item.u.username }` }} />
<View style={styles.texts}>
<Text onPress={this._onPress} style={styles.username}>
{this.props.item.u.username}
</Text>
<Text style={styles.msg}>
{this.props.item.msg}
</Text>
{/* <Markdown whitelist={['link', 'url']}>
{this.props.item.msg}
</Markdown> */}
</View>
</View>
);
}
}

View File

@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Text, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
roomItem: {
lineHeight: 18,
borderTopWidth: 2,
borderColor: '#aaa',
padding: 14
}
});
export default class RoomItem extends React.PureComponent {
static propTypes = {
onPressItem: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
id: PropTypes.string.isRequired
}
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
return (
<Text onPress={this._onPress} style={styles.roomItem}>{ this.props.title }</Text>
);
}
}

View File

@ -33,25 +33,23 @@ export function connect(cb) {
cb(); cb();
}); });
Meteor.ddp.on("changed", ddbMessage => { Meteor.ddp.on('changed', (ddbMessage) => {
console.log('changed', ddbMessage); console.log('changed', ddbMessage);
if (ddbMessage.collection === 'stream-room-messages') { if (ddbMessage.collection === 'stream-room-messages') {
setTimeout(function() { setTimeout(() => {
realm.write(() => { realm.write(() => {
const message = ddbMessage.fields.args[0]; const message = ddbMessage.fields.args[0];
message.temp = false; message.temp = false;
realm.create('messages', message, true); realm.create('messages', message, true);
}); });
}, 1000) }, 1000);
} }
}); });
}); });
} }
export function loginWithPassword(selector, password, cb) { export function loginWithPassword(selector, password, cb) {
Meteor.loginWithPassword(selector, password, (err, data) => { Meteor.loginWithPassword(selector, password, () => cb && cb());
cb && cb();
});
} }
export function loadSubscriptions(cb) { export function loadSubscriptions(cb) {
@ -72,7 +70,7 @@ export function loadSubscriptions(cb) {
}); });
}); });
cb && cb(); return cb && cb();
}); });
} }
@ -102,8 +100,8 @@ export function sendMessage(rid, msg, cb) {
_id, _id,
rid, rid,
msg, msg,
ts: new Date, ts: new Date(),
_updatedAt: new Date, _updatedAt: new Date(),
temp: true, temp: true,
u: { u: {
_id: user._id, _id: user._id,
@ -112,7 +110,5 @@ export function sendMessage(rid, msg, cb) {
}, true); }, true);
}); });
Meteor.call('sendMessage', {_id, rid, msg}, (err, data) => { Meteor.call('sendMessage', { _id, rid, msg }, () => cb && cb());
cb && cb();
});
} }

View File

@ -1,9 +1,9 @@
import { StackNavigator } from 'react-navigation'; import { StackNavigator } from 'react-navigation';
import LoginView from './login'; import LoginView from './views/login';
import NewServerView from './servers/new'; import NewServerView from './views/serverNew';
import ListServerView from './servers/list'; import ListServerView from './views/serverList';
import RoomsView from './rooms'; import RoomsListView from './views/roomsList';
import RoomView from './room'; import RoomView from './views/room';
const navigationOptions = { const navigationOptions = {
// headerStyle: { // headerStyle: {
@ -24,7 +24,7 @@ export default new StackNavigator({
navigationOptions navigationOptions
}, },
Login: { screen: LoginView }, Login: { screen: LoginView },
Rooms: { screen: RoomsView }, Rooms: { screen: RoomsListView },
Room: { Room: {
screen: RoomView screen: RoomView
// navigationOptions: { // navigationOptions: {

View File

@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, TextInput, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native'; import { TextInput, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native';
import realm from './realm'; import realm from '../lib/realm';
import { loginWithPassword, loadSubscriptions, Accounts } from './meteor'; import { loginWithPassword, loadSubscriptions, Accounts } from '../lib/meteor';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
view: { view: {

View File

@ -1,18 +1,13 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, KeyboardAvoidingView, Text, TextInput, FlatList, StyleSheet, Image, Platform } from 'react-native'; import { View, KeyboardAvoidingView, TextInput, FlatList, StyleSheet, Platform } from 'react-native';
// import Markdown from 'react-native-simple-markdown'; // import Markdown from 'react-native-simple-markdown';
import realm from './realm'; import realm from '../lib/realm';
import { loadMessagesForRoom, sendMessage } from './meteor'; import { loadMessagesForRoom, sendMessage } from '../lib/meteor';
import Message from '../components/Message';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
roomItem: {
borderColor: '#aaa',
padding: 14,
flexDirection: 'row',
transform: [{ scaleY: -1 }]
},
avatar: { avatar: {
backgroundColor: '#ccc', backgroundColor: '#ccc',
width: 40, width: 40,
@ -54,36 +49,6 @@ const styles = StyleSheet.create({
} }
}); });
class RoomItem extends React.PureComponent {
static propTypes = {
item: PropTypes.object.isRequired
}
render() {
const extraStyle = {};
if (this.props.item.temp) {
extraStyle.opacity = .3;
}
return (
<View style={[styles.roomItem, extraStyle]}>
<Image style={styles.avatar} source={{ uri: `${ this.props.baseUrl }/avatar/${ this.props.item.u.username }` }} />
<View style={styles.texts}>
<Text onPress={this._onPress} style={styles.username}>
{this.props.item.u.username}
</Text>
<Text style={styles.msg}>
{this.props.item.msg}
</Text>
{/* <Markdown whitelist={['link', 'url']}>
{this.props.item.msg}
</Markdown> */}
</View>
</View>
);
}
}
export default class RoomView extends React.Component { export default class RoomView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired navigation: PropTypes.object.isRequired
@ -106,16 +71,6 @@ export default class RoomView extends React.Component {
this.url = realm.objectForPrimaryKey('settings', 'Site_Url').value; this.url = realm.objectForPrimaryKey('settings', 'Site_Url').value;
} }
getMessages = () => {
return realm.objects('messages').filtered('rid = $0', this.rid).sorted('ts', true);
}
updateState = () => {
this.setState({
dataSource: this.getMessages()
});
};
componentWillMount() { componentWillMount() {
loadMessagesForRoom(this.rid); loadMessagesForRoom(this.rid);
realm.addListener('change', this.updateState); realm.addListener('change', this.updateState);
@ -125,17 +80,13 @@ export default class RoomView extends React.Component {
realm.removeListener('change', this.updateState); realm.removeListener('change', this.updateState);
} }
renderItem = ({ item }) => ( getMessages = () => realm.objects('messages').filtered('rid = $0', this.rid).sorted('ts', true)
<RoomItem
id={item._id}
item={item}
baseUrl={this.url}
/>
);
renderSeparator = () => ( updateState = () => {
<View style={styles.separator} /> this.setState({
); dataSource: this.getMessages()
});
};
submit = () => { submit = () => {
console.log(this.state.text); console.log(this.state.text);
@ -149,7 +100,19 @@ export default class RoomView extends React.Component {
...this.state, ...this.state,
text: '' text: ''
}); });
} };
renderSeparator = () => (
<View style={styles.separator} />
);
renderItem = ({ item }) => (
<Message
id={item._id}
item={item}
baseUrl={this.url}
/>
);
render() { render() {
return ( return (
@ -172,7 +135,7 @@ export default class RoomView extends React.Component {
onSubmitEditing={this.submit} onSubmitEditing={this.submit}
autoFocus autoFocus
placeholder='New message' placeholder='New message'
></TextInput> />
</View> </View>
</KeyboardAvoidingView> </KeyboardAvoidingView>
); );

View File

@ -1,16 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, Text, FlatList, StyleSheet } from 'react-native'; import { View, FlatList, StyleSheet } from 'react-native';
import realm from './realm'; import realm from '../lib/realm';
import RoomItem from '../components/RoomItem';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
roomItem: {
lineHeight: 18,
borderTopWidth: 2,
borderColor: '#aaa',
padding: 14
},
container: { container: {
flex: 1 flex: 1
}, },
@ -22,25 +17,7 @@ const styles = StyleSheet.create({
} }
}); });
class RoomItem extends React.PureComponent { export default class RoomsListView extends React.Component {
static propTypes = {
onPressItem: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
id: PropTypes.string.isRequired
}
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
return (
<Text onPress={this._onPress} style={styles.roomItem}>{ this.props.title }</Text>
);
}
}
export default class RoomsView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired navigation: PropTypes.object.isRequired
} }
@ -53,10 +30,6 @@ export default class RoomsView extends React.Component {
}; };
} }
componentWillMount() {
realm.addListener('change', () => this.setState(getState()));
}
_onPressItem = (id) => { _onPressItem = (id) => {
const { navigate } = this.props.navigation; const { navigate } = this.props.navigation;
console.log('pressed', id); console.log('pressed', id);

View File

@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Zeroconf from 'react-native-zeroconf'; import Zeroconf from 'react-native-zeroconf';
import { H1, View, TouchableOpacity, Text, TextInput, SectionList, Button, StyleSheet } from 'react-native'; import { View, Text, SectionList, Button, StyleSheet } from 'react-native';
import realm from '../realm'; import realm from '../lib/realm';
import { connect } from '../meteor'; import { connect } from '../lib/meteor';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
view: { view: {
@ -57,7 +57,7 @@ export default class ListServerView extends React.Component {
title: 'Servers', title: 'Servers',
headerRight: ( headerRight: (
<Button <Button
title = "Add" title='Add'
onPress={() => navigation.navigate('NewServer')} onPress={() => navigation.navigate('NewServer')}
/> />
) )
@ -78,16 +78,17 @@ export default class ListServerView extends React.Component {
}]; }];
if (this.state.nearBy) { if (this.state.nearBy) {
const nearBy = Object.keys(this.state.nearBy).filter(key => this.state.nearBy[key].addresses); const nearBy = Object.keys(this.state.nearBy)
.filter(key => this.state.nearBy[key].addresses);
if (nearBy.length) { if (nearBy.length) {
sections.push({ sections.push({
title: 'Nearby', title: 'Nearby',
data: nearBy.map((key) => { data: nearBy.map((key) => {
const server = this.state.nearBy[key]; const server = this.state.nearBy[key];
const address = `http://${ server.addresses[0] }:${ server.port }` const address = `http://${ server.addresses[0] }:${ server.port }`;
return { return {
id: address id: address
} };
}) })
}); });
} }
@ -108,7 +109,7 @@ export default class ListServerView extends React.Component {
const currentServer = realm.objects('servers').filtered('current = true')[0]; const currentServer = realm.objects('servers').filtered('current = true')[0];
if (currentServer) { if (currentServer) {
connect(() => { connect(() => {
navigation.navigate('Login') navigation.navigate('Login');
}); });
} }
@ -121,12 +122,14 @@ export default class ListServerView extends React.Component {
realm.addListener('change', () => this.setState(getState())); realm.addListener('change', () => this.setState(getState()));
this.state = getState(); this.state = getState();
return null;
} }
onPressItem(item) { onPressItem(item) {
const { navigate } = this.props.navigation; const { navigate } = this.props.navigation;
realm.write(() => { realm.write(() => {
realm.objects('servers').filtered('current = true').forEach(item => (item.current = false)); realm.objects('servers').filtered('current = true').forEach(server => (server.current = false));
realm.create('servers', { id: item.id, current: true }, true); realm.create('servers', { id: item.id, current: true }, true);
}); });
@ -138,7 +141,7 @@ export default class ListServerView extends React.Component {
renderItem = ({ item }) => ( renderItem = ({ item }) => (
<Text <Text
style={styles.listItem} style={styles.listItem}
onPress={() => {this.onPressItem(item)}} onPress={() => { this.onPressItem(item); }}
> >
{item.id} {item.id}
</Text> </Text>
@ -160,11 +163,10 @@ export default class ListServerView extends React.Component {
sections={this.state.sections} sections={this.state.sections}
renderItem={this.renderItem} renderItem={this.renderItem}
renderSectionHeader={this.renderSectionHeader} renderSectionHeader={this.renderSectionHeader}
keyExtractor={(item) => item.id} keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={this.renderSeparator}
/> />
</View> </View>
); );
} }
} }

View File

@ -1,11 +1,9 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Zeroconf from 'react-native-zeroconf'; import { TextInput, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native';
import { View, Text, TextInput, Button, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native';
import { NavigationActions } from 'react-navigation'
import realm from '../realm'; import realm from '../lib/realm';
import { connect } from '../meteor'; import { connect } from '../lib/meteor';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
view: { view: {
@ -28,14 +26,12 @@ const styles = StyleSheet.create({
} }
}); });
const zeroconf = new Zeroconf();
export default class NewServerView extends React.Component { export default class NewServerView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired navigation: PropTypes.object.isRequired
} }
static navigationOptions = ({navigation}) => ({ static navigationOptions = () => ({
title: 'New Server Connection' title: 'New Server Connection'
}); });

10
package-lock.json generated
View File

@ -1488,6 +1488,11 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
"integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=" "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg="
}, },
"events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
},
"exec-sh": { "exec-sh": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz",
@ -4192,6 +4197,11 @@
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-0.0.65.tgz", "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-0.0.65.tgz",
"integrity": "sha1-toXqMIH/fJZIbNmXNhAmxAcwLFk=" "integrity": "sha1-toXqMIH/fJZIbNmXNhAmxAcwLFk="
}, },
"react-native-zeroconf": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/react-native-zeroconf/-/react-native-zeroconf-0.7.1.tgz",
"integrity": "sha1-zb+/JVCqcc89r4LYJnWI7+44uwE="
},
"react-navigation": { "react-navigation": {
"version": "1.0.0-beta.11", "version": "1.0.0-beta.11",
"resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-1.0.0-beta.11.tgz", "resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-1.0.0-beta.11.tgz",