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'
+ }]
+ }
+};