diff --git a/.eslintrc b/.eslintrc index b701a4b0..cadce991 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,6 @@ { "parser": "babel-eslint", + "extends": "airbnb", "parserOptions": { "sourceType": "module", "ecmaVersion": 2017, @@ -10,6 +11,8 @@ }, "plugins": [ "react", + "jsx-a11y", + "import", "react-native" ], "env": { @@ -20,6 +23,23 @@ "jquery": true }, "rules": { + "react/jsx-filename-extension": [1, { + "extensions": [".js", ".jsx"] + }], + "react/require-default-props": [0], + "react/no-unused-prop-types": [2, { + "skipShapeProps": true + }], + "react/no-multi-comp": [0], + "react/jsx-indent": [2, "tab"], + "react/jsx-indent-props": [2, "tab"], + "react/forbid-prop-types": 0, + "jsx-quotes": [2, "prefer-single"], + "jsx-a11y/href-no-hash": 0, + "no-underscore-dangle": 0, + "no-return-assign": 0, + "no-param-reassign": 0, + "no-tabs": 0, "no-multi-spaces": 2, "no-eval": 2, "no-extend-native": 2, @@ -82,9 +102,7 @@ "block-scoped-var": 2, "curly": [2, "all"], "eqeqeq": [2, "allow-null"], - "new-cap": [2, { - "capIsNewExceptions": ["Match.Optional", "Match.Maybe", "Match.ObjectIncluding", "Push.Configure", "SHA256"] - }], + "new-cap": [2], "use-isnan": 2, "valid-typeof": 2, "linebreak-style": [2, "unix"], @@ -95,73 +113,5 @@ "prefer-const": 2, "object-shorthand": 2 }, - "globals": { - "_" : false, - "__meteor_runtime_config__" : false, - "AccountBox" : false, - "Accounts" : false, - "AgentUsers" : false, - "Assets" : false, - "Blaze" : false, - "BlazeLayout" : false, - "browser" : false, - "ChatMessage" : false, - "ChatMessages" : false, - "ChatRoom" : false, - "ChatSubscription" : false, - "check" : false, - "CryptoJS" : false, - "Department" : false, - "DDPRateLimiter" : false, - "EJSON" : false, - "Email" : false, - "FlowRouter" : false, - "FileUpload" : false, - "HTTP" : false, - "getNextAgent" : false, - "handleError" : false, - "getAvatarUrlFromUsername" : false, - "LivechatCustomField" : false, - "LivechatDepartment" : false, - "LivechatDepartmentAgents" : false, - "livechatManagerRoutes" : true, - "LivechatPageVisited" : false, - "LivechatTrigger" : false, - "Logger" : false, - "Match" : false, - "Meteor" : false, - "moment" : false, - "Mongo" : false, - "Npm" : false, - "Package" : false, - "parentCall" : false, - "Promise" : false, - "Random" : false, - "ReactiveVar" : false, - "RocketChat" : true, - "RocketChatFile" : false, - "RoomHistoryManager" : false, - "RoomManager" : false, - "s" : false, - "ServiceConfiguration" : false, - "Session" : false, - "Settings" : false, - "SHA256" : false, - "SideNav" : false, - "swal" : false, - "t" : false, - "TAPi18n" : false, - "TAPi18next" : false, - "Template" : false, - "TimeSync" : false, - "toastr" : false, - "Tracker" : false, - "Trigger" : false, - "Triggers" : false, - "UAParser" : false, - "visitor" : false, - "WebApp" : false, - "VideoRecorder" : false, - "VRecDialog" : false - } + "globals": {} } diff --git a/app/login.js b/app/login.js index 5d5387ba..5b6c2694 100644 --- a/app/login.js +++ b/app/login.js @@ -1,7 +1,8 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { View, TextInput, StyleSheet } from 'react-native'; import realm from './realm'; -import {loginWithPassword} from './meteor'; +import { loginWithPassword } from './meteor'; const styles = StyleSheet.create({ @@ -24,7 +25,11 @@ const styles = StyleSheet.create({ } }); -export class LoginView extends React.Component { +export default class LoginView extends React.Component { + static propTypes = { + navigation: PropTypes.object.isRequired + } + static navigationOptions = () => ({ title: realm.objectForPrimaryKey('settings', 'Site_Name').value }); @@ -40,10 +45,10 @@ export class LoginView extends React.Component { const { navigate } = this.props.navigation; this.submit = () => { - loginWithPassword({username: this.state.username}, this.state.password, function() { - console.log(arguments); + loginWithPassword({ username: this.state.username }, this.state.password, () => { navigate('Rooms'); }); + // let url = this.state.text.trim(); // if (!url) { // url = defaultServer; @@ -65,30 +70,30 @@ export class LoginView extends React.Component { this.setState({username})} + onChangeText={username => this.setState({ username })} keyboardType='email-address' autoCorrect={false} returnKeyType='done' autoCapitalize='none' - autoFocus={true} + autoFocus onSubmitEditing={this.submit} - placeholder='Email or username' /> + placeholder='Email or username' + /> this.setState({password})} - secureTextEntry={true} + onChangeText={password => this.setState({ password })} + secureTextEntry autoCorrect={false} returnKeyType='done' autoCapitalize='none' onSubmitEditing={this.submit} - placeholder='Password' /> + placeholder='Password' + /> ); } } - - // export class LoginView extends React.Component { // renderRow(setting) { // return ( diff --git a/app/meteor.js b/app/meteor.js index 46b0c405..803cc6fa 100644 --- a/app/meteor.js +++ b/app/meteor.js @@ -1,5 +1,5 @@ -import realm from './realm'; import Meteor from 'react-native-meteor'; +import realm from './realm'; export function connect(cb) { const currentServer = realm.objects('servers').filtered('current = true')[0]; @@ -9,16 +9,16 @@ export function connect(cb) { Meteor.connect(url); - Meteor.ddp.on('connected', function() { + Meteor.ddp.on('connected', () => { console.log('connected'); - Meteor.call('public-settings/get', function(err, data) { + Meteor.call('public-settings/get', (err, data) => { if (err) { console.error(err); } realm.write(() => { - data.forEach(item => { + data.forEach((item) => { const setting = { _id: item._id }; @@ -35,14 +35,14 @@ export function connect(cb) { } export function loginWithPassword(selector, password, cb) { - Meteor.loginWithPassword(selector, password, function() { - Meteor.call('subscriptions/get', function(err, data) { + Meteor.loginWithPassword(selector, password, () => { + Meteor.call('subscriptions/get', (err, data) => { if (err) { console.error(err); } realm.write(() => { - data.forEach(subscription => { + data.forEach((subscription) => { // const subscription = { // _id: item._id // }; @@ -58,14 +58,14 @@ export function loginWithPassword(selector, password, cb) { } export function loadMessagesForRoom(rid) { - Meteor.call('loadHistory', rid, null, 50, function(err, data) { + Meteor.call('loadHistory', rid, null, 50, (err, data) => { if (err) { console.error(err); } console.log(data); realm.write(() => { - data.messages.forEach(message => { + data.messages.forEach((message) => { realm.create('messages', message, true); }); }); diff --git a/app/navigation.js b/app/navigation.js index 73eb71ea..b5788e4b 100644 --- a/app/navigation.js +++ b/app/navigation.js @@ -1,8 +1,8 @@ import { StackNavigator } from 'react-navigation'; -import { LoginView } from './login'; -import { NewServerView } from './new-server'; -import { RoomsView } from './rooms'; -import { RoomView } from './room'; +import LoginView from './login'; +import NewServerView from './new-server'; +import RoomsView from './rooms'; +import RoomView from './room'; export default new StackNavigator({ diff --git a/app/new-server.js b/app/new-server.js index 60d17f9b..28c74c4a 100644 --- a/app/new-server.js +++ b/app/new-server.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { View, TextInput, StyleSheet } from 'react-native'; import realm from './realm'; import { connect } from './meteor'; @@ -23,7 +24,11 @@ const styles = StyleSheet.create({ const defaultServer = 'http://localhost:3000'; -export class NewServerView extends React.Component { +export default class NewServerView extends React.Component { + static propTypes = { + navigation: PropTypes.object.isRequired + } + constructor(props) { super(props); this.state = { @@ -41,8 +46,8 @@ export class NewServerView extends React.Component { // TODO: validate URL realm.write(() => { - realm.objects('servers').filtered('current = true').forEach(item => item.current = false); - realm.create('servers', {id: url, current: true}, true); + realm.objects('servers').filtered('current = true').forEach(item => (item.current = false)); + realm.create('servers', { id: url, current: true }, true); }); connect(() => { @@ -57,14 +62,15 @@ export class NewServerView extends React.Component { this.setState({text})} + onChangeText={text => this.setState({ text })} keyboardType='url' autoCorrect={false} returnKeyType='done' autoCapitalize='none' - autoFocus={true} + autoFocus onSubmitEditing={this.submit} - placeholder={defaultServer} /> + placeholder={defaultServer} + /> ); } diff --git a/app/realm.js b/app/realm.js index de927cf9..6e0624fb 100644 --- a/app/realm.js +++ b/app/realm.js @@ -14,7 +14,7 @@ const settingsSchema = { primaryKey: '_id', properties: { _id: 'string', - value: {type: 'string', optional: true} + value: { type: 'string', optional: true } } }; @@ -27,7 +27,7 @@ const subscriptionSchema = { ts: 'date', ls: 'date', name: 'string', - fname: {type: 'string', optional: true}, + fname: { type: 'string', optional: true }, rid: 'string', // u: { _id: 'hKCY2XGzHYk89SAaM', username: 'rodrigo', name: null }, open: 'bool', @@ -46,16 +46,16 @@ const usersSchema = { properties: { _id: 'string', username: 'string', - name: {type: 'string', optional: true} + name: { type: 'string', optional: true } } -} +}; const messagesSchema = { name: 'messages', primaryKey: '_id', properties: { _id: 'string', - msg: {type: 'string', optional: true}, + msg: { type: 'string', optional: true }, rid: 'string', ts: 'date', u: 'users', diff --git a/app/room.js b/app/room.js index b99bfabd..d4ee5972 100644 --- a/app/room.js +++ b/app/room.js @@ -1,8 +1,9 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { View, Text, FlatList, StyleSheet, Image } from 'react-native'; +// import Markdown from 'react-native-simple-markdown'; import realm from './realm'; import { loadMessagesForRoom } from './meteor'; -import Markdown from 'react-native-simple-markdown'; const styles = StyleSheet.create({ @@ -34,46 +35,38 @@ const styles = StyleSheet.create({ }); class RoomItem extends React.PureComponent { - _onPress = () => { - this.props.onPressItem(this.props.id); - }; + static propTypes = { + item: PropTypes.object.isRequired + } render() { return ( - - + {this.props.item.u.username} - + {this.props.item.msg} - + + {/* + {this.props.item.msg} + */} ); } } -export class RoomView extends React.Component { - static navigationOptions = ({ navigation }) => ({ - title: realm.objectForPrimaryKey('subscriptions', navigation.state.params.sid).name - // title: navigation.state.params.rid - }); - - _onPressItem(id) { - console.log('pressed', id); +export default class RoomView extends React.Component { + static propTypes = { + navigation: PropTypes.object.isRequired } - renderItem = ({item}) => ( - - ); + static navigationOptions = ({ navigation }) => ({ + title: realm.objectForPrimaryKey('subscriptions', navigation.state.params.sid).name + }); constructor(props) { super(props); @@ -82,24 +75,26 @@ export class RoomView extends React.Component { loadMessagesForRoom(this.rid); - const getState = () => { - return { - selected: new Map(), - dataSource: realm.objects('messages').filtered('rid = $0', this.rid) - }; - }; + const getState = () => ({ + selected: new Map(), + dataSource: realm.objects('messages').filtered('rid = $0', this.rid) + }); realm.addListener('change', () => this.setState(getState())); this.state = getState(); } - renderSeparator = () => { - return ( - - ); - }; + renderItem = ({ item }) => ( + + ); + renderSeparator = () => ( + + ); render() { return ( diff --git a/app/rooms.js b/app/rooms.js index dd81b337..ae941308 100644 --- a/app/rooms.js +++ b/app/rooms.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { View, Text, FlatList, StyleSheet } from 'react-native'; import realm from './realm'; @@ -22,54 +23,58 @@ const styles = StyleSheet.create({ }); 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 ( - {this.props.title} + { this.props.title } ); } } -export class RoomsView extends React.Component { - _onPressItem = (id) => { - const { navigate } = this.props.navigation; - console.log('pressed', id); - navigate('Room', {sid: id}); +export default class RoomsView extends React.Component { + static propTypes = { + navigation: PropTypes.object.isRequired } - renderItem = ({item}) => ( - - ); - constructor(props) { super(props); - const getState = () => { - return { - selected: new Map(), - dataSource: realm.objects('subscriptions') - }; - }; + const getState = () => ({ + selected: new Map(), + dataSource: realm.objects('subscriptions') + }); realm.addListener('change', () => this.setState(getState())); this.state = getState(); } - renderSeparator = () => { - return ( - - ); - }; + _onPressItem = (id) => { + const { navigate } = this.props.navigation; + console.log('pressed', id); + navigate('Room', { sid: id }); + } + renderItem = ({ item }) => ( + + ); + + renderSeparator = () => ( + + ); render() { return ( diff --git a/package.json b/package.json index 7c8a3703..9698fc95 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,16 @@ { - "name": "RocketChatRN", + "name": "rocket-chat-rn", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest" + "test": "jest", + "lint": "eslint .", + "ios": "react-native run-ios", + "android": "react-native run-android" }, "dependencies": { + "prop-types": "^15.5.10", "react": "16.0.0-alpha.12", "react-native": "0.47.1", "react-native-meteor": "^1.1.0", @@ -16,6 +20,10 @@ "devDependencies": { "babel-jest": "20.0.3", "babel-preset-react-native": "2.1.0", + "eslint": "^4.4.0", + "eslint-config-airbnb": "^15.1.0", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-react": "^7.1.0", "jest": "20.0.4", "react-test-renderer": "16.0.0-alpha.12"