diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..1ac595da --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +/node_modules/ +/web/output/ diff --git a/.gitignore b/.gitignore index 9b25b931..a6ff951f 100755 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,10 @@ project.xcworkspace .gradle local.properties +# Web +# +/web/output/ + # node.js # node_modules/ diff --git a/README.md b/README.md index d0f9dd73..9f4ddeb4 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,13 @@ For local development you need to follow the below commands: ``` git clone https://github.com/soliury/noder-react-native.git npm install +./post_npm_install.sh ``` Click the run button in Xcode, if something went wrong, you need to rebuild all packages that be used in this project with Xcode (Just select the package and press **command+B** to run compile). -If you want to run it on you iPhone, please follow the [Offical Doc](http://facebook.github.io/react-native/docs/runningondevice.html#content). +If you want to run it on your iPhone, please follow the [Offical Doc](http://facebook.github.io/react-native/docs/runningondevice.html#content). If you don't want to update the ip manually, please run: @@ -38,6 +39,11 @@ npm start The ip will be replaced automatically. +If you want to run it on your Browser (localhost:3000), please run: +``` +react-web start +``` + ## Screenshots diff --git a/index.web.js b/index.web.js new file mode 100644 index 00000000..c163b595 --- /dev/null +++ b/index.web.js @@ -0,0 +1,13 @@ +import 'babel-polyfill'; +import {AppRegistry} from 'react-native'; +import Noder from './src'; + + +AppRegistry.registerComponent('noder', () => Noder); + +var app = document.createElement('div'); +document.body.appendChild(app); + +AppRegistry.runApplication('noder', { + rootTag: app +}); diff --git a/package.json b/package.json index 01605a61..86a86feb 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "start": "node node_modules/react-native/local-cli/cli.js start", "android": "node node_modules/react-native/local-cli/cli.js run-android", "ios": "node node_modules/react-native/local-cli/cli.js run-ios", + "web": "node node_modules/react-web/local-cli/cli.js start web/webpack.config.js", "log": "adb logcat *:S ReactNative:V ReactNativeJS:V", + "build-web": "node node_modules/react-web/local-cli/cli.js bundle web/webpack.config.js", "build-ios": "react-native unbundle --entry-file index.ios.js --platform ios --dev false", "build-android": "cd android && ./gradlew assembleRelease && open app/build/outputs/apk && cd ..", "checkversion": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", @@ -24,6 +26,7 @@ "query-string": "^4.2.2", "react": "15.1.0", "react-addons-pure-render-mixin": "15.1.0", + "react-dom": "15.1.0", "react-native": "^0.28.0", "react-native-barcodescanner": "^3.0.0", "react-native-blur": "^1.0.0", @@ -35,6 +38,7 @@ "react-native-scrollable-tab-view": "^0.5.1", "react-native-vector-icons": "^2.0.3", "react-redux": "^4.4.5", + "react-web": "git+https://github.com/flyskywhy/react-web.git", "redux": "^3.5.2", "redux-actions": "^0.10.0", "redux-logger": "^2.6.1", @@ -43,16 +47,29 @@ }, "devDependencies": { "babel-eslint": "^6.1.0", + "babel-loader": "^6.2.4", + "babel-polyfill": "^6.13.0", + "babel-preset-es2015": "^6.6.0", + "babel-preset-react": "^6.5.0", + "babel-preset-stage-1": "^6.5.0", "coffee-script": "^1.9.2", "dev-ip": "^1.0.1", "eslint": "^2.13.1", "eslint-plugin-react": "^5.2.2", + "file-loader": "^0.9.0", "gulp": "^3.9.1", "gulp-replace": "^0.5.4", "gulp-util": "^3.0.4", + "haste-resolver-webpack-plugin": "^0.2.1", + "json-loader": "^0.5.4", + "react-hot-loader": "^1.3.0", "react-native-cli": "^0.2.0", "redux-devtools": "^3.3.1", - "run-sequence": "^1.2.1" + "run-sequence": "^1.2.1", + "url-loader": "^0.5.7", + "webpack": "^1.13.1", + "webpack-dev-server": "^1.14.1", + "webpack-html-plugin": "^0.1.1" }, "devEngines": { "node": ">= 4.x", diff --git a/post_npm_install.sh b/post_npm_install.sh new file mode 100755 index 00000000..45a94fd8 --- /dev/null +++ b/post_npm_install.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# patch some node_modules to build and run well + +if [ ! -d node_modules ]; then + npm install +fi + +# react-web can't recognize *.android.js and *.ios.js now, so be it +cp post_npm_install/react-native-blur/src/* node_modules/react-native-blur/src/ +cp post_npm_install/react-native-scrollable-tab-view/Button.js node_modules/react-native-scrollable-tab-view/ + +# looks like a BUG +sed -i "s/export default parseHtml = function (html, done) {/export default function (html, done) {/" node_modules/react-native-html-render/lib/htmlParse.js +sed -i "s/ uri = node.attribs.href;/ let uri = node.attribs.href;/" node_modules/react-native-html-render/lib/htmlRender.js diff --git a/post_npm_install/react-native-blur/src/BlurView.js b/post_npm_install/react-native-blur/src/BlurView.js new file mode 100644 index 00000000..2d2c0b0a --- /dev/null +++ b/post_npm_install/react-native-blur/src/BlurView.js @@ -0,0 +1,3 @@ +import {Platform} from 'react-native'; + +module.exports = Platform.OS === 'web' ? require('./BlurView.ios') : require('./BlurView.' + Platform.OS); diff --git a/post_npm_install/react-native-blur/src/VibrancyView.js b/post_npm_install/react-native-blur/src/VibrancyView.js new file mode 100644 index 00000000..2046710b --- /dev/null +++ b/post_npm_install/react-native-blur/src/VibrancyView.js @@ -0,0 +1,3 @@ +import {Platform} from 'react-native'; + +module.exports = Platform.OS === 'web' ? require('./VibrancyView.ios') : require('./VibrancyView.' + Platform.OS); diff --git a/post_npm_install/react-native-scrollable-tab-view/Button.js b/post_npm_install/react-native-scrollable-tab-view/Button.js new file mode 100644 index 00000000..71ec0031 --- /dev/null +++ b/post_npm_install/react-native-scrollable-tab-view/Button.js @@ -0,0 +1,3 @@ +import {Platform} from 'react-native'; + +module.exports = Platform.OS === 'web' ? require('./Button.ios') : require('./Button.' + Platform.OS); diff --git a/src/components/base/Modal.js b/src/components/base/Modal.js index 0f177cc4..431f09f2 100644 --- a/src/components/base/Modal.js +++ b/src/components/base/Modal.js @@ -1,6 +1,8 @@ import React, {Component, PropTypes} from 'react'; import {Dimensions, View, StyleSheet, Animated, Easing, Platform, TouchableWithoutFeedback, findNodeHandle} from 'react-native'; -import {BlurView} from 'react-native-blur'; +if (Platform.OS !== 'web') { + var BlurView = require('react-native-blur').BlurView; +} const {height, width} = Dimensions.get('window'); diff --git a/src/layouts/Login.js b/src/layouts/Login.js index d4543ec7..2203cc40 100644 --- a/src/layouts/Login.js +++ b/src/layouts/Login.js @@ -1,5 +1,6 @@ import React, {Component, PropTypes} from 'react'; import { + Platform, View, Text, TouchableHighlight, @@ -10,7 +11,11 @@ import { TouchableOpacity } from 'react-native'; import Icon from 'react-native-vector-icons/Ionicons'; -import Camera from 'react-native-camera'; + +if (Platform.OS !== 'web') { + var Camera = require('react-native-camera'); +} + import Spinner from '../components/base/Spinner'; import packageObj from '../../package.json'; @@ -21,18 +26,19 @@ class Login extends Component { _onLoginPress() { const {ui, router, actions} = this.props; if (ui.checkTokenPending) return; - Camera.checkDeviceAuthorizationStatus() - .then((isAuth)=> { - if (isAuth) { - router.toQRCode(); - } - else { - actions.toast('请在设置中开启Noder对相机的访问'); - } - }) - .catch((err)=> { - actions.toast('获取相机访问权错误'); - }); + if (Platform.OS !== 'web') { + Camera.checkDeviceAuthorizationStatus() + .then((isAuth) => { + if (isAuth) { + router.toQRCode(); + } else { + actions.toast('请在设置中开启Noder对相机的访问'); + } + }) + .catch((err) => { + actions.toast('获取相机访问权错误'); + }); + } } diff --git a/src/layouts/QRCode.js b/src/layouts/QRCode.js index 2c73d093..1c8fb165 100644 --- a/src/layouts/QRCode.js +++ b/src/layouts/QRCode.js @@ -1,7 +1,11 @@ import React, {Component} from 'react'; import {StyleSheet, View, Text, Dimensions, Platform, TouchableOpacity, Vibration} from 'react-native'; -import Camera from 'react-native-camera'; -import BarcodeScanner from 'react-native-barcodescanner'; + +if (Platform.OS !== 'web') { + var Camera = require('react-native-camera'); + var BarcodeScanner = require('react-native-barcodescanner'); +} + import Icon from 'react-native-vector-icons/Ionicons'; import OverlayButton from '../components/base/OverlayButton'; @@ -54,6 +58,17 @@ class QRCode extends Component { ); + // for web + if (Platform.OS === 'web') { + return ( + + + 只有原生 APP 才支持二维码 + + + ); + } + // for android if (Platform.OS === 'android') { return ( diff --git a/src/layouts/Utils.js b/src/layouts/Utils.js index c17d556d..9ecdd502 100644 --- a/src/layouts/Utils.js +++ b/src/layouts/Utils.js @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import {View, StyleSheet, Text, StatusBar, AppState} from 'react-native'; +import {Platform, View, StyleSheet, Text, StatusBar, AppState} from 'react-native'; import Toast from '../components/base/Toast'; import * as codePushUtils from '../utils/codePushSync'; @@ -12,13 +12,15 @@ class Utils extends Component { actions.getUnreadMessageCount(); } }); - codePushUtils.sync(); - AppState.addEventListener("change", (newState) => { - if (newState === "active") { - codePushUtils.sync(); - this.props.user.secret && actions.getUnreadMessageCount(); - } - }); + if (Platform.OS !== 'web') { + codePushUtils.sync(); + AppState.addEventListener("change", (newState) => { + if (newState === "active") { + codePushUtils.sync(); + this.props.user.secret && actions.getUnreadMessageCount(); + } + }); + } // if (__DEV__) { // actions.checkToken('your secretKey', ()=> { @@ -36,12 +38,20 @@ class Utils extends Component { render() { - return ( - - - this.toast=view }/> - - ); + if (Platform.OS === 'web') { + return ( + + this.toast=view }/> + + ); + } else { + return ( + + + this.toast=view }/> + + ); + } } } diff --git a/src/store/configureStore.js b/src/store/configureStore.js index ba31c34a..28826eaf 100644 --- a/src/store/configureStore.js +++ b/src/store/configureStore.js @@ -34,7 +34,7 @@ if (isDebuggingInChrome) { export default function configureStore(initialState) { const store = applyMiddleware( ...middlewares - )(createStore)(reducers, initialState); + )(createStore)(reducers, initialState, window.devToolsExtension && window.devToolsExtension()); if (module.hot) { module.hot.accept(() => { diff --git a/web/webpack.config.js b/web/webpack.config.js new file mode 100644 index 00000000..9dfeb7ed --- /dev/null +++ b/web/webpack.config.js @@ -0,0 +1,85 @@ +'use strict'; + +var path = require('path'); +var webpack = require('webpack'); +var HtmlPlugin = require('webpack-html-plugin'); +var HasteResolverPlugin = require('haste-resolver-webpack-plugin'); + +var IP = '0.0.0.0'; +var PORT = 3000; +var NODE_ENV = process.env.NODE_ENV; +var ROOT_PATH = path.resolve(__dirname, '..'); +var PROD = 'production'; +var DEV = 'development'; +let isProd = NODE_ENV === 'production'; + +var config = { + paths: { + src: path.join(ROOT_PATH, '.'), + index: path.join(ROOT_PATH, 'index.web'), + }, +}; + +module.exports = { + ip: IP, + port: PORT, + devtool: 'cheap-module-eval-source-map', + resolve: { + alias: { + 'react-native': 'ReactWeb', + }, + extensions: ['', '.js', '.jsx'], + }, + entry: isProd? [ + 'babel-polyfill', + config.paths.index + ]: [ + 'babel-polyfill', + 'webpack-dev-server/client?http://' + IP + ':' + PORT, + 'webpack/hot/only-dev-server', + config.paths.index, + ], + output: { + path: path.join(__dirname, 'output'), + filename: 'bundle.js' + }, + plugins: [ + new HasteResolverPlugin({ + platform: 'web', + nodeModules: ['react-web'] + }), + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify(isProd? PROD: DEV), + }, + '__DEV__': !isProd + }), + isProd? new webpack.ProvidePlugin({ + React: "react" + }): new webpack.HotModuleReplacementPlugin(), + new webpack.NoErrorsPlugin(), + new HtmlPlugin(), + ], + module: { + loaders: [{ + test: /\.json$/, + loader: 'json', + }, { + test: /\.jsx?$/, + loader: 'react-hot', + include: [config.paths.src], + exclude: [/node_modules/] + }, { + test: /\.jsx?$/, + loader: 'babel', + query: { + presets: ['es2015', 'react', 'stage-1'] + }, + include: [config.paths.src], + exclude: [/(node_modules\/(?!react))/, path.join(ROOT_PATH, 'post_npm_install')] + }, { + test : /\.(png|gif|svg|jpg)$/, + loader : 'url-loader?limit=8192' + }] + } +};