├── .watchmanconfig
├── .gitattributes
├── .babelrc
├── app.json
├── res
└── images
│ ├── close.png
│ ├── ic_over.png
│ ├── ic_Subscribe.png
│ ├── ic_unSubscribe.png
│ ├── ic_Subscribe@2x.png
│ ├── ic_Subscribe@3x.png
│ ├── ic_unSubscribe@2x.png
│ ├── ic_unSubscribe@3x.png
│ └── ic_arrow_back_white_36pt.png
├── screenshot
└── thegif.gif
├── android
├── app
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── assets
│ │ │ │ └── fonts
│ │ │ │ │ ├── Entypo.ttf
│ │ │ │ │ ├── Zocial.ttf
│ │ │ │ │ ├── EvilIcons.ttf
│ │ │ │ │ ├── Ionicons.ttf
│ │ │ │ │ ├── Octicons.ttf
│ │ │ │ │ ├── FontAwesome.ttf
│ │ │ │ │ ├── Foundation.ttf
│ │ │ │ │ ├── MaterialIcons.ttf
│ │ │ │ │ ├── SimpleLineIcons.ttf
│ │ │ │ │ └── MaterialCommunityIcons.ttf
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ └── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── assets
│ │ │ └── fonts
│ │ │ │ ├── Entypo.ttf
│ │ │ │ ├── Zocial.ttf
│ │ │ │ ├── Ionicons.ttf
│ │ │ │ ├── Octicons.ttf
│ │ │ │ ├── EvilIcons.ttf
│ │ │ │ ├── FontAwesome.ttf
│ │ │ │ ├── Foundation.ttf
│ │ │ │ ├── MaterialIcons.ttf
│ │ │ │ ├── SimpleLineIcons.ttf
│ │ │ │ └── MaterialCommunityIcons.ttf
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── ishuhui
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainApplication.java
│ │ │ └── AndroidManifest.xml
│ ├── BUCK
│ ├── proguard-rules.pro
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── keystores
│ ├── debug.keystore.properties
│ └── BUCK
├── settings.gradle
├── build.gradle
├── gradle.properties
├── gradlew.bat
└── gradlew
├── js
├── page
│ ├── ShuHui.js
│ └── SearchContainer.js
├── store
│ └── store.js
├── Setup.js
├── reducers
│ ├── rootReducer.js
│ ├── searchReducer.js
│ ├── authReducer.js
│ ├── chapterReducer.js
│ ├── shuhuiComicReducer.js
│ ├── passionComicReducer.js
│ ├── domesticComicReducer.js
│ └── subscribeReducer.js
├── common
│ ├── localStorage.js
│ ├── api.js
│ └── actionTypes.js
├── widget
│ ├── LoadingMoreFooter.js
│ ├── ComicCell.js
│ ├── SearchHeader.js
│ ├── TabBar.js
│ ├── NavigationBar.js
│ └── SubscribeCell.js
├── utils
│ ├── ViewUtils.js
│ └── HttpUtils.js
├── containers
│ ├── mApp.js
│ ├── app.js
│ ├── Hot.js
│ ├── Setting.js
│ ├── Detail
│ │ └── Detail.js
│ ├── Hot
│ │ ├── PassionComic.js
│ │ ├── ShuHuiComic.js
│ │ └── DomesticComic.js
│ ├── Login.js
│ ├── Subscribe.js
│ ├── Search.js
│ └── Chapter
│ │ └── Chapter.js
├── actions
│ ├── passionComicAction.js
│ ├── domesticComicAction.js
│ ├── shuhuiComicAction.js
│ ├── chapterAction.js
│ ├── searchAction.js
│ ├── subscribeAction.js
│ └── loginAction.js
├── Welcome.js
└── Home.js
├── .buckconfig
├── index.ios.js
├── index.android.js
├── __tests__
├── index.ios.js
└── index.android.js
├── ios
├── IShuHui
│ ├── AppDelegate.h
│ ├── main.m
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.m
│ ├── Info.plist
│ └── Base.lproj
│ │ └── LaunchScreen.xib
├── IShuHuiTests
│ ├── Info.plist
│ └── IShuHuiTests.m
├── IShuHui-tvOSTests
│ └── Info.plist
├── IShuHui-tvOS
│ └── Info.plist
└── IShuHui.xcodeproj
│ └── xcshareddata
│ └── xcschemes
│ ├── IShuHui.xcscheme
│ └── IShuHui-tvOS.xcscheme
├── package.json
├── .gitignore
├── README.md
└── .flowconfig
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react-native"]
3 | }
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IShuHui",
3 | "displayName": "IShuHui"
4 | }
--------------------------------------------------------------------------------
/res/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/close.png
--------------------------------------------------------------------------------
/screenshot/thegif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/screenshot/thegif.gif
--------------------------------------------------------------------------------
/res/images/ic_over.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_over.png
--------------------------------------------------------------------------------
/res/images/ic_Subscribe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_Subscribe.png
--------------------------------------------------------------------------------
/res/images/ic_unSubscribe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_unSubscribe.png
--------------------------------------------------------------------------------
/res/images/ic_Subscribe@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_Subscribe@2x.png
--------------------------------------------------------------------------------
/res/images/ic_Subscribe@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_Subscribe@3x.png
--------------------------------------------------------------------------------
/res/images/ic_unSubscribe@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_unSubscribe@2x.png
--------------------------------------------------------------------------------
/res/images/ic_unSubscribe@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_unSubscribe@3x.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | IShuHui
3 |
4 |
--------------------------------------------------------------------------------
/res/images/ic_arrow_back_white_36pt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/res/images/ic_arrow_back_white_36pt.png
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/js/page/ShuHui.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sample React Native App
3 | * https://github.com/facebook/react-native
4 | * @flow
5 | */
6 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/keystores/debug.keystore.properties:
--------------------------------------------------------------------------------
1 | key.store=debug.keystore
2 | key.alias=androiddebugkey
3 | key.store.password=android
4 | key.alias.password=android
5 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | import {
2 | AppRegistry,
3 | } from 'react-native'
4 | import setup from './js/setup';
5 |
6 | AppRegistry.registerComponent('IShuHui', () => setup);
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/assets/fonts/MaterialCommunityIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loopcf/IShuHui/HEAD/android/app/src/main/res/assets/fonts/MaterialCommunityIcons.ttf
--------------------------------------------------------------------------------
/android/keystores/BUCK:
--------------------------------------------------------------------------------
1 | keystore(
2 | name = "debug",
3 | properties = "debug.keystore.properties",
4 | store = "debug.keystore",
5 | visibility = [
6 | "PUBLIC",
7 | ],
8 | )
9 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | import {
2 | AppRegistry
3 | } from 'react-native'
4 | import Setup from './js/Setup';
5 | import React from 'react';
6 |
7 | AppRegistry.registerComponent('IShuHui', () => Setup);
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
6 |
--------------------------------------------------------------------------------
/__tests__/index.ios.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import Index from '../index.ios.js';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | const tree = renderer.create(
10 |
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/__tests__/index.android.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import Index from '../index.android.js';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | const tree = renderer.create(
10 |
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/js/store/store.js:
--------------------------------------------------------------------------------
1 |
2 | import thunk from 'redux-thunk';
3 | import { createStore, applyMiddleware, compose } from 'redux';
4 | import rootReducer from './../reducers/rootReducer';
5 |
6 |
7 | let store = createStore(rootReducer, {}, compose(
8 | applyMiddleware(thunk),
9 | window.devToolsExtension ? window.devToolsExtension() : f => f
10 | ))
11 |
12 | export default store;
13 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'IShuHui'
2 | include ':react-native-cookie'
3 | project(':react-native-cookie').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-cookie/android')
4 | include ':react-native-vector-icons'
5 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
6 |
7 | include ':app'
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/ishuhui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ishuhui;
2 |
3 | import com.facebook.react.ReactActivity;
4 |
5 | public class MainActivity extends ReactActivity {
6 |
7 | /**
8 | * Returns the name of the main component registered from JavaScript.
9 | * This is used to schedule rendering of the component.
10 | */
11 | @Override
12 | protected String getMainComponentName() {
13 | return "IShuHui";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/js/Setup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import store from './store/store';
4 | import { AsyncStorage } from 'react-native';
5 | import App from './containers/app';
6 |
7 |
8 |
9 | export default class Setup extends React.Component {
10 |
11 | render() {
12 |
13 | return (
14 |
15 |
16 |
17 | )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ios/IShuHui/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (nonatomic, strong) UIWindow *window;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/ios/IShuHui/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #import "AppDelegate.h"
13 |
14 | int main(int argc, char * argv[]) {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/js/page/SearchContainer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Jonson
3 | */
4 | import React from 'react';
5 | import {connect} from 'react-redux';
6 | // import Search from '../pages/Search';
7 | // import Search from '../Home/SearchHeader';
8 | import Search from '../containers/Search';
9 |
10 |
11 |
12 |
13 | class SearchContainer extends React.Component {
14 | render() {
15 | return (
16 |
17 | )
18 | }
19 | }
20 |
21 | module.exports = connect((state) => {
22 | const {Search} = state;
23 | return {
24 | Search
25 | }
26 | })(SearchContainer);
27 |
--------------------------------------------------------------------------------
/js/reducers/rootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import shuhuiComicReducer from './shuhuiComicReducer';
4 | import passionComicReducer from './passionComicReducer';
5 | import domesticComicReducer from './domesticComicReducer';
6 | import chapterReducer from './chapterReducer';
7 | import authReducer from './authReducer';
8 | import searchReducer from './searchReducer';
9 | import subscribeReducer from './subscribeReducer';
10 | export default rootReducer = combineReducers({
11 | shuhuiComicReducer,
12 | passionComicReducer,
13 | domesticComicReducer,
14 | chapterReducer,
15 | authReducer,
16 | searchReducer,
17 | subscribeReducer
18 | })
19 |
--------------------------------------------------------------------------------
/js/common/localStorage.js:
--------------------------------------------------------------------------------
1 | import { AsyncStorage } from 'react-native';
2 |
3 | let localStorage = {
4 | setObject: (key, value) => {
5 | const jsonValue = JSON.stringify(value);
6 | return AsyncStorage.setItem(key, jsonValue, (error) => {
7 |
8 | });
9 | },
10 |
11 | cachedObject: (key) => {
12 | return AsyncStorage.getItem(key)
13 | .then((data, error) => {
14 | if (data) return JSON.parse(data);
15 | return null;
16 | })
17 | },
18 |
19 | clearCachedObject: (key) => {
20 | return AsyncStorage.removeItem(key);
21 | },
22 | }
23 |
24 | export default localStorage;
25 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | mavenLocal()
18 | jcenter()
19 | maven {
20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
21 | url "$rootDir/../node_modules/react-native/android"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ios/IShuHui/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/js/reducers/searchReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 |
3 | const initialState = {
4 | searchText: null,
5 | recomdData:[],
6 | }
7 |
8 | let searchReducer = (state = initialState, action={})=> {
9 |
10 | switch (action.type) {
11 | case types.SETUP_SEARCH_TEXT:
12 | return Object.assign({}, state, {
13 | searchText: action.searchText,
14 | })
15 | case types.SELECT_RECOMMEND_TAG:
16 | return Object.assign({}, state, {
17 | recomdData: action.recomdData
18 | })
19 | case types.RESET_SEARCH_STATE:
20 | return initialState
21 | default:
22 | return state;
23 | }
24 | }
25 |
26 | export default searchReducer;
27 |
--------------------------------------------------------------------------------
/js/reducers/authReducer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as TYPES from '../common/actionTypes';
4 |
5 | const initialState = {
6 | isLoggedIn: false,
7 | status: null,
8 | };
9 |
10 | export default function authReducer(state=initialState, action){
11 |
12 | switch(action.type){
13 | case TYPES.LOGGED_DOING:
14 | return {
15 | ...state,
16 | status: 'doing'
17 | };
18 |
19 | case TYPES.LOGGED_IN:
20 | return {
21 | ...state,
22 | isLoggedIn: true,
23 | status: 'done'
24 | };
25 |
26 | case TYPES.LOGGED_OUT:
27 | return {
28 | ...state,
29 | isLoggedIn: false,
30 | status: null
31 | };
32 | case TYPES.LOGGED_ERROR:
33 | return {
34 | ...state,
35 | isLoggedIn: false,
36 | status: null
37 | }
38 |
39 | default:
40 | return state;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/ios/IShuHuiTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/IShuHui-tvOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/js/widget/LoadingMoreFooter.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import {
4 | View,
5 | Text,
6 | ActivityIndicator,
7 | StyleSheet
8 | } from 'react-native';
9 |
10 |
11 | export default class LoadingMoreFooter extends Component {
12 |
13 | render() {
14 | return (
15 |
16 |
17 |
18 | 正在加载中...
19 |
20 |
21 | )
22 | }
23 | }
24 | const styles = StyleSheet.create({
25 |
26 | container: {
27 | height: 40,
28 | flexDirection: 'row',
29 | justifyContent: 'center',
30 | alignItems: 'center'
31 | },
32 |
33 | text: {
34 | fontSize: 14,
35 | color: '#CCCCCC',
36 | marginLeft: 10
37 | }
38 | })
39 |
--------------------------------------------------------------------------------
/js/utils/ViewUtils.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import {
3 | View,
4 | StyleSheet,
5 | Text,
6 | Image,
7 | TouchableOpacity,
8 | } from 'react-native';
9 | export default class ViewUtils{
10 | static getLeftButtion(callBack){
11 | return
14 |
18 |
19 |
20 | }
21 | static getRightButton(title, callBack) {
22 | return
25 |
26 | {title}
27 |
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | android.useDeprecatedNdk=true
21 |
--------------------------------------------------------------------------------
/js/reducers/chapterReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from './../common/actionTypes';
2 |
3 | const initState = {
4 | ChapterList: [],
5 | isLoading: true,
6 | isLoadMore: false,
7 | isRefresh: false,
8 | }
9 |
10 | let chapterReducer = (state = initState, action) => {
11 | switch (action.type) {
12 | case types.LOAD_CHAPTER_LIST:
13 | return Object.assign({}, state, {
14 | isLoading: action.isLoading,
15 | isLoadMore: action.isLoadMore,
16 | isRefresh: action.isRefresh
17 | });
18 | case types.GET_CHAPTER_LIST:
19 | return Object.assign({}, state, {
20 | isLoading: false,
21 | isRefresh: false,
22 | ChapterList: state.isLoadMore ? state.ChapterList.concat(action.ChapterList) : action.ChapterList
23 | });
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | export default chapterReducer;
30 |
--------------------------------------------------------------------------------
/js/reducers/shuhuiComicReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from './../common/actionTypes';
2 |
3 | const initState = {
4 | ShuhuiList: [],
5 | isLoading: true,
6 | isLoadMore: false,
7 | isRefresh: false,
8 | }
9 |
10 | let shuhuiComicReducer = (state = initState, action) => {
11 | switch (action.type) {
12 | case types.LOAD_SHUHUI_LIST:
13 | return Object.assign({}, state, {
14 | isLoading: action.isLoading,
15 | isLoadMore: action.isLoadMore,
16 | isRefresh: action.isRefresh
17 | });
18 | case types.GET_SHUHUI_LIST:
19 | return Object.assign({}, state, {
20 | isLoading: false,
21 | isRefresh: false,
22 | ShuhuiList: state.isLoadMore ? state.ShuhuiList.concat(action.ShuhuiList) : action.ShuhuiList
23 | });
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | export default shuhuiComicReducer;
30 |
--------------------------------------------------------------------------------
/js/reducers/passionComicReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from './../common/actionTypes';
2 |
3 | const initState = {
4 | PassionList: [],
5 | isLoading: true,
6 | isLoadMore: false,
7 | isRefresh: false,
8 | }
9 |
10 | let passionComicReducer = (state = initState, action) => {
11 | switch (action.type) {
12 | case types.LOAD_PASSION_LIST:
13 | return Object.assign({}, state, {
14 | isLoading: action.isLoading,
15 | isLoadMore: action.isLoadMore,
16 | isRefresh: action.isRefresh
17 | });
18 | case types.GET_PASSION_LIST:
19 | return Object.assign({}, state, {
20 | isLoading: false,
21 | isRefresh: false,
22 | PassionList: state.isLoadMore ? state.PassionList.concat(action.PassionList) : action.PassionList
23 | });
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | export default passionComicReducer;
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IShuHui",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node node_modules/react-native/local-cli/cli.js start",
7 | "test": "jest"
8 | },
9 | "dependencies": {
10 | "blueimp-md5": "^2.7.0",
11 | "react": "15.4.2",
12 | "react-native": "0.42",
13 | "react-native-app-intro": "^1.1.5",
14 | "react-native-cookie": "^0.1.1",
15 | "react-native-elements": "^0.10.3",
16 | "react-native-modalbox": "^1.3.9",
17 | "react-native-scrollable-tab-view": "^0.7.4",
18 | "react-native-vector-icons": "^4.0.1",
19 | "react-redux": "^5.0.3",
20 | "react-static-container": "^1.0.1",
21 | "redux": "^3.6.0",
22 | "redux-thunk": "^2.2.0"
23 | },
24 | "devDependencies": {
25 | "babel-jest": "19.0.0",
26 | "babel-preset-react-native": "1.9.1",
27 | "jest": "19.0.2",
28 | "react-test-renderer": "16.0.0-alpha.6"
29 | },
30 | "jest": {
31 | "preset": "react-native"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/js/common/api.js:
--------------------------------------------------------------------------------
1 |
2 | const BASE_API = "http://www.ishuhui.net/";
3 |
4 |
5 | // 订阅
6 |
7 | export const API_SUBSCRIBE_LIST= BASE_API + "ComicBooks/GetSubscribe?fromtype=2";
8 |
9 | // 查询
10 |
11 | export const API_SEARCH = BASE_API + "ComicBooks/GetAllBook?Title=";
12 |
13 | //国产漫画 要填index试试没有PageIndex的结果
14 |
15 | export const API_DOMESTIC_COMIC_LIST = BASE_API + "ComicBooks/GetAllBook?ClassifyId=2&Size=10&PageIndex=";
16 |
17 |
18 | export const API_PASSION_COMIC_LIST = BASE_API + "ComicBooks/GetAllBook?ClassifyId=4&Size=10&PageIndex=";
19 |
20 |
21 | export const API_SHUHUI_COMIC_LIST = BASE_API + "ComicBooks/GetAllBook?ClassifyId=3&Size=10&PageIndex=";
22 |
23 | // 查询章节
24 |
25 |
26 | //订阅章节
27 | //参数 isSubscribe true or false /bookId /frometype=3
28 | export const API_SUBSCRIBE = BASE_API+"ComicBooks/Subscribe"
29 |
30 | //登录
31 | //参数 FromType=5 /Email/ Password MD5加密
32 | export const API_LOGIN = "http://www.ishuhui.net/UserCenter/Login"
33 |
34 | //注册
35 |
--------------------------------------------------------------------------------
/js/reducers/domesticComicReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from './../common/actionTypes';
2 |
3 | const initState = {
4 | DomesticList: [],
5 | isLoading: true,
6 | isLoadMore: false,
7 | isRefresh: false,
8 | }
9 |
10 | let domesticComicReducer = (state = initState, action) => {
11 | switch (action.type) {
12 | case types.LOAD_DOMESTIC_LIST:
13 | return Object.assign({}, state, {
14 | isLoading: action.isLoading,
15 | isLoadMore: action.isLoadMore,
16 | isRefresh: action.isRefresh
17 | });
18 | case types.GET_DOMESTIC_LIST:
19 | return Object.assign({}, state, {
20 | isLoading: false,
21 | isRefresh: false,
22 | DomesticList: state.isLoadMore ? state.DomesticList.concat(action.DomesticList) : action.DomesticList
23 | });
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | export default domesticComicReducer;
30 |
--------------------------------------------------------------------------------
/js/reducers/subscribeReducer.js:
--------------------------------------------------------------------------------
1 | import * as types from './../common/actionTypes';
2 |
3 | const initState = {
4 | SubscribeList: [],
5 | isLoading: true,
6 | isLoadMore: false,
7 | isRefresh: false,
8 | }
9 |
10 | let subscribeReducer = (state = initState, action) => {
11 | switch (action.type) {
12 | case types.LOAD_SUBSCRIBE_LIST:
13 | return Object.assign({}, state, {
14 | isLoading: action.isLoading,
15 | isLoadMore: action.isLoadMore,
16 | isRefresh: action.isRefresh
17 | });
18 | case types.GET_SUBSCRIBE_LIST:
19 | return Object.assign({}, state, {
20 | isLoading: false,
21 | isRefresh: false,
22 | SubscribeList: state.isLoadMore ? state.SubscribeList.concat(action.SubscribeList) : action.SubscribeList
23 | });
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | export default subscribeReducer;
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *keystores
32 | *.iml
33 | *.apk
34 | # node.js
35 | #
36 | node_modules/
37 | npm-debug.log
38 | yarn-error.log
39 |
40 | # BUCK
41 | buck-out/
42 | \.buckd/
43 | *.keystore
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
51 |
52 | fastlane/report.xml
53 | fastlane/Preview.html
54 | fastlane/screenshots
55 |
--------------------------------------------------------------------------------
/js/containers/mApp.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | AppRegistry,
4 | StyleSheet,
5 | Text,
6 | View,
7 | navigator
8 | } from 'react-native';
9 | import NavigationBar from '../widget/NavigationBar'
10 | import ViewUtils from '../utils/ViewUtils';
11 |
12 | export default class mApp extends Component {
13 | onBack(){
14 | this.props.navigator.pop()
15 | }
16 | render() {
17 | return (
18 |
19 | this.onBack())}
22 | />
23 |
24 | 这是一个练手App,有事请联系jxing001@163.com
25 |
26 |
27 | );
28 | }
29 | }
30 |
31 | const styles = StyleSheet.create({
32 | container: {
33 | flex: 1,
34 | backgroundColor: '#F5FCFF',
35 | },
36 |
37 | text: {
38 | textAlign: 'center',
39 | color: '#333333',
40 | marginBottom: 5,
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/js/containers/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Navigator,
4 | View,
5 | StatusBar
6 | } from 'react-native';
7 |
8 | import Welcome from '../Welcome';
9 |
10 | class App extends React.Component {
11 | render() {
12 | return (
13 |
14 |
15 | {
18 | return Navigator.SceneConfigs.PushFromRight;
19 | }}
20 | renderScene={(route, navigator) => {
21 | let Component = route.component;
22 | return (
23 |
24 | )
25 | }}
26 | />
27 |
28 | )
29 | }
30 | }
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/js/actions/passionComicAction.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 | import {API_PASSION_COMIC_LIST} from '../common/api';
3 | import HttpUtil from '../utils/HttpUtils';
4 |
5 | export let passionComic = (PageIndex, isLoading, isLoadMore, isRefresh) => {
6 | return dispatch => {
7 |
8 | dispatch(loadPassionData(isLoading, isLoadMore, isRefresh));
9 | return HttpUtil.fetchGet(API_PASSION_COMIC_LIST,
10 | PageIndex,
11 | (jsonData) => {
12 | dispatch(receivePassionData(jsonData.List));
13 | },
14 | (err) => {
15 | dispatch(receivePassionData([]));
16 | })
17 | }
18 | }
19 |
20 | let loadPassionData = (isLoading, isLoadMore, isRefresh) => {
21 | return {
22 | type: types.LOAD_PASSION_LIST,
23 | isLoading: isLoading,
24 | isLoadMore: isLoadMore,
25 | isRefresh: isRefresh
26 | }
27 | }
28 |
29 | let receivePassionData = (PassionList) => {
30 | return {
31 | type: types.GET_PASSION_LIST,
32 | PassionList:PassionList
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/js/actions/domesticComicAction.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 | import {API_DOMESTIC_COMIC_LIST} from '../common/api';
3 | import HttpUtil from '../utils/HttpUtils';
4 |
5 | export let domesticComic = (PageIndex, isLoading, isLoadMore, isRefresh) => {
6 | return dispatch => {
7 |
8 | dispatch(loadDomesticData(isLoading, isLoadMore, isRefresh));
9 | return HttpUtil.fetchGet(API_DOMESTIC_COMIC_LIST,
10 | PageIndex,
11 | (jsonData) => {
12 | dispatch(receiveDomesticData(jsonData.List));
13 | },
14 | (err) => {
15 | dispatch(receiveDomesticData([]));
16 | })
17 | }
18 | }
19 |
20 | let loadDomesticData = (isLoading, isLoadMore, isRefresh) => {
21 | return {
22 | type: types.LOAD_DOMESTIC_LIST,
23 | isLoading: isLoading,
24 | isLoadMore: isLoadMore,
25 | isRefresh: isRefresh
26 | }
27 | }
28 |
29 | let receiveDomesticData = (DomesticList) => {
30 | return {
31 | type: types.GET_DOMESTIC_LIST,
32 | DomesticList:DomesticList
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/js/actions/shuhuiComicAction.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 | import {API_SHUHUI_COMIC_LIST} from '../common/api';
3 | import HttpUtil from '../utils/HttpUtils';
4 |
5 | export let shuhuiComic = (PageIndex, isLoading, isLoadMore, isRefresh) => {
6 | return dispatch => {
7 |
8 | dispatch(loadShuhuiData(isLoading, isLoadMore, isRefresh));
9 | return HttpUtil.fetchGet(API_SHUHUI_COMIC_LIST,
10 | PageIndex,
11 | (jsonData) => {
12 | dispatch({
13 | type: types.GET_SHUHUI_LIST,
14 | ShuhuiList: jsonData.List,
15 | });
16 | },
17 | (err) => {
18 | dispatch({
19 | type: types.GET_SHUHUI_LIST,
20 | ShuhuiList:[]
21 | });
22 | })
23 | }
24 | }
25 |
26 | let loadShuhuiData = (isLoading, isLoadMore, isRefresh) => {
27 | return {
28 | type: types.LOAD_SHUHUI_LIST,
29 | isLoading: isLoading,
30 | isLoadMore: isLoadMore,
31 | isRefresh: isRefresh
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/js/actions/chapterAction.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 | import HttpUtil from '../utils/HttpUtils';
3 |
4 | export let chapter = (id,PageIndex, isLoading, isLoadMore, isRefresh) => {
5 | return dispatch => {
6 | let url = "http://www.ishuhui.net/ComicBooks/GetChapterList?id="+id+"&PageIndex="
7 |
8 | dispatch(ChapterData(isLoading, isLoadMore, isRefresh));
9 | return HttpUtil.fetchGet(url,
10 | PageIndex,
11 | (jsonData) => {
12 | dispatch({
13 | type: types.GET_CHAPTER_LIST,
14 | ChapterList: jsonData.List,
15 | });
16 | },
17 | (err) => {
18 | dispatch({
19 | type: types.GET_CHAPTER_LIST,
20 | ChapterList:[]
21 | });
22 | })
23 | }
24 | }
25 |
26 | let ChapterData = (isLoading, isLoadMore, isRefresh) => {
27 | return {
28 | type: types.LOAD_CHAPTER_LIST,
29 | isLoading: isLoading,
30 | isLoadMore: isLoadMore,
31 | isRefresh: isRefresh
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## IShuHui
2 | IShuiHui 是React Native实现的鼠绘漫画的第三方客户端。主要针对react-native的api熟悉,以及redux的熟悉,不做商业用途。如有问题和改进意见,欢迎提issue一起讨论学习。ps.目前只导出了Android版本,iOS的并没有测试过(因为没有相关设备
3 |
4 |
5 |
6 |
7 | ## 运行
8 |
9 | ```
10 | $ git clone https://github.com/loopcf/IShuHui.git
11 | $ cd IShuHui
12 | $ npm install && react-native link
13 | //maybe need$ react-native link react-native-cookie
14 | //maybe need$ react-native link react-native-http-cache
15 | $ react-native run-ios/run-android
16 | ```
17 | ## 运行截图
18 | 
19 | [Android下载](https://fir.im/4p38?release_id=59042eed959d6971bd000077)
20 | ## 主要第三方库
21 |
22 | 1. [redux](https://github.com/reactjs/redux)
23 | 2. [react-redux](https://github.com/reactjs/react-redux)
24 | 3. [redux-thunk](https://github.com/gaearon/redux-thunk)
25 | 4. [react-native-app-intro](https://github.com/FuYaoDe/react-native-app-intro)
26 | 5. [react-native-elements](https://github.com/react-native-training/react-native-elements)
27 | 6. [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons)
28 | 7. etc...
29 |
30 | #####有事清联系jxing001@163.com
31 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/ishuhui/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.ishuhui;
2 |
3 | import android.app.Application;
4 |
5 | import com.facebook.react.ReactApplication;
6 | import im.shimo.react.cookie.CookieManagerPackage;
7 | import com.oblador.vectoricons.VectorIconsPackage;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.react.shell.MainReactPackage;
11 | import com.facebook.soloader.SoLoader;
12 |
13 | import java.util.Arrays;
14 | import java.util.List;
15 |
16 | public class MainApplication extends Application implements ReactApplication {
17 |
18 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
19 | @Override
20 | public boolean getUseDeveloperSupport() {
21 | return BuildConfig.DEBUG;
22 | }
23 |
24 | @Override
25 | protected List getPackages() {
26 | return Arrays.asList(
27 | new MainReactPackage(),
28 | new CookieManagerPackage(),
29 | new VectorIconsPackage()
30 | );
31 | }
32 | };
33 |
34 | @Override
35 | public ReactNativeHost getReactNativeHost() {
36 | return mReactNativeHost;
37 | }
38 |
39 | @Override
40 | public void onCreate() {
41 | super.onCreate();
42 | SoLoader.init(this, /* native exopackage */ false);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/js/actions/searchAction.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 | import localStorage from '../common/localStorage';
3 |
4 |
5 | export let fetchRecommendedList = (keyword)=> {
6 |
7 | let URL = 'http://www.ishuhui.net/ComicBooks/GetAllBook?Title='+keyword;
8 |
9 | return dispatch => {
10 | fetch(URL,{
11 | method:'POST',
12 | header:{
13 | 'Accpet':'application/json',
14 | 'Content-Type':'application/json',
15 | },
16 |
17 | }
18 | )
19 | .then((response) => response.json())
20 | .then((response) => {
21 | dispatch(RecommendedList(response.Return.List))
22 | })
23 | .catch((err) => {
24 |
25 | dispatch(RecommendedList([]))
26 | })
27 | }
28 |
29 | }
30 | let RecommendedList = (data)=> {
31 | return {
32 | type: types.SELECT_RECOMMEND_TAG,
33 | recomdData : data,
34 | }
35 |
36 | }
37 |
38 |
39 | export let resetState = ()=> {
40 | return {
41 | type: types.RESET_SEARCH_STATE,
42 | }
43 | }
44 |
45 | export let setupSearchText = (text)=> {
46 | if(text === 'null'){
47 | return {
48 | type: 1,
49 |
50 | }
51 | }else{
52 | return {
53 | type: types.SETUP_SEARCH_TEXT,
54 | searchText: text,
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/js/containers/Hot.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | AppRegistry,
4 | StyleSheet,
5 | Text,
6 | View,
7 | TouchableOpacity
8 | } from 'react-native';
9 | import NavigationBar from '../widget/NavigationBar'
10 | import ScrollableTabView,{ScrollableTabBar,DefaultTabBar} from 'react-native-scrollable-tab-view'
11 | import DomesticComic from './Hot/DomesticComic';
12 | import ShuHuiComic from './Hot/ShuHuiComic';
13 | import PassionComic from './Hot/PassionComic';
14 |
15 | export default class Hot extends Component {
16 | render() {
17 | return (
18 |
19 |
20 | }
27 | ref={(tabView) => { this.tabView = tabView }}
28 | >
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
42 | const styles = StyleSheet.create({
43 | container: {
44 | flex: 1,
45 | backgroundColor: '#EEF0F3',
46 | },
47 |
48 | });
49 |
--------------------------------------------------------------------------------
/ios/IShuHui/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AppDelegate.h"
11 |
12 | #import
13 | #import
14 |
15 | @implementation AppDelegate
16 |
17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
18 | {
19 | NSURL *jsCodeLocation;
20 |
21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
22 |
23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
24 | moduleName:@"IShuHui"
25 | initialProperties:nil
26 | launchOptions:launchOptions];
27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
28 |
29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
30 | UIViewController *rootViewController = [UIViewController new];
31 | rootViewController.view = rootView;
32 | self.window.rootViewController = rootViewController;
33 | [self.window makeKeyAndVisible];
34 | return YES;
35 | }
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/js/actions/subscribeAction.js:
--------------------------------------------------------------------------------
1 | import * as types from '../common/actionTypes';
2 | import {API_SUBSCRIBE_LIST} from '../common/api';
3 | // import HttpUtil from '../utils/HttpUtils';
4 |
5 | export let subscribeComic = ( isLoading, isLoadMore, isRefresh) => {
6 | return dispatch => {
7 | dispatch(loadSubscribeData(isLoading, isLoadMore, isRefresh));
8 | return fetch(API_SUBSCRIBE_LIST,{
9 | method:'POST',
10 | header:{
11 | 'Accpet':'application/json',
12 | 'Content-Type':'application/json',
13 | },
14 | })
15 | .then(response=>response.json())
16 | .then(result=>{
17 |
18 | // if(result.ErrCode===""){
19 | dispatch({
20 | type: types.GET_SUBSCRIBE_LIST,
21 | SubscribeList: result.Return.List,
22 | });
23 | // } else{
24 | // dispatch({
25 | // type: types.GET_SUBSCRIBE_LIST,
26 | // SubscribeList:[]
27 | // });
28 | // }
29 | })
30 |
31 | .catch(err=>{
32 | dispatch({
33 | type: types.GET_SUBSCRIBE_LIST,
34 | SubscribeList:[]
35 | });
36 | })
37 |
38 | }
39 | }
40 |
41 | let loadSubscribeData = (isLoading, isLoadMore, isRefresh) => {
42 | return {
43 | type: types.LOAD_SUBSCRIBE_LIST,
44 | isLoading: isLoading,
45 | isLoadMore: isLoadMore,
46 | isRefresh: isRefresh
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/js/containers/Setting.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | AppRegistry,
4 | StyleSheet,
5 | Text,
6 | View,
7 | navigator
8 | } from 'react-native';
9 | import NavigationBar from '../widget/NavigationBar'
10 | import { List, ListItem } from 'react-native-elements'
11 | import mApp from './mApp';
12 | export default class Setting extends Component {
13 |
14 | onPressApp(){
15 | this.props.navigator.push({
16 | component:mApp,
17 | name:mApp,
18 | })
19 | }
20 | render() {
21 | return (
22 |
23 |
26 |
27 |
32 |
36 |
37 |
38 |
42 | this.onPressApp()}
46 | rightTitle="它能点"
47 | />
48 |
49 |
50 | );
51 | }
52 | }
53 |
54 | const styles = StyleSheet.create({
55 | container: {
56 | flex: 1,
57 | backgroundColor: '#EEF0F3',
58 | },
59 | });
60 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | ; We fork some components by platform
3 | .*/*[.]android.js
4 |
5 | ; Ignore "BUCK" generated dirs
6 | /\.buckd/
7 |
8 | ; Ignore unexpected extra "@providesModule"
9 | .*/node_modules/.*/node_modules/fbjs/.*
10 |
11 | ; Ignore duplicate module providers
12 | ; For RN Apps installed via npm, "Libraries" folder is inside
13 | ; "node_modules/react-native" but in the source repo it is in the root
14 | .*/Libraries/react-native/React.js
15 | .*/Libraries/react-native/ReactNative.js
16 |
17 | [include]
18 |
19 | [libs]
20 | node_modules/react-native/Libraries/react-native/react-native-interface.js
21 | node_modules/react-native/flow
22 | flow/
23 |
24 | [options]
25 | emoji=true
26 |
27 | module.system=haste
28 |
29 | experimental.strict_type_args=true
30 |
31 | munge_underscores=true
32 |
33 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
34 |
35 | suppress_type=$FlowIssue
36 | suppress_type=$FlowFixMe
37 | suppress_type=$FixMe
38 |
39 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-0]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
40 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-0]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
41 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
42 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
43 |
44 | unsafe.enable_getters_and_setters=true
45 |
46 | [version]
47 | ^0.40.0
48 |
--------------------------------------------------------------------------------
/js/utils/HttpUtils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 网络请求工具类
3 | * loopfc create by 2017-04-16
4 | * url:请求地址
5 | * successCallback:成功回调
6 | * failCallback:失败回调
7 | */
8 |
9 | let HttpUtil = {
10 |
11 | /**
12 | * Get請求,沒有參數傳null
13 | */
14 | fetchGet: (url, params, successCallback, failCallback) => {
15 | url = url+params
16 | fetch(url)
17 | .then((response) => {
18 | return response.json();
19 | })
20 | .then((responseObj) => {
21 | successCallback(responseObj.Return)
22 | })
23 | .catch((error) => {
24 | failCallback(error)
25 | }).done();
26 | },
27 |
28 | /**
29 | * POST請求
30 | */
31 | fetchPost: (url, params, successCallback, failCallback) => {
32 |
33 | // 1.拼接參數
34 | var paramsBody = Object.keys(params)
35 | .reduce((a, k) => {
36 | a.push(k + "=" + encodeURIComponent(obj[k]));
37 | return a;
38 | }, [])
39 | .join('&');
40 | // 2.發送請求
41 | fetch(url, {
42 | method: 'POST',
43 | headers: {
44 | 'Content-Type': 'application/x-www-form-urlencoded'
45 | },
46 | body: paramsBody + "&key=35b747ca6b8056f8b5cd039f225001dc"
47 | })
48 | .then((response) => response.json())
49 | .then((responseObj) => successCallback(responseObj))
50 | .catch((error) => failCallback(error));
51 | }
52 | }
53 |
54 | export default HttpUtil;
55 |
--------------------------------------------------------------------------------
/js/Welcome.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | AppRegistry,
4 | StyleSheet,
5 | Text,
6 | View,
7 | Animated,
8 | } from 'react-native';
9 | import AppIntro from 'react-native-app-intro';
10 | import Home from './Home'
11 |
12 |
13 | const styles = StyleSheet.create({
14 | slide: {
15 | flex: 1,
16 | justifyContent: 'center',
17 | alignItems: 'center',
18 | backgroundColor: '#9DD6EB',
19 | padding: 15,
20 | },
21 | text: {
22 | color: '#fff',
23 | fontSize: 30,
24 | fontWeight: 'bold',
25 | },
26 | });
27 |
28 | export default class Welcome extends Component {
29 | constructor(props) {
30 | super(props);
31 | this.state = {
32 | progress: new Animated.Value(0),
33 | };
34 | }
35 |
36 |
37 | onSkipBtnHandle = (index) => {
38 | this.props.navigator.resetTo({
39 | component: Home
40 | })
41 | }
42 | doneBtnHandle = () => {
43 | this.props.navigator.resetTo({
44 | component: Home
45 | })
46 | }
47 | render() {
48 | return (
49 |
53 |
54 | this is intro Page 1
55 |
56 |
57 | this is intro Page 2
58 |
59 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/ios/IShuHui-tvOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UIViewControllerBasedStatusBarAppearance
38 |
39 | NSLocationWhenInUseUsageDescription
40 |
41 | NSAppTransportSecurity
42 |
43 |
44 | NSExceptionDomains
45 |
46 | localhost
47 |
48 | NSExceptionAllowsInsecureHTTPLoads
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/android/app/BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | lib_deps = []
12 |
13 | for jarfile in glob(['libs/*.jar']):
14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
15 | lib_deps.append(':' + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
21 | for aarfile in glob(['libs/*.aar']):
22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
23 | lib_deps.append(':' + name)
24 | android_prebuilt_aar(
25 | name = name,
26 | aar = aarfile,
27 | )
28 |
29 | android_library(
30 | name = "all-libs",
31 | exported_deps = lib_deps,
32 | )
33 |
34 | android_library(
35 | name = "app-code",
36 | srcs = glob([
37 | "src/main/java/**/*.java",
38 | ]),
39 | deps = [
40 | ":all-libs",
41 | ":build_config",
42 | ":res",
43 | ],
44 | )
45 |
46 | android_build_config(
47 | name = "build_config",
48 | package = "com.ishuhui",
49 | )
50 |
51 | android_resource(
52 | name = "res",
53 | package = "com.ishuhui",
54 | res = "src/main/res",
55 | )
56 |
57 | android_binary(
58 | name = "app",
59 | keystore = "//android/keystores:debug",
60 | manifest = "src/main/AndroidManifest.xml",
61 | package_type = "debug",
62 | deps = [
63 | ":app-code",
64 | ],
65 | )
66 |
--------------------------------------------------------------------------------
/js/containers/Detail/Detail.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import React, {Component} from 'react'
3 | import {
4 | Image,
5 | ScrollView,
6 | StyleSheet,
7 | WebView,
8 | Platform,
9 | TouchableOpacity,
10 | Text,
11 | View,
12 | } from 'react-native'
13 | import NavigationBar from '../../widget/NavigationBar'
14 | import ViewUtils from '../../utils/ViewUtils'
15 | const uri='http://www.ishuhui.net/ReadComicBooksToIso/';
16 | export default class Detail extends Component{
17 | constructor(props){
18 | super(props);
19 | // console.log(this.props);
20 | this.url=uri+this.props.route.params.id ;
21 | let title=this.props.route.params.name;
22 | this.state = {
23 | url:this.url,
24 | canGoBack:false,
25 | title:title
26 | }
27 | }
28 | onBack(){
29 | if(this.state.canGoBack){
30 | this.webView.goBack();
31 | }else{
32 | this.props.navigator.pop();
33 | }
34 | }
35 | onNavigationStateChange(e){
36 | this.setState({
37 | canGoBack:e.canGoBack,
38 | url:e.url,
39 | });
40 | }
41 |
42 | render(){
43 | return(
44 |
45 | this.onBack())}
48 | />
49 | this.webView=webView}
51 | onNavigationStateChange={(e)=>this.onNavigationStateChange(e)}
52 | source={{uri:this.state.url}}
53 | //source={{uri:'www.baidu.com'}}
54 | startInLoadingState={true}
55 | />
56 |
57 | )
58 | }
59 | }
60 | const styles = StyleSheet.create({
61 | container: {
62 | flex: 1,
63 | backgroundColor: '#ffffff',
64 | },
65 | })
66 |
--------------------------------------------------------------------------------
/js/common/actionTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * action 类型
3 | * loopcf create by 2017-04-16
4 | */
5 |
6 |
7 | //读取鼠绘漫画列表
8 | export const LOAD_SHUHUI_LIST = "LOAD_SHUHUI_LIST";
9 | //得到鼠绘漫画列表
10 | export const GET_SHUHUI_LIST = "GET_SHUHUI_LIST";
11 | //刷新鼠绘漫画列表
12 | export const RESET_SHUHUI_LIST = "RESET_SHUHUI_LIST";
13 |
14 | // 订阅
15 | export const GET_SUBSCRIBE_LIST="GET_SUBSCRIBE_LIST";
16 | export const LOAD_SUBSCRIBE_LIST="LOAD_SUBSCRIBE_LIST";
17 |
18 | //读取发现列表
19 | export const LOAD_DISCOVERY_LIST = "LOAD_DISCOVERY_LIST";
20 | //得到发现列表
21 | export const GET_DISCOVERY_LIST = "GET_DISCOVERY_LIST";
22 | //刷新
23 | export const RESET_DISCOVERY_LIST = "RESET_DISCOVERY_LIST";
24 |
25 | //读取国产漫画列表
26 | export const LOAD_DOMESTIC_LIST = "LOAD_DOMESTIC_LIST";
27 | //得到国产漫画列表
28 | export const GET_DOMESTIC_LIST = "GET_DOMESTIC_LIST";
29 | //刷新国产漫画列表
30 | export const RESET_DOMESTIC_LIST = "RESET_DOMESTIC_LIST";
31 |
32 | //读取热血漫画列表
33 | export const LOAD_PASSION_LIST = "LOAD_PASSION_LIST";
34 | //得到热血漫画列表
35 | export const GET_PASSION_LIST = "GET_PASSION_LIST";
36 | //刷新热血漫画列表
37 | export const RESET_PASSION_LIST = "RESET_PASSION_LIST";
38 | // 章节列表
39 |
40 | export const LOAD_CHAPTER_LIST = "LOAD_CHAPTER_LIST";
41 | export const GET_CHAPTER_LIST = "GET_CHAPTER_LIST";
42 | export const RESET_CHAPTER_LIST = "RESET_CHAPTER_LIST";
43 |
44 | // 漫画详情
45 |
46 | export const LOAD_COMBICK_DETIAL_LIST = "LOAD_COMBICK_DETIAL_LIST";
47 | export const GET_COMBICK_DETIAL_LIST = "GET_COMBICK_DETIAL_LIST";
48 | export const RESET_COMBICK_DETIAL_LIST = "RESET_COMBICK_DETIAL_LIST";
49 |
50 | // 收藏
51 |
52 | export const LOAD_COLLECTION_LIST = "LOAD_COLLECTION_LIST";
53 | export const GET_COLLECTION_LIST = "GET_COLLECTION_LIST";
54 | export const RESET_COLLECTION_LIST = "RESET_COLLECTION_LIST";
55 |
56 |
57 | // for user
58 | export const LOGGED_IN = 'LOGGED_IN';
59 | export const LOGGED_OUT = 'LOGGED_OUT';
60 | export const LOGGED_ERROR = 'LOGGED_ERROR';
61 | export const LOGGED_DOING = 'LOGGED_DOING';
62 |
63 |
64 | export const SELECT_RECOMMEND_TAG = 'SELECT_RECOMMEND_TAG';
65 | export const RESET_SEARCH_STATE = 'RESET_SEARCH_STATE';
66 | export const SETUP_SEARCH_TEXT = 'SETUP_SEARCH_TEXT';
67 |
--------------------------------------------------------------------------------
/js/actions/loginAction.js:
--------------------------------------------------------------------------------
1 | import * as TYPES from '../common/actionTypes';
2 | import {API_LOGIN, API_SUBSCRIBE} from '../common/api';
3 | import Cookie from 'react-native-cookie';
4 | import { AsyncStorage,Alert } from 'react-native';
5 |
6 | export function signinUser({ email, password }) {
7 | return (dispatch) => {
8 | dispatch({'type': TYPES.LOGGED_DOING});
9 | let formData = new FormData();
10 | formData.append("Email",email);
11 | formData.append("Password",password);
12 | formData.append("FromType","2");
13 | let result = fetch(API_LOGIN,{
14 | method:'POST',
15 | header:{
16 | 'Accpet':'application/json',
17 | 'Content-Type':'application/json',
18 | },
19 | body: formData
20 | }
21 | )
22 | .then(response=>response.json())
23 | .then(response => {
24 | if(response.ErrMsg==='0'){
25 | dispatch({'type': TYPES.LOGGED_IN});
26 | Cookie.get('http://www.ishuhui.net', 'PHPSESSID').then((cookie)=>{
27 | AsyncStorage.setItem('token',cookie,(error)=>{
28 | })
29 | })
30 | }else{
31 | Alert.alert("帐号密码错误");
32 | }
33 | })
34 | .catch(() => {
35 | Alert.alert('帐号密码错误');
36 | dispatch({'type': TYPES.LOGGED_ERROR, error: e});
37 | });
38 | }
39 | }
40 | // export function authError(error) {
41 | // return {
42 | // type: AUTH_ERROR,
43 | // };
44 | // }
45 |
46 | export function signoutUser() {
47 | AsyncStorage.clear((error)=>{
48 | });
49 | Cookie.clear('http://www.ishuhui.net');
50 | return { 'type': TYPES.LOGGED_OUT};
51 | }
52 |
53 | // export function fetchMessage() {
54 | // return function(dispatch) {
55 | // fetch(API_SUBSCRIBE,{
56 | // method:'POST',
57 | // header:{
58 | // 'Content-Type':'application/json',
59 | // },
60 | // })
61 | // .then(response=>response.json())
62 | // .then(responseObj => {
63 | // dispatch({
64 | // type: FETCH_MESSAGE,
65 | // payload: responseObj.Return.List
66 | // });
67 | // })
68 | // .catch(error=>{
69 | //
70 | // })
71 | //
72 | // }
73 | // }
74 |
--------------------------------------------------------------------------------
/ios/IShuHuiTests/IShuHuiTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | #import
14 | #import
15 |
16 | #define TIMEOUT_SECONDS 600
17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
18 |
19 | @interface IShuHuiTests : XCTestCase
20 |
21 | @end
22 |
23 | @implementation IShuHuiTests
24 |
25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
26 | {
27 | if (test(view)) {
28 | return YES;
29 | }
30 | for (UIView *subview in [view subviews]) {
31 | if ([self findSubviewInView:subview matching:test]) {
32 | return YES;
33 | }
34 | }
35 | return NO;
36 | }
37 |
38 | - (void)testRendersWelcomeScreen
39 | {
40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
42 | BOOL foundElement = NO;
43 |
44 | __block NSString *redboxError = nil;
45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
46 | if (level >= RCTLogLevelError) {
47 | redboxError = message;
48 | }
49 | });
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | RCTSetLogFunction(RCTDefaultLogFunction);
64 |
65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
67 | }
68 |
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/ios/IShuHui/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | IShuHui
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UIViewControllerBasedStatusBarAppearance
40 |
41 | NSLocationWhenInUseUsageDescription
42 |
43 | NSAppTransportSecurity
44 |
45 | NSExceptionDomains
46 |
47 | localhost
48 |
49 | NSExceptionAllowsInsecureHTTPLoads
50 |
51 |
52 |
53 |
54 | UIAppFonts
55 |
56 | Entypo.ttf
57 | EvilIcons.ttf
58 | FontAwesome.ttf
59 | Foundation.ttf
60 | Ionicons.ttf
61 | MaterialCommunityIcons.ttf
62 | MaterialIcons.ttf
63 | Octicons.ttf
64 | SimpleLineIcons.ttf
65 | Zocial.ttf
66 |
67 |
68 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Disabling obfuscation is useful if you collect stack traces from production crashes
20 | # (unless you are using a system that supports de-obfuscate the stack traces).
21 | -dontobfuscate
22 |
23 | # React Native
24 |
25 | # Keep our interfaces so they can be used by other ProGuard rules.
26 | # See http://sourceforge.net/p/proguard/bugs/466/
27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
30 |
31 | # Do not strip any method/class that is annotated with @DoNotStrip
32 | -keep @com.facebook.proguard.annotations.DoNotStrip class *
33 | -keep @com.facebook.common.internal.DoNotStrip class *
34 | -keepclassmembers class * {
35 | @com.facebook.proguard.annotations.DoNotStrip *;
36 | @com.facebook.common.internal.DoNotStrip *;
37 | }
38 |
39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
40 | void set*(***);
41 | *** get*();
42 | }
43 |
44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; }
46 | -keepclassmembers,includedescriptorclasses class * { native ; }
47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
50 |
51 | -dontwarn com.facebook.react.**
52 |
53 | # okhttp
54 |
55 | -keepattributes Signature
56 | -keepattributes *Annotation*
57 | -keep class okhttp3.** { *; }
58 | -keep interface okhttp3.** { *; }
59 | -dontwarn okhttp3.**
60 |
61 | # okio
62 |
63 | -keep class sun.misc.Unsafe { *; }
64 | -dontwarn java.nio.file.*
65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
66 | -dontwarn okio.**
67 |
--------------------------------------------------------------------------------
/js/widget/ComicCell.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Image,
7 | TouchableOpacity,
8 | } from 'react-native';
9 | import { Icon } from 'react-native-elements'
10 | export default class ComicCell extends Component{
11 | constructor(props){
12 | super(props);
13 | }
14 | render(){
15 | return(
16 |
20 |
21 |
22 | {this.props.data.SerializedState==='已完结' ? : }
24 |
25 |
26 |
27 |
28 |
29 | {this.props.data.Title}
30 | 最新{this.props.data.LastChapter.ChapterNo}话:{this.props.data.LastChapter.Title}
31 |
32 |
33 |
34 | {this.props.data.Explain}
35 |
36 |
37 |
38 | )
39 | }
40 | }
41 | const styles = StyleSheet.create({
42 | container: {
43 | flex:1
44 | },
45 | fonts: {
46 | fontSize: 18,
47 |
48 | },Icon:{
49 | flex:1,
50 | marginRight:30,
51 | marginTop:-3,
52 | },
53 | listitem: {
54 | flex:1,
55 | alignItems: "center",
56 | flexDirection: 'row',
57 | borderBottomColor: '#E6E6E6',
58 | borderBottomWidth: 0.5
59 | },
60 |
61 | itemImage: {
62 | margin: 10,
63 | width: 105,
64 | height: 70,
65 | borderRadius: 10
66 | },
67 | itemTitle: {
68 | flex: 1,
69 | justifyContent: 'flex-start',
70 | flexDirection: 'column',
71 | },
72 | hintImg: {
73 | width: 50,
74 | height: 50,
75 | marginLeft:58,
76 | marginTop:-3,
77 | },
78 | desc: {
79 | fontSize: 12,
80 | color:'#3C3C3C'
81 |
82 | },
83 | })
84 |
--------------------------------------------------------------------------------
/js/widget/SearchHeader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | Text,
6 | TouchableOpacity,
7 | Image,
8 | TextInput,
9 | Platform,
10 | } from 'react-native';
11 | import { Icon } from 'react-native-elements'
12 |
13 | export default class SearchHeader extends React.Component {
14 | render() {
15 | return (
16 |
17 |
18 |
19 |
25 |
29 |
30 |
31 |
32 |
35 |
36 | this._textInput = component}
41 | clearButtonMode={'never'}
42 | returnKeyType='search'
43 | {...this.props}
44 | />
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
56 | }
57 |
58 | const styles = StyleSheet.create({
59 | searchContainer: {
60 | flexDirection: 'row',
61 | height: Platform.OS === 'ios' ? 64 : 44,
62 | alignItems: 'center',
63 | borderBottomColor: '#ccc',
64 | borderBottomWidth: 0.5,
65 | backgroundColor: '#FAFDFF',
66 | elevation:2,
67 |
68 | },
69 | inputText: {
70 | flex: 1,
71 | backgroundColor: '#fff',
72 | fontSize: Platform.OS === 'ios' ? 15 : 11,
73 | marginTop: Platform.OS === 'ios' ? 0 : 10,
74 | height: Platform.OS === 'ios' ? 28 : 28, //通过大于TextInput的高度来弥补上面的问题
75 | justifyContent: 'flex-end' //放置到底部
76 | },
77 | textInput: {
78 | flex:1,
79 | textAlignVertical:'center',
80 | marginLeft:10,
81 | padding:Platform.OS === 'ios' ? 0 : 0,
82 |
83 | fontSize: 15,
84 |
85 | },
86 |
87 | searchIcon: {
88 | marginLeft:10,
89 | width:15,
90 | height:15
91 | },
92 | backIcon: {
93 | marginTop:Platform.OS === 'ios' ? 20 : 0,
94 | width:35,
95 | height:44,
96 | justifyContent:'center'
97 | },
98 | })
99 | module.exports = SearchHeader;
100 |
--------------------------------------------------------------------------------
/js/widget/TabBar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component
5 | } from 'react';
6 |
7 | import {
8 | StyleSheet,
9 | View,
10 | Text,
11 | TouchableOpacity,
12 | Keyboard,
13 | Platform,
14 | } from 'react-native';
15 |
16 | type State = {
17 | keyboardUp: boolean,
18 | }
19 |
20 | class TabBar extends Component {
21 | state: State = {};
22 |
23 | onSelect(el){
24 | if (el.props.onSelect) {
25 | el.props.onSelect(el);
26 | } else if (this.props.onSelect) {
27 | this.props.onSelect(el);
28 | }
29 | }
30 |
31 | componentWillMount(){
32 | if (Platform.OS==='android') {
33 | this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardWillShow);
34 | this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardWillHide);
35 | }
36 | }
37 |
38 | componentWillUnmount(){
39 | this.keyboardDidShowListener.remove();
40 | this.keyboardDidHideListener.remove();
41 | }
42 |
43 | keyboardWillShow = (e) => {
44 | this.setState({ keyboardUp: true });
45 | };
46 |
47 | keyboardWillHide = (e) => {
48 | this.setState({ keyboardUp: false });
49 | };
50 |
51 | render(){
52 | const self = this;
53 | let selected = this.props.selected
54 | if (!selected){
55 | React.Children.forEach(this.props.children.filter(c=>c), el=>{
56 | if (!selected || el.props.initial){
57 | selected = el.props.name || el.key;
58 | }
59 | });
60 | }
61 | return (
62 |
63 | {React.Children.map(this.props.children.filter(c=>c),(el)=>
64 | !self.props.locked && self.onSelect(el)}
68 | onLongPress={()=>self.onSelect(el)}
69 | activeOpacity={el.props.pressOpacity}>
70 | {selected == (el.props.name || el.key) ? React.cloneElement(el, {selected: true, style: [el.props.style, this.props.selectedStyle, el.props.selectedStyle]}) : el}
71 |
72 | )}
73 |
74 | );
75 | }
76 | }
77 | var styles = StyleSheet.create({
78 | tabbarView: {
79 | position:'absolute',
80 | bottom:0,
81 | right:0,
82 | left:0,
83 | height:50,
84 | opacity:1,
85 | backgroundColor:'transparent',
86 | flexDirection: 'row',
87 | justifyContent: 'center',
88 | alignItems: 'center'
89 | },
90 | iconView: {
91 | flex: 1,
92 | height: 50,
93 | justifyContent: 'center',
94 | alignItems: 'center',
95 | },
96 | hidden: {
97 | height: 0,
98 | },
99 | });
100 |
101 | module.exports = TabBar;
102 |
--------------------------------------------------------------------------------
/js/widget/NavigationBar.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 |
3 | import {
4 | StyleSheet,
5 | Platform,
6 | TouchableOpacity,
7 | Image,
8 | StatusBar,
9 | Text,
10 | View
11 | } from 'react-native'
12 | const NAV_BAR_HEIGHT_IOS = 44;
13 | const NAV_BAR_HEIGHT_ANDROID = 50;
14 | const STATUS_BAR_HEIGHT = 20;
15 | const StatusBarShape = {
16 | barStyle: PropTypes.oneOf(['light-content', 'default',]),
17 | hidden: PropTypes.bool,
18 | backgroundColor: PropTypes.string,
19 | };
20 | export default class NavigationBar extends Component {
21 | static propTypes = {
22 | style: View.propTypes.style,
23 | title: PropTypes.string,
24 | titleView: PropTypes.element,
25 | titleLayoutStyle:View.propTypes.style,
26 | hide: PropTypes.bool,
27 | statusBar: PropTypes.shape(StatusBarShape),
28 | rightButton: PropTypes.element,
29 | leftButton: PropTypes.element,
30 | ...View.propTypes,
31 |
32 | }
33 | static defaultProps = {
34 | statusBar: {
35 | barStyle: 'light-content',
36 | hidden: false,
37 | },
38 | }
39 | constructor(props) {
40 | super(props);
41 | this.state = {
42 | title: '',
43 | hide: false
44 | };
45 | }
46 |
47 | getButtonElement(data) {
48 | return (
49 |
50 | {data? data : null}
51 |
52 | );
53 | }
54 |
55 | render() {
56 | let statusBar = !this.props.statusBar.hidden ?
57 |
58 |
59 | : null;
60 |
61 | let titleView = this.props.titleView ? this.props.titleView :
62 | {this.props.title};
63 |
64 | let content = this.props.hide ? null :
65 |
66 | {this.getButtonElement(this.props.leftButton)}
67 |
68 | {titleView}
69 |
70 | {this.getButtonElement(this.props.rightButton)}
71 | ;
72 | return (
73 |
74 | {statusBar}
75 | {content}
76 |
77 | )
78 | }
79 | }
80 |
81 | const styles = StyleSheet.create({
82 | container: {
83 | backgroundColor: '#FAFDFF',
84 | elevation:2,
85 | },
86 | navBar: {
87 | flexDirection: 'row',
88 | alignItems: 'center',
89 | justifyContent: 'space-between',
90 | height: Platform.OS === 'ios' ? NAV_BAR_HEIGHT_IOS : NAV_BAR_HEIGHT_ANDROID,
91 | },
92 | navBarTitleContainer: {
93 | alignItems: 'center',
94 | justifyContent: 'center',
95 | position: 'absolute',
96 | left: 40,
97 | top: 0,
98 | right: 40,
99 | bottom: 0,
100 | },
101 | title: {
102 | fontSize: 20,
103 | color: '#000000',
104 | },
105 | navBarButton: {
106 | alignItems: 'center',
107 | },
108 | statusBar: {
109 | height: Platform.OS === 'ios' ? STATUS_BAR_HEIGHT:0,
110 |
111 | },
112 | })
113 |
--------------------------------------------------------------------------------
/ios/IShuHui/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/js/widget/SubscribeCell.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Image,
7 | TouchableOpacity,
8 | } from 'react-native';
9 | import { Icon } from 'react-native-elements'
10 | export default class SubscribeCell extends Component{
11 | constructor(props){
12 | super(props);
13 | this.state={
14 | isSubscribe:false,
15 | IconUri:require('./../../res/images/ic_Subscribe.png')
16 | }
17 | }
18 | setSubscribe(isSubscribe){
19 | this.setState({
20 | isSubscribe:isSubscribe,
21 | IconUri:isSubscribe?require('./../../res/images/ic_unSubscribe.png'):require('./../../res/images/ic_Subscribe.png')
22 | })
23 | }
24 | onPressSubscribe(){
25 | this.setSubscribe(!this.state.isSubscribe);
26 | }
27 |
28 | render(){
29 | // console.log(this.props);
30 | let SubscribeButton={this.onPressSubscribe()}}
32 | >
33 |
38 |
39 |
40 |
41 | return(
42 |
46 |
47 | {/* */}
48 |
49 |
50 | {this.props.data.SerializedState==='已完结' ? : }
52 |
53 |
54 |
55 |
56 |
57 | {this.props.data.Title}
58 | 最新{this.props.data.LastChapter.ChapterNo}话:{this.props.data.LastChapter.Title}
59 |
60 | {SubscribeButton}
61 |
62 |
63 | {this.props.data.Explain}
64 |
65 |
66 |
67 | )
68 | }
69 | }
70 | const styles = StyleSheet.create({
71 | container: {
72 | flex:1
73 | },
74 | fonts: {
75 | fontSize: 18,
76 |
77 | },Icon:{
78 | flex:1,
79 | marginRight:30,
80 | marginTop:-3,
81 | },
82 | listitem: {
83 | flex:1,
84 | alignItems: "center",
85 | flexDirection: 'row',
86 | borderBottomColor: '#E6E6E6',
87 | borderBottomWidth: 0.5
88 | },
89 |
90 | itemImage: {
91 | margin: 10,
92 | width: 105,
93 | height: 70,
94 | borderRadius: 10
95 | },
96 | itemTitle: {
97 | flex: 1,
98 | justifyContent: 'flex-start',
99 | flexDirection: 'column',
100 | },
101 | hintImg: {
102 | width: 50,
103 | height: 50,
104 | marginLeft:58,
105 | marginTop:-3,
106 | },
107 | desc: {
108 | fontSize: 12,
109 | color:'#3C3C3C'
110 |
111 | },
112 | })
113 |
--------------------------------------------------------------------------------
/js/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | Navigator,
4 | AppRegistry,
5 | StyleSheet,
6 | Text,
7 | View
8 | } from 'react-native';
9 | import Hot from './containers/Hot';
10 | import Subscribe from './containers/Subscribe';
11 | import Setting from './containers/Setting';
12 | import Search from './containers/Search';
13 | import { Tabs, Tab, Icon } from 'react-native-elements'
14 |
15 | export default class Home extends Component {
16 |
17 | constructor() {
18 | super()
19 | this.state = {
20 | selectedTab: 'hot',
21 | }
22 | }
23 |
24 | changeTab (selectedTab) {
25 | this.setState({selectedTab});
26 | }
27 |
28 |
29 |
30 | render() {
31 |
32 | const { selectedTab } = this.state
33 | return (
34 |
35 |
36 |
39 | }
45 | renderSelectedIcon={() => }
46 | onPress={() => this.changeTab('hot')}>
47 |
48 |
49 | }
55 | renderSelectedIcon={() => }
56 | onPress={() => this.changeTab('Subscribe')}>
57 |
58 |
59 | }
65 | renderSelectedIcon={() => }
66 | onPress={() => this.changeTab('Search')}>
67 |
68 |
69 | }
75 | renderSelectedIcon={() => }
76 | onPress={() => this.changeTab('Setting')}>
77 |
78 |
79 |
80 |
81 | )
82 | }
83 | }
84 |
85 | const styles= StyleSheet.create({
86 | container: {
87 | flex: 1,
88 | backgroundColor: '#FFFFFF',
89 | height:15,
90 | },
91 | TabBar:{
92 | height:50,
93 | },
94 |
95 | contentContainer: {
96 | flex: 1
97 | },
98 |
99 |
100 |
101 |
102 | hintImg: {
103 | width: 50,
104 | height: 50,
105 | },
106 | desc: {
107 | fontSize: 12
108 | }
109 |
110 | });
111 |
--------------------------------------------------------------------------------
/js/containers/Hot/PassionComic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | View,
4 | Image,
5 | Text,
6 | ListView,
7 | Navigator,
8 | RefreshControl,
9 | TouchableOpacity,
10 | TouchableHighlight,
11 | StyleSheet,
12 | Dimensions,
13 | InteractionManager,
14 | navigator
15 | } from 'react-native';
16 | import { Tile } from 'react-native-elements'
17 | import { connect } from 'react-redux';
18 | import {passionComic} from '../../actions/passionComicAction'
19 | import LoadingMoreFooter from '../../widget/LoadingMoreFooter';
20 | import Chapter from '../Chapter/Chapter';
21 | import ComicCell from '../../widget/ComicCell'
22 |
23 | let isLoading = true;
24 | let isLoadMore = false;
25 | let isRefresh = false;
26 | let PageIndex = 0;
27 | let isFirstLoad = true;
28 |
29 |
30 | class PassionComic extends Component {
31 |
32 | constructor(props) {
33 | super(props);
34 | this.state = {
35 | dataSource: new ListView.DataSource({
36 | rowHasChanged: (row1, row2) => row1 !== row2
37 | })
38 | }
39 | }
40 |
41 | componentDidMount() {
42 | InteractionManager.runAfterInteractions(() => {
43 | this.props.passionComic(PageIndex, isLoading, isLoadMore, isRefresh);
44 | });
45 | }
46 |
47 | render() {
48 | if (this.props.PassionList) {
49 | isFirstLoad = false;
50 | }
51 | return (
52 |
53 | }
67 | />
68 |
69 | )
70 | }
71 | renderRow(rowData, sectionId) {
72 | return this.onPressRow(rowData)}
74 | key={sectionId}
75 | data={rowData}/>
76 | }
77 |
78 | onRefresh() {
79 | isRefresh = true;
80 | isLoading = false;
81 | isLoadMore = false;
82 | const {passionComic} = this.props;
83 | PageIndex = 0;
84 | passionComic(PageIndex, isLoading, isLoadMore, isRefresh);
85 | }
86 |
87 | onEndReach() {
88 |
89 | if (!isFirstLoad) {
90 | isLoadMore = true;
91 | isRefresh = false;
92 | isLoading = false;
93 | const {passionComic} = this.props;
94 | PageIndex = +PageIndex + 1;
95 | passionComic(PageIndex, isLoading, isLoadMore, isRefresh);
96 | }
97 | }
98 |
99 | renderFooter() {
100 |
101 | if (this.props.isLoadMore && this.props.PassionList.length > 0) {
102 | return
103 |
104 | }
105 | }
106 |
107 | onPressRow(rowData) {
108 | this.props.navigator.push({
109 | component:Chapter,
110 | params:{
111 | name:rowData.Title,
112 | id:rowData.Id,
113 | cover:rowData.FrontCover,
114 | src:rowData.Explain,
115 | Author:rowData.Author,
116 | }
117 | })
118 | }
119 | }
120 |
121 | const styles = StyleSheet.create({
122 | container: {
123 | flex: 1,
124 | backgroundColor: '#F5FCFF',
125 | },
126 | listview: {
127 | height: Dimensions.get('window').height
128 | },
129 | });
130 |
131 | export default PassionComic = connect(
132 | (state) => {
133 | const { PassionList,isLoading,isLoadMore,isRefresh } = state.passionComicReducer;
134 | return {
135 | PassionList,
136 | isLoading,
137 | isLoadMore,
138 | isRefresh
139 | }
140 | },{passionComic})(PassionComic);
141 |
--------------------------------------------------------------------------------
/js/containers/Hot/ShuHuiComic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | View,
4 | Image,
5 | Text,
6 | ListView,
7 | Navigator,
8 | RefreshControl,
9 | TouchableOpacity,
10 | TouchableHighlight,
11 | StyleSheet,
12 | Dimensions,
13 | InteractionManager,
14 | navigator
15 | } from 'react-native';
16 | import { Tile } from 'react-native-elements'
17 | import { connect } from 'react-redux';
18 | import {API_SHUHUI_COMIC_LIST} from '../../common/api';
19 | import {shuhuiComic} from '../../actions/shuhuiComicAction'
20 | import LoadingMoreFooter from '../../widget/LoadingMoreFooter';
21 | import Chapter from '../Chapter/Chapter';
22 | import ComicCell from '../../widget/ComicCell'
23 |
24 | let PageIndex = 0;
25 | let isLoading = true;
26 | let isLoadMore = false;
27 | let isRefresh = false;
28 | let isFirstLoad = true;
29 |
30 |
31 | class ShuHuiComic extends Component {
32 |
33 | constructor(props) {
34 | super(props);
35 | this.state = {
36 | dataSource: new ListView.DataSource({
37 | rowHasChanged: (row1, row2) => row1 !== row2
38 | })
39 | }
40 | }
41 |
42 | componentDidMount() {
43 | InteractionManager.runAfterInteractions(() => {
44 | this.props.shuhuiComic(PageIndex, isLoading, isLoadMore, isRefresh);
45 | });
46 | }
47 |
48 | render() {
49 | // console.log(this.props.ShuhuiList);
50 | if (this.props.ShuhuiList) {
51 | isFirstLoad = false;
52 | }
53 | return (
54 |
55 | }
69 | />
70 |
71 | )
72 | }
73 | renderRow(rowData, sectionId) {
74 | return this.onPressRow(rowData)}
76 | key={sectionId}
77 | data={rowData}/>
78 | }
79 |
80 | onRefresh() {
81 | isRefresh = true;
82 | isLoading = false;
83 | isLoadMore = false;
84 | const {shuhuiComic} = this.props;
85 | PageIndex = 0;
86 | shuhuiComic(PageIndex, isLoading, isLoadMore, isRefresh);
87 | }
88 |
89 | onEndReach() {
90 |
91 | if (!isFirstLoad) {
92 | isLoadMore = true;
93 | isRefresh = false;
94 | isLoading = false;
95 | const {shuhuiComic} = this.props;
96 | PageIndex = +PageIndex + 1;
97 | shuhuiComic(PageIndex, isLoading, isLoadMore, isRefresh);
98 | }
99 | }
100 |
101 | renderFooter() {
102 |
103 | if (this.props.isLoadMore && this.props.ShuhuiList.length > 0) {
104 | return
105 |
106 | }
107 | }
108 |
109 | onPressRow(rowData) {
110 | this.props.navigator.push({
111 | component:Chapter,
112 | params:{
113 | name:rowData.Title,
114 | id:rowData.Id,
115 | cover:rowData.FrontCover,
116 | src:rowData.Explain,
117 | Author:rowData.Author,
118 | // 标签sign:
119 | }
120 | })
121 | }
122 | }
123 |
124 | const styles = StyleSheet.create({
125 | container: {
126 | flex: 1,
127 | backgroundColor: '#F5FCFF',
128 | },
129 |
130 |
131 | listview: {
132 | height: Dimensions.get('window').height
133 | },
134 |
135 | });
136 |
137 | export default ShuHuiComic = connect(
138 | (state) => {
139 | const { ShuhuiList,isLoading,isLoadMore,isRefresh } = state.shuhuiComicReducer;
140 | return {
141 | ShuhuiList,
142 | isLoading,
143 | isLoadMore,
144 | isRefresh
145 | }
146 | },{shuhuiComic})(ShuHuiComic);
147 |
--------------------------------------------------------------------------------
/js/containers/Login.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | StyleSheet,
4 | TextInput,
5 | Text,
6 | View,
7 | navigator,
8 | TouchableHighlight,
9 | ActivityIndicator,
10 | ScrollView,
11 | DeviceEventEmitter,
12 | } from 'react-native';
13 |
14 | import {connect} from 'react-redux';//将我们的页面和action链接起来
15 | import {bindActionCreators} from 'redux';//将要绑定的actions和dispatch绑定到一起
16 | import * as actionCreators from '../actions/loginAction';//导入需要绑定的actions
17 | import * as subscribeComic from '../actions/subscribeAction';
18 | import Subscribe from './Subscribe';
19 | import md5 from 'blueimp-md5';
20 | import Modal from 'react-native-modalbox';
21 | import ViewUtils from '../utils/ViewUtils';
22 | import NavigationBar from '../widget/NavigationBar';
23 | let isLoading = true;
24 | let isLoadMore = false;
25 | let isRefresh = false;
26 | class Login extends Component{
27 |
28 | constructor(props){
29 | super(props);
30 | console.log(this.props)
31 | this.state={
32 | }
33 | this.login=this.login.bind(this);
34 | this.onChangeEmail=this.onChangeEmail.bind(this);
35 | this.onChangePswd=this.onChangePswd.bind(this);
36 | }
37 |
38 | onChangeEmail(text){
39 | this.setState({'Email':text,});
40 | }
41 |
42 | onChangePswd(text){
43 | this.setState({'password':text,});
44 | }
45 |
46 | login(){
47 |
48 | if(!this.state.Email||!this.state.password){
49 | alert('用户名或密码不能为空!');
50 | }else{
51 | this.props.actions.signinUser({'email':this.state.Email,'password':md5(this.state.password)});//dispath 登陆
52 | }
53 | }
54 |
55 | shouldComponentUpdate(nextProps,nextState){
56 | if(nextProps.isLoggedIn != this.props.isLoggedIn && nextProps.isLoggedIn === true){
57 | this.refs.modal.close();
58 |
59 | this.toSubscribe();
60 | return false;
61 | }
62 |
63 |
64 | return true;
65 |
66 | }
67 |
68 | onBack(){
69 | this.props.navigator.pop();
70 | }
71 | toSubscribe(){
72 | this.subscription = DeviceEventEmitter.addListener('Function', ()=>{
73 | this.props.subscribeComic.subscribeComic(isLoading, isLoadMore, isRefresh);
74 | });
75 |
76 | this.props.navigator.pop();
77 | }
78 | render(){
79 | console.log('render...');
80 | return(
81 |
83 | this.onBack())}
86 | />
87 |
88 | 邮箱号码
89 |
95 |
96 |
97 | 密码
98 |
105 |
106 |
107 |
109 | 登陆
110 |
111 |
112 |
113 |
120 |
123 | 登陆中...
124 |
125 |
126 |
127 | );
128 | }
129 | }
130 |
131 | const styles =StyleSheet.create({
132 | item:{
133 | flexDirection:'row',
134 | alignItems:'center',
135 | height:50,
136 | width:200,
137 | },
138 | input:{
139 | fontSize:14,
140 | width:150,
141 | },
142 | button:{
143 | backgroundColor:'#1a191f',
144 | height:50,
145 | marginTop:40,
146 | justifyContent:'center',
147 | alignItems:'center'
148 | },
149 | modal: {
150 | justifyContent: 'center',
151 | alignItems: 'center',
152 | width:150,
153 | height:150,
154 | borderRadius:10,
155 | },
156 | });
157 |
158 |
159 | function mapStateToProps(state){
160 | return {
161 | isLoggedIn: state.authReducer.isLoggedIn,
162 | status: state.authReducer.status, };
163 | }
164 | function mapDispatchToProps(dispatch){
165 | return {
166 | actions: bindActionCreators(actionCreators, dispatch),
167 | subscribeComic:bindActionCreators(subscribeComic, dispatch)
168 | };
169 | }
170 | export default connect(mapStateToProps,mapDispatchToProps)(Login);
171 |
--------------------------------------------------------------------------------
/js/containers/Hot/DomesticComic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | View,
4 | Image,
5 | Text,
6 | ListView,
7 | Navigator,
8 | RefreshControl,
9 | TouchableOpacity,
10 | TouchableHighlight,
11 | StyleSheet,
12 | Dimensions,
13 | InteractionManager,
14 | navigator
15 | } from 'react-native';
16 |
17 |
18 | import { connect } from 'react-redux';
19 | import {domesticComic} from '../../actions/domesticComicAction'
20 | import LoadingMoreFooter from '../../widget/LoadingMoreFooter';
21 | import Chapter from '../Chapter/Chapter';
22 | import ComicCell from '../../widget/ComicCell'
23 |
24 | let isLoading = true;
25 | let isLoadMore = false;
26 | let isRefresh = false;
27 | let PageIndex = 0;
28 | let isFirstLoad = true;
29 |
30 |
31 | class DomesticComic extends Component {
32 |
33 | constructor(props) {
34 | super(props);
35 | this.state = {
36 | dataSource: new ListView.DataSource({
37 | rowHasChanged: (row1, row2) => row1 !== row2
38 | })
39 | }
40 | }
41 |
42 | componentDidMount() {
43 | InteractionManager.runAfterInteractions(() => {
44 | this.props.domesticComic(PageIndex, isLoading, isLoadMore, isRefresh);
45 | });
46 | }
47 |
48 | render() {
49 | if (this.props.DomesticList) {
50 | isFirstLoad = false;
51 | }
52 | return (
53 |
54 | }
68 | />
69 |
70 | )
71 | }
72 | renderRow(rowData, sectionId) {
73 | return this.onPressRow(rowData)}
75 | key={sectionId}
76 | data={rowData}/>
77 | }
78 |
79 | onRefresh() {
80 | isRefresh = true;
81 | isLoading = false;
82 | isLoadMore = false;
83 | const {domesticComic} = this.props;
84 | PageIndex = 0;
85 | domesticComic(PageIndex, isLoading, isLoadMore, isRefresh);
86 | }
87 |
88 | onEndReach() {
89 |
90 | if (!isFirstLoad) {
91 | isLoadMore = true;
92 | isRefresh = false;
93 | isLoading = false;
94 | const {domesticComic} = this.props;
95 | PageIndex = +PageIndex + 1;
96 | domesticComic(PageIndex, isLoading, isLoadMore, isRefresh);
97 | }
98 | }
99 |
100 | renderFooter() {
101 |
102 | if (this.props.isLoadMore && this.props.DomesticList.length > 0) {
103 | return
104 |
105 | }
106 | }
107 |
108 | onPressRow(rowData) {
109 | this.props.navigator.push({
110 | component:Chapter,
111 | params:{
112 | name:rowData.Title,
113 | id:rowData.Id,
114 | cover:rowData.FrontCover,
115 | src:rowData.Explain,
116 | Author:rowData.Author,
117 | }
118 | })
119 | }
120 | }
121 |
122 | const styles = StyleSheet.create({
123 | container: {
124 | flex: 1,
125 | backgroundColor: '#F5FCFF',
126 | },
127 |
128 | contentContainer: {
129 | flex: 1
130 | },
131 |
132 | listview: {
133 | height: Dimensions.get('window').height
134 | },
135 |
136 | listitem: {
137 | alignItems: "center",
138 | flexDirection: 'row',
139 | borderBottomColor: '#E6E6E6',
140 | borderBottomWidth: 0.5
141 | },
142 |
143 | itemImage: {
144 | margin: 10,
145 | width: 105,
146 | height: 70,
147 | borderRadius: 10
148 | },
149 | itemTitle: {
150 | flex: 1,
151 | justifyContent: 'flex-start',
152 | flexDirection: 'column',
153 | },
154 | hintImg: {
155 | width: 50,
156 | height: 50,
157 | },
158 | desc: {
159 | fontSize: 12,
160 | color:'#3C3C3C'
161 |
162 | }
163 |
164 | });
165 |
166 |
167 | export default DomesticComic = connect(
168 | (state) => {
169 | const { DomesticList,isLoading,isLoadMore,isRefresh } = state.domesticComicReducer;
170 | return {
171 | DomesticList,
172 | isLoading,
173 | isLoadMore,
174 | isRefresh
175 | }
176 | },{domesticComic})(DomesticComic);
177 |
--------------------------------------------------------------------------------
/ios/IShuHui.xcodeproj/xcshareddata/xcschemes/IShuHui.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/js/containers/Subscribe.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | StyleSheet,
4 | Text,
5 | View,
6 | Alert,
7 | TouchableHighlight,
8 | navigator,
9 | ListView,
10 | RefreshControl,
11 | Dimensions,
12 | InteractionManager,
13 | DeviceEventEmitter,
14 | } from 'react-native';
15 | import NavigationBar from '../widget/NavigationBar'
16 | import Login from './Login';
17 | import {connect} from 'react-redux';//将我们的页面和action链接起来
18 | import {bindActionCreators} from 'redux';//将要绑定的actions和dispatch绑定到一起
19 | import * as actionCreators from '../actions/loginAction';//导入需要绑定的actions
20 | import Chapter from './Chapter/Chapter';
21 | import SubscribeCell from '../widget/SubscribeCell';
22 | import LoadingMoreFooter from '../widget/LoadingMoreFooter';
23 | import {subscribeComic} from '../actions/subscribeAction';
24 |
25 | let isLoading = true;
26 | let isLoadMore = false;
27 | let isRefresh = false;
28 | let isFirstLoad = true;
29 |
30 | class Subscribe extends Component {
31 | constructor(props) {
32 | super(props);
33 | this.state = {
34 | dataSource: new ListView.DataSource({
35 | rowHasChanged: (row1, row2) => row1 !== row2
36 | })
37 | }
38 | DeviceEventEmitter.emit('Function',null);
39 | }
40 | renderRow(rowData, sectionId) {
41 | return this.onPressRow(rowData)}
43 | key={sectionId}
44 | data={rowData}
45 | onPressImage={()=>this.onPressSubscribe()}
46 | />
47 | }
48 | onPressSubscribe(){
49 | Alert.alert('onPressSubscribe');
50 | }
51 | onRefresh() {
52 | isRefresh = true;
53 | isLoading = false;
54 | isLoadMore = false;
55 | this.props.subscribeComic(isLoading, isLoadMore, isRefresh);
56 | }
57 |
58 | onEndReach() {
59 |
60 | if (!isFirstLoad) {
61 | isLoadMore = true;
62 | isRefresh = false;
63 | isLoading = false;
64 | this.props.subscribeComic( isLoading, isLoadMore, isRefresh);
65 | }
66 | }
67 |
68 | renderFooter() {
69 |
70 | if (this.props.isLoadMore && this.props.SubscribeList.length > 0) {
71 | return
72 |
73 | }
74 | }
75 |
76 | onPressRow(rowData) {
77 | this.props.navigator.push({
78 | component:Chapter,
79 | params:{
80 | name:rowData.Title,
81 | id:rowData.Id,
82 | cover:rowData.FrontCover,
83 | src:rowData.Explain,
84 | Author:rowData.Author,
85 | }
86 | })
87 | }
88 | toLogin(){
89 | this.props.navigator.push({
90 | component:Login,
91 | name:Login,
92 | })
93 | }
94 |
95 | render() {
96 |
97 |
98 | let content = this.props.isLoggedIn?
99 |
100 | }
114 | />
115 | :
116 |
118 | 登陆
119 |
120 |
121 | return (
122 |
123 |
124 |
128 | {content}
129 |
130 |
131 | );
132 | }
133 | }
134 | const styles =StyleSheet.create({
135 | container: {
136 | flex: 1,
137 | backgroundColor: '#EEF0F3',
138 |
139 | },
140 | button:{
141 | backgroundColor:'#1a191f',
142 | height:50,
143 | width:80,
144 | marginTop:200,
145 | marginLeft:150,
146 | justifyContent:'center',
147 | alignItems:'center'
148 | },
149 | listview: {
150 | height: Dimensions.get('window').height
151 | },
152 |
153 | });
154 |
155 | function mapStateToProps(state){
156 | return {
157 | isLoggedIn: state.authReducer.isLoggedIn,
158 | status: state.authReducer.status,
159 | SubscribeList:state.subscribeReducer.SubscribeList,
160 | isLoading:state.subscribeReducer.isLoading,
161 | isLoadMore:state.subscribeReducer.isLoadMore,
162 | isRefresh:state.subscribeReducer.isRefresh,
163 | };
164 | }
165 | // function mapDispatchToProps(dispatch){
166 | // return {
167 | // // actions: bindActionCreators(actionCreators, dispatch)
168 | // //some action
169 | // };
170 | // }
171 | export default connect(mapStateToProps,{subscribeComic})(Subscribe);
172 |
--------------------------------------------------------------------------------
/ios/IShuHui.xcodeproj/xcshareddata/xcschemes/IShuHui-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/js/containers/Search.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Jonson on 16/6/5.
3 | */
4 | import React from 'react';
5 | import {
6 | StyleSheet,
7 | View,
8 | Text,
9 | TextInput,
10 | Image,
11 | Dimensions,
12 | ListView,
13 | TouchableOpacity,
14 | InteractionManager
15 | } from 'react-native';
16 |
17 | import {
18 | fetchRecommendedList,
19 | resetState,
20 | setupSearchText,
21 | } from '../actions/searchAction';
22 | import { connect } from 'react-redux';
23 | import SearchHeader from '../widget/SearchHeader';//头文件
24 | import Chapter from './Chapter/Chapter';
25 | var height = 40;
26 | class Search extends React.Component {
27 | constructor(props) {
28 | super(props);
29 | this.renderResultRow = this.renderResultRow.bind(this);
30 | console.log(this.props)
31 | this.state = {
32 | dataSource: new ListView.DataSource({
33 | rowHasChanged: (row1, row2) => row1 !== row2,
34 | })
35 | }
36 | }
37 |
38 | componentDidMount() {
39 |
40 | }
41 |
42 | componentWillUnmount() {
43 | this.props.resetState()
44 | }
45 |
46 | render() {
47 |
48 | return (
49 |
50 | {
54 |
55 | this.props.setupSearchText(text)
56 | this.props.fetchRecommendedList(text)
57 |
58 | } }
59 | onEndEditing={this.pushToResultPage.bind(this, this.props.searchText)}
60 | />
61 |
62 |
63 |
64 |
69 |
70 |
71 |
72 |
73 | )
74 | }
75 |
76 | pushToResultPage(keyword) {
77 | if (!keyword || !keyword.trim().length) {
78 | if (!keyword) {
79 | return;
80 | }
81 | alert('名称不能为空!');
82 | return;
83 | }
84 | // console.log(this.props);
85 | this.props.fetchRecommendedList(keyword);
86 | }
87 | pushSearchListRow( rowData, rowId){
88 | InteractionManager.runAfterInteractions(() => {
89 | this.props.navigator.push({
90 | name: 'Chapter',
91 | component: Chapter,
92 | params:{
93 | name:rowData.Title,
94 | id:rowData.Id,
95 | cover:rowData.FrontCover,
96 | src:rowData.Explain,
97 | Author:rowData.Author,
98 | }
99 | })
100 | })
101 | }
102 | renderResultRow(rowData, sectionId, rowId) {
103 | return
104 |
105 |
106 |
107 |
108 |
109 | {rowData.Title}
110 | 最新{rowData.LastChapter.ChapterNo}话:{rowData.LastChapter.Title}
111 |
112 | {rowData.SerializedState==='已完结' ? : }
114 |
115 | {rowData.Explain}
116 |
117 |
118 |
119 | }
120 | }
121 |
122 | const styles = StyleSheet.create({
123 |
124 | fonts: {
125 | fontSize: 18,
126 |
127 | },
128 | listitem: {
129 | flex:1,
130 | alignItems: "center",
131 | flexDirection: 'row',
132 | borderBottomColor: '#E6E6E6',
133 | borderBottomWidth: 0.5
134 | },
135 |
136 | itemImage: {
137 | margin: 10,
138 | width: 105,
139 | height: 70,
140 | borderRadius: 10
141 | },
142 | itemTitle: {
143 | flex: 1,
144 | justifyContent: 'flex-start',
145 | flexDirection: 'column',
146 | },
147 | hintImg: {
148 | width: 50,
149 | height: 50,
150 | },
151 | desc: {
152 | fontSize: 12,
153 | color:'#3C3C3C'
154 |
155 | },
156 | })
157 |
158 | export default Search = connect(
159 | (state) => {
160 | const { searchText,recomdData, } = state.searchReducer;
161 | return {
162 | searchText,
163 | recomdData,
164 | }
165 | },{
166 | fetchRecommendedList,
167 | resetState,
168 | setupSearchText,
169 | })(Search);
170 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation
19 | * entryFile: "index.android.js",
20 | *
21 | * // whether to bundle JS and assets in debug mode
22 | * bundleInDebug: false,
23 | *
24 | * // whether to bundle JS and assets in release mode
25 | * bundleInRelease: true,
26 | *
27 | * // whether to bundle JS and assets in another build variant (if configured).
28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
29 | * // The configuration property can be in the following formats
30 | * // 'bundleIn${productFlavor}${buildType}'
31 | * // 'bundleIn${buildType}'
32 | * // bundleInFreeDebug: true,
33 | * // bundleInPaidRelease: true,
34 | * // bundleInBeta: true,
35 | *
36 | * // the root of your project, i.e. where "package.json" lives
37 | * root: "../../",
38 | *
39 | * // where to put the JS bundle asset in debug mode
40 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
41 | *
42 | * // where to put the JS bundle asset in release mode
43 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
44 | *
45 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
46 | * // require('./image.png')), in debug mode
47 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
48 | *
49 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
50 | * // require('./image.png')), in release mode
51 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
52 | *
53 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
54 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
55 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
56 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
57 | * // for example, you might want to remove it from here.
58 | * inputExcludes: ["android/**", "ios/**"],
59 | *
60 | * // override which node gets called and with what additional arguments
61 | * nodeExecutableAndArgs: ["node"]
62 | *
63 | * // supply additional arguments to the packager
64 | * extraPackagerArgs: []
65 | * ]
66 | */
67 | project.ext.vectoricons = [
68 | iconFontNames: [ 'MaterialIcons.ttf', 'EvilIcons.ttf' ] // Name of the font files you want to copy
69 | ]
70 |
71 | apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
72 | apply from: "../../node_modules/react-native/react.gradle"
73 |
74 | /**
75 | * Set this to true to create two separate APKs instead of one:
76 | * - An APK that only works on ARM devices
77 | * - An APK that only works on x86 devices
78 | * The advantage is the size of the APK is reduced by about 4MB.
79 | * Upload all the APKs to the Play Store and people will download
80 | * the correct one based on the CPU architecture of their device.
81 | */
82 | def enableSeparateBuildPerCPUArchitecture = false
83 |
84 | /**
85 | * Run Proguard to shrink the Java bytecode in release builds.
86 | */
87 | def enableProguardInReleaseBuilds = false
88 |
89 | android {
90 | compileSdkVersion 23
91 | buildToolsVersion "23.0.1"
92 |
93 | defaultConfig {
94 | applicationId "com.ishuhui"
95 | minSdkVersion 16
96 | targetSdkVersion 22
97 | versionCode 1
98 | versionName "1.0"
99 | ndk {
100 | abiFilters "armeabi-v7a", "x86"
101 | }
102 | }
103 |
104 | splits {
105 | abi {
106 | reset()
107 | enable enableSeparateBuildPerCPUArchitecture
108 | universalApk false // If true, also generate a universal APK
109 | include "armeabi-v7a", "x86"
110 | }
111 | }
112 | buildTypes {
113 | release {
114 | minifyEnabled enableProguardInReleaseBuilds
115 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
116 | }
117 | }
118 | // applicationVariants are e.g. debug, release
119 | applicationVariants.all { variant ->
120 | variant.outputs.each { output ->
121 | // For each separate APK per architecture, set a unique version code as described here:
122 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
123 | def versionCodes = ["armeabi-v7a":1, "x86":2]
124 | def abi = output.getFilter(OutputFile.ABI)
125 | if (abi != null) { // null for the universal-debug, universal-release variants
126 | output.versionCodeOverride =
127 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
128 | }
129 | }
130 | }
131 | }
132 |
133 | dependencies {
134 | compile project(':react-native-cookie')
135 | compile project(':react-native-vector-icons')
136 | compile fileTree(dir: "libs", include: ["*.jar"])
137 | compile "com.android.support:appcompat-v7:23.0.1"
138 | compile "com.facebook.react:react-native:+" // From node_modules
139 | compile project(':react-native-vector-icons')
140 | }
141 |
142 | // Run this once to be able to run the application with BUCK
143 | // puts all compile dependencies into folder libs for BUCK to use
144 | task copyDownloadableDepsToLibs(type: Copy) {
145 | from configurations.compile
146 | into 'libs'
147 | }
148 |
--------------------------------------------------------------------------------
/js/containers/Chapter/Chapter.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {
3 | View,
4 | Text,
5 | Image,
6 | ListView,
7 | navigator,
8 | BackAndroid,
9 | TouchableHighlight,
10 | StyleSheet,
11 | RefreshControl,
12 | TouchableOpacity,
13 | InteractionManager,
14 | Dimensions,
15 | } from 'react-native';
16 | import NavigationBar from '../../widget/NavigationBar';
17 | import ViewUtils from '../../utils/ViewUtils';
18 | import { connect } from 'react-redux';
19 | import ParallaxView from 'react-native-parallax-view';
20 | import { chapter } from './../../actions/chapterAction';
21 | import { Tile } from 'react-native-elements';
22 | import Detail from '../Detail/Detail';
23 | import LoadingMoreFooter from '../../widget/LoadingMoreFooter';
24 |
25 | let isLoading = true;
26 | let isLoadMore = false;
27 | let isRefresh = false;
28 | let PageIndex = 0;
29 | let isFirstLoad = true;
30 | class Chapter extends Component {
31 |
32 | constructor(props) {
33 | super(props);
34 | this.state = {
35 | time:true,
36 | dataSource: new ListView.DataSource({
37 | rowHasChanged: (row1, row2) => row1 !== row2
38 | })
39 | }
40 |
41 | }
42 |
43 | componentDidMount() {
44 | InteractionManager.runAfterInteractions(() => {
45 |
46 | this.props.chapter(this.props.route.params.id ,PageIndex, isLoading, isLoadMore, isRefresh);
47 | });
48 | this.onRefresh();
49 | }
50 |
51 | render() {
52 |
53 | // _navigator = this.props.navigator;
54 | //
55 | // const {Chapter} = this.props;
56 | // let chapterList = Chapter.chapterList;
57 | // if (chapterList.length > 0) {
58 | // isFirstLoad = false;
59 | // }
60 |
61 | if (this.props.ChapterList) {
62 | isFirstLoad = false;
63 |
64 | //
70 | }
71 |
72 | return (
73 |
74 | this.onBack())}
76 | title={this.props.route.params.name}
77 | />
78 |
79 |
80 | }
96 | />
97 |
98 | //
99 | )
100 | }
101 | renderHeader() {
102 | return (
103 |
104 |
105 |
106 |
107 |
108 | { this.props.route.params.name}
109 | 作者:{ this.props.route.params.Author}
110 | 简介:{ this.props.route.params.src}
111 |
112 |
113 |
114 |
115 | )
116 | }
117 |
118 | onBack(){
119 | this.state.dataSource.cloneWithRows([]);
120 | this.props.navigator.pop();
121 | }
122 | renderRow(rowData, sectionId, rowId) {
123 | let Sort = rowData.ChapterType===0 ?
124 | {rowData.Sort}话:{rowData.Title} :
125 | {rowData.Sort}卷:{rowData.Title}
126 | return
127 |
128 |
129 |
130 |
131 |
132 | {rowData.Title}
133 | {Sort}
134 |
135 |
136 |
137 |
138 |
139 | }
140 |
141 | onRefresh() {
142 | isRefresh = true;
143 | isLoading = false;
144 | isLoadMore = false;
145 | PageIndex = 0;
146 | this.props.chapter(this.props.route.params.id,PageIndex, isLoading, isLoadMore, isRefresh);
147 | }
148 |
149 | onEndReach() {
150 |
151 | if (!isFirstLoad) {
152 | isLoadMore = true;
153 | isRefresh = false;
154 | isLoading = false;
155 | PageIndex = +PageIndex + 1;
156 | this.props.chapter(this.props.route.params.id,PageIndex, isLoading, isLoadMore, isRefresh);
157 | }
158 | }
159 |
160 | renderFooter() {
161 | //
162 | // if(this.props.isLoadMore
163 | // && this.props.ChapterList.length>0
164 | // ){
165 | // return(
166 | //
167 | // )}
168 | }
169 |
170 | onPressRow(rowData, rowId) {
171 | this.props.navigator.push({
172 | component:Detail,
173 | params:{
174 | name:rowData.Title,
175 | id:rowData.Id
176 | }
177 | })
178 | }
179 | _back() {
180 | this.props.navigator.pop();
181 | }
182 | }
183 | const styles = StyleSheet.create({
184 | container: {
185 | flex: 1,
186 | backgroundColor: '#F5FCFF',
187 | },
188 |
189 | contentContainer: {
190 | flex: 1
191 | },
192 | headerImg:{
193 | margin: 10,
194 | width: 150,
195 | height: 100,
196 | },
197 | header:{
198 | alignItems: "center",
199 | flexDirection: 'row',
200 | borderBottomColor:'#CCCCCC',
201 | backgroundColor:'#FFFFFF',
202 | borderBottomWidth: 1,
203 | marginBottom:20,
204 | },
205 | listview: {
206 | height: Dimensions.get('window').height
207 | },
208 |
209 | listitem: {
210 | alignItems: "center",
211 | flexDirection: 'row',
212 | borderBottomColor: '#E6E6E6',
213 | borderBottomWidth: 0.5
214 | },
215 |
216 | itemImage: {
217 | margin: 10,
218 | width: 105,
219 | height: 70,
220 | borderRadius: 10
221 | },
222 | itemTitle: {
223 | flex: 1,
224 | justifyContent: 'flex-start',
225 | flexDirection: 'column',
226 | },
227 | hintImg: {
228 | width: 50,
229 | height: 50,
230 | },
231 | desc: {
232 | fontSize: 12,
233 | color:'#3C3C3C'
234 |
235 | }
236 |
237 | });
238 |
239 |
240 | export default Chapter = connect(
241 | (state) => {
242 | const { ChapterList,isLoading,isLoadMore,isRefresh } = state.chapterReducer;
243 | return {
244 | ChapterList,
245 | isLoading,
246 | isLoadMore,
247 | isRefresh
248 | }
249 | },{chapter})(Chapter);
250 |
--------------------------------------------------------------------------------