├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── README.md ├── __tests__ ├── index.android.js └── index.ios.js ├── android ├── app │ ├── BUCK │ ├── build.gradle │ ├── gank.keystore │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── gank │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystores │ ├── BUCK │ └── debug.keystore.properties └── settings.gradle ├── app.js ├── app.json ├── index.android.js ├── index.ios.js ├── ios ├── gank-tvOS │ └── Info.plist ├── gank-tvOSTests │ └── Info.plist ├── gank.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── gank-tvOS.xcscheme │ │ └── gank.xcscheme ├── gank │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── gank1024.png │ │ │ ├── gank120-1.png │ │ │ ├── gank120.png │ │ │ ├── gank180.png │ │ │ ├── gank40.png │ │ │ ├── gank58.png │ │ │ ├── gank60.png │ │ │ ├── gank80.png │ │ │ └── gank87.png │ ├── Info.plist │ └── main.m └── gankTests │ ├── Info.plist │ └── gankTests.m ├── note.txt ├── package.json ├── screenshots ├── Android_AllView.gif ├── Android_GirlView.gif ├── Android_VideoView.gif ├── Android_iOSView.gif ├── ios_allView.gif ├── ios_androidview.gif ├── ios_girlview.gif └── ios_videoview.gif ├── src ├── assets │ └── img │ │ ├── APP.png │ │ ├── APP@1.5x.png │ │ ├── APP@2x.png │ │ ├── APP@3x.png │ │ ├── APP@4x.png │ │ ├── gank.png │ │ ├── gank@1.5x.png │ │ ├── gank@2x.png │ │ ├── gank@3x.png │ │ ├── gank@4x.png │ │ ├── icon_all.png │ │ ├── icon_all@1.5x.png │ │ ├── icon_all@2x.png │ │ ├── icon_all@3x.png │ │ ├── icon_all@4x.png │ │ ├── icon_android.png │ │ ├── icon_android@1.5x.png │ │ ├── icon_android@2x.png │ │ ├── icon_android@3x.png │ │ ├── icon_android@4x.png │ │ ├── icon_girl.png │ │ ├── icon_girl@1.5x.png │ │ ├── icon_girl@2x.png │ │ ├── icon_girl@3x.png │ │ ├── icon_ios.png │ │ ├── icon_ios@1.5.png │ │ ├── icon_ios@2x.png │ │ ├── icon_ios@3x.png │ │ ├── icon_ios@4x.png │ │ ├── icon_js.png │ │ ├── icon_js@1.5x.png │ │ ├── icon_js@2x.png │ │ ├── icon_js@3x.png │ │ ├── icon_me.png │ │ ├── icon_me@1.5x.png │ │ ├── icon_me@2x.png │ │ ├── icon_me@3x.png │ │ ├── icon_menu.png │ │ ├── icon_menu@1.5x.png │ │ ├── icon_menu@2x.png │ │ ├── icon_menu@3x.png │ │ ├── icon_menu@4x.png │ │ ├── icon_menu_right.png │ │ ├── icon_menu_right@1.5x.png │ │ ├── icon_menu_right@2x.png │ │ ├── icon_menu_right@3x.png │ │ ├── icon_menu_right@4x.png │ │ ├── icon_menu_right_ios.png │ │ ├── icon_menu_right_ios@1.5x.png │ │ ├── icon_menu_right_ios@2x.png │ │ ├── icon_menu_right_ios@3x.png │ │ ├── icon_menu_right_ios@4x.png │ │ ├── icon_search.png │ │ ├── icon_search@1.5x.png │ │ ├── icon_search@2x.png │ │ ├── icon_search@3x.png │ │ ├── icon_search@4x.png │ │ ├── icon_video.png │ │ ├── icon_video@1.5x.png │ │ ├── icon_video@2x.png │ │ ├── icon_video@3x.png │ │ ├── icon_video@4x.png │ │ ├── recommendation.png │ │ ├── recommendation@1.5x.png │ │ ├── recommendation@2x.png │ │ ├── recommendation@3x.png │ │ ├── recommendation@4x.png │ │ ├── resource.png │ │ ├── resource@1.5x.png │ │ ├── resource@2x.png │ │ ├── resource@3x.png │ │ └── resource@4x.png ├── components │ ├── MyItem.js │ ├── ProgressComponent.js │ ├── RightMenu.js │ └── TitleBar.js ├── navigators │ ├── CustomDrawerContentComponent.js │ └── Index.js ├── redux │ ├── actions │ │ ├── AllAction.js │ │ ├── AndroidAction.js │ │ ├── AppAction.js │ │ ├── GirlAction.js │ │ ├── IosAction.js │ │ ├── JsAction.js │ │ ├── QueryAction.js │ │ ├── RecommendationAction.js │ │ ├── ResourcesAction.js │ │ ├── Types.js │ │ └── VideoAction.js │ ├── reducers │ │ ├── AllReducer.js │ │ ├── AndroidReducer.js │ │ ├── AppReducer.js │ │ ├── GirlReducer.js │ │ ├── Index.js │ │ ├── IosReducer.js │ │ ├── JsReducer.js │ │ ├── QueryReducer.js │ │ ├── RecomendationReducer.js │ │ ├── ResourcesReducer.js │ │ └── VideoReducer.js │ └── store │ │ └── Index.js ├── styles │ └── Common.js ├── utils │ ├── ClickUtil.js │ ├── Constants.js │ ├── ToastUtils.js │ └── Utils.js └── views │ ├── AllView.js │ ├── AndroidView.js │ ├── AppView.js │ ├── DetailsView.js │ ├── GirlDetailView.js │ ├── GirlView.js │ ├── IosView.js │ ├── JavascriptView.js │ ├── MeView.js │ ├── RecommendationView.js │ ├── ResourcesView.js │ ├── SearchView.js │ └── VideoView.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.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 | munge_underscores=true 30 | 31 | 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' 32 | 33 | suppress_type=$FlowIssue 34 | suppress_type=$FlowFixMe 35 | suppress_type=$FixMe 36 | 37 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 38 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 41 | 42 | unsafe.enable_getters_and_setters=true 43 | 44 | [version] 45 | ^0.49.1 46 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.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 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | #*.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gank 2 | 3 | 4 | #### iOS Preview: 5 | - 6 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/ios_allView.gif) 7 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/ios_androidview.gif) 8 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/ios_girlview.gif) 9 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/ios_videoview.gif) 10 | 11 | #### Android Preview: 12 | - 13 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/Android_AllView.gif) 14 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/Android_iOSView.gif) 15 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/Android_GirlView.gif) 16 | ![Image iOS](https://github.com/serge66/gank/blob/master/screenshots/Android_VideoView.gif) 17 | 18 | #### Support 19 | 20 | Support iOS & Android 21 | Support drop down refresh & pull up to loading 22 | 23 | #### The techniques used 24 | 25 | redux 26 | react-redux 27 | redux-thunk 28 | react-native-progress 29 | react-native-image-progress 30 | react-native-root-toast 31 | react-navigation 32 | teaset 33 | react-native-fast-image 34 | 35 | #### Getting started 36 | 37 | 38 | Installation 39 | git clone https://github.com/serge66/gank.git 40 | cd gank 41 | npm install 42 | 43 | Run 44 | react-native run-ios 45 | react-native run-android 46 | 47 | #### Android App 48 | 49 | https://www.pgyer.com/gank_io 50 | 51 | #### Link 52 | 53 | CSDN: http://blog.csdn.net/vitamio/article/details/78706959 54 | 55 | #### Thanks 56 | 57 | Thanks for Gank.io providing this API 58 | 59 | #### License 60 | 61 | Copyright (C) 2017 serge66 62 | 63 | Licensed under the Apache License, Version 2.0 (the "License"); 64 | you may not use this file except in compliance with the License. 65 | You may obtain a copy of the License at 66 | 67 | http://www.apache.org/licenses/LICENSE-2.0 68 | 69 | Unless required by applicable law or agreed to in writing, software 70 | distributed under the License is distributed on an "AS IS" BASIS, 71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 72 | See the License for the specific language governing permissions and 73 | limitations under the License. -------------------------------------------------------------------------------- /__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 | -------------------------------------------------------------------------------- /__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 | -------------------------------------------------------------------------------- /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.gank", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.gank", 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 | -------------------------------------------------------------------------------- /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 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | apply from: "../../node_modules/react-native/react.gradle" 76 | 77 | /** 78 | * Set this to true to create two separate APKs instead of one: 79 | * - An APK that only works on ARM devices 80 | * - An APK that only works on x86 devices 81 | * The advantage is the size of the APK is reduced by about 4MB. 82 | * Upload all the APKs to the Play Store and people will download 83 | * the correct one based on the CPU architecture of their device. 84 | */ 85 | def enableSeparateBuildPerCPUArchitecture = false 86 | 87 | /** 88 | * Run Proguard to shrink the Java bytecode in release builds. 89 | */ 90 | def enableProguardInReleaseBuilds = false 91 | 92 | android { 93 | compileSdkVersion 23 94 | buildToolsVersion "23.0.1" 95 | 96 | defaultConfig { 97 | applicationId "com.gank" 98 | minSdkVersion 16 99 | targetSdkVersion 22 100 | versionCode 2 101 | versionName "1.1" 102 | ndk { 103 | abiFilters "armeabi-v7a", "x86" 104 | } 105 | } 106 | signingConfigs { 107 | release { 108 | storeFile file(MYAPP_RELEASE_STORE_FILE) 109 | storePassword MYAPP_RELEASE_STORE_PASSWORD 110 | keyAlias MYAPP_RELEASE_KEY_ALIAS 111 | keyPassword MYAPP_RELEASE_KEY_PASSWORD 112 | } 113 | } 114 | splits { 115 | abi { 116 | reset() 117 | enable enableSeparateBuildPerCPUArchitecture 118 | universalApk false // If true, also generate a universal APK 119 | include "armeabi-v7a", "x86" 120 | } 121 | } 122 | buildTypes { 123 | release { 124 | signingConfig signingConfigs.release 125 | minifyEnabled enableProguardInReleaseBuilds 126 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 127 | } 128 | } 129 | // applicationVariants are e.g. debug, release 130 | applicationVariants.all { variant -> 131 | variant.outputs.each { output -> 132 | // For each separate APK per architecture, set a unique version code as described here: 133 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 134 | def versionCodes = ["armeabi-v7a":1, "x86":2] 135 | def abi = output.getFilter(OutputFile.ABI) 136 | if (abi != null) { // null for the universal-debug, universal-release variants 137 | output.versionCodeOverride = 138 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 139 | } 140 | } 141 | } 142 | } 143 | 144 | dependencies { 145 | compile project(':react-native-fast-image') 146 | compile fileTree(dir: "libs", include: ["*.jar"]) 147 | compile "com.android.support:appcompat-v7:23.0.1" 148 | compile "com.facebook.react:react-native:+" // From node_modules 149 | } 150 | 151 | // Run this once to be able to run the application with BUCK 152 | // puts all compile dependencies into folder libs for BUCK to use 153 | task copyDownloadableDepsToLibs(type: Copy) { 154 | from configurations.compile 155 | into 'libs' 156 | } 157 | -------------------------------------------------------------------------------- /android/app/gank.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/android/app/gank.keystore -------------------------------------------------------------------------------- /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 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/gank/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.gank; 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 "gank"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/gank/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.gank; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.dylanvann.fastimage.FastImageViewPackage; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.shell.MainReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage(), 27 | new FastImageViewPackage() 28 | ); 29 | } 30 | }; 31 | 32 | @Override 33 | public ReactNativeHost getReactNativeHost() { 34 | return mReactNativeHost; 35 | } 36 | 37 | @Override 38 | public void onCreate() { 39 | super.onCreate(); 40 | SoLoader.init(this, /* native exopackage */ false); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Gank 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | MYAPP_RELEASE_STORE_FILE=gank.keystore 22 | MYAPP_RELEASE_KEY_ALIAS=gank 23 | MYAPP_RELEASE_STORE_PASSWORD=gank123 24 | MYAPP_RELEASE_KEY_PASSWORD=gank123 -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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-3.3-all.zip 6 | -------------------------------------------------------------------------------- /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/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/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /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/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'gank' 2 | include ':react-native-fast-image' 3 | project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, {Component} from 'react'; 4 | import {Provider} from 'react-redux'; 5 | import configureStore from './src/redux/store/Index'; 6 | import './src/utils/Constants'; 7 | import {AppRegistry, StyleSheet, Text, View} from 'react-native'; 8 | import Navigator from './src/navigators/Index'; 9 | 10 | const store = configureStore() 11 | class Root extends Component { 12 | render() { 13 | return ( 14 | 15 | 16 | 17 | ) 18 | } 19 | } 20 | 21 | AppRegistry.registerComponent('gank', () => Root); -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gank", 3 | "displayName": "gank" 4 | } -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | import './app'; -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | import './app'; -------------------------------------------------------------------------------- /ios/gank-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 | -------------------------------------------------------------------------------- /ios/gank-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 | -------------------------------------------------------------------------------- /ios/gank.xcodeproj/xcshareddata/xcschemes/gank-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 | -------------------------------------------------------------------------------- /ios/gank.xcodeproj/xcshareddata/xcschemes/gank.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 | -------------------------------------------------------------------------------- /ios/gank/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/gank/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 | #import "RCTLinkingManager.h"//增加用于打开Email lsj 2017-11-11 17:43:48 http://blog.csdn.net/liu__520/article/details/52851609 12 | #import 13 | #import 14 | 15 | @implementation AppDelegate 16 | 17 | //增加用于打开Email lsj 2017-11-11 17:43:48 18 | -(BOOL)application:(UIApplication*)application 19 | openURL:(NSURL*)url 20 | sourceApplication:(NSString*)sourceApplication 21 | annotation:(id)annotation{ 22 | return[RCTLinkingManager 23 | application:application 24 | openURL:url 25 | sourceApplication:sourceApplication 26 | annotation:annotation];} 27 | //Only if your app is using [Universal Links] 28 | //(https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html). 29 | -(BOOL)application:(UIApplication*)application 30 | continueUserActivity:(NSUserActivity*)userActivity 31 | restorationHandler:(void(^)(NSArray* 32 | _Nullable))restorationHandler{ 33 | return[RCTLinkingManager 34 | application:application 35 | continueUserActivity:userActivity 36 | restorationHandler:restorationHandler]; 37 | } 38 | 39 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 40 | { 41 | NSURL *jsCodeLocation; 42 | 43 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 44 | 45 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 46 | moduleName:@"gank" 47 | initialProperties:nil 48 | launchOptions:launchOptions]; 49 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 50 | 51 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 52 | UIViewController *rootViewController = [UIViewController new]; 53 | rootViewController.view = rootView; 54 | self.window.rootViewController = rootViewController; 55 | [self.window makeKeyAndVisible]; 56 | return YES; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ios/gank/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 | -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "gank40.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "gank60.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "gank58.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "gank87.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "gank80.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "gank120-1.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "gank120.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "gank180.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "1024x1024", 53 | "idiom" : "ios-marketing", 54 | "filename" : "gank1024.png", 55 | "scale" : "1x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank1024.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank120-1.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank120.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank180.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank40.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank58.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank60.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank80.png -------------------------------------------------------------------------------- /ios/gank/Images.xcassets/AppIcon.appiconset/gank87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/ios/gank/Images.xcassets/AppIcon.appiconset/gank87.png -------------------------------------------------------------------------------- /ios/gank/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Gank 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 | LSApplicationCategoryType 26 | 27 | LSRequiresIPhoneOS 28 | 29 | NSAppTransportSecurity 30 | 31 | NSAllowsArbitraryLoads 32 | 33 | 34 | NSLocationWhenInUseUsageDescription 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UIViewControllerBasedStatusBarAppearance 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/gank/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 | -------------------------------------------------------------------------------- /ios/gankTests/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/gankTests/gankTests.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 gankTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation gankTests 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 = [[[RCTSharedApplication() 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 | -------------------------------------------------------------------------------- /note.txt: -------------------------------------------------------------------------------- 1 | 2 | 一 react-native-root-siblings 在RN 0.48.1中存在不兼容 3 | 临时解决方案: 4 | 修改node_modules/react-native-root-siblings/lib/AppRegistryInjection.js 中的第4行 5 | 改为import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter'; 6 | RN的这个版本移动了emitter包的位置 7 | 或者: 8 | 在package包中修改为"react-native-root-toast": "github:pilipa-cn/react-native-root-toast#v2.2.0". 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gank", 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 | "react": "16.0.0-alpha.12", 11 | "react-native": "0.48.2", 12 | "react-native-fast-image": "^2.0.1", 13 | "react-native-image-progress": "^1.0.1", 14 | "react-native-root-toast": "^1.3.0", 15 | "react-navigation": "^1.0.0-beta.11", 16 | "react-redux": "^5.0.6", 17 | "redux": "^3.7.2", 18 | "redux-persist": "^4.9.1", 19 | "redux-thunk": "^2.2.0", 20 | "teaset": "^0.4.9" 21 | }, 22 | "devDependencies": { 23 | "babel-jest": "21.0.0", 24 | "babel-preset-react-native": "3.0.2", 25 | "jest": "21.0.1", 26 | "react-test-renderer": "16.0.0-alpha.12" 27 | }, 28 | "jest": { 29 | "preset": "react-native" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /screenshots/Android_AllView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/Android_AllView.gif -------------------------------------------------------------------------------- /screenshots/Android_GirlView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/Android_GirlView.gif -------------------------------------------------------------------------------- /screenshots/Android_VideoView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/Android_VideoView.gif -------------------------------------------------------------------------------- /screenshots/Android_iOSView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/Android_iOSView.gif -------------------------------------------------------------------------------- /screenshots/ios_allView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/ios_allView.gif -------------------------------------------------------------------------------- /screenshots/ios_androidview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/ios_androidview.gif -------------------------------------------------------------------------------- /screenshots/ios_girlview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/ios_girlview.gif -------------------------------------------------------------------------------- /screenshots/ios_videoview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/screenshots/ios_videoview.gif -------------------------------------------------------------------------------- /src/assets/img/APP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/APP.png -------------------------------------------------------------------------------- /src/assets/img/APP@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/APP@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/APP@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/APP@2x.png -------------------------------------------------------------------------------- /src/assets/img/APP@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/APP@3x.png -------------------------------------------------------------------------------- /src/assets/img/APP@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/APP@4x.png -------------------------------------------------------------------------------- /src/assets/img/gank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/gank.png -------------------------------------------------------------------------------- /src/assets/img/gank@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/gank@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/gank@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/gank@2x.png -------------------------------------------------------------------------------- /src/assets/img/gank@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/gank@3x.png -------------------------------------------------------------------------------- /src/assets/img/gank@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/gank@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_all.png -------------------------------------------------------------------------------- /src/assets/img/icon_all@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_all@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_all@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_all@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_all@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_all@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_all@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_all@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_android.png -------------------------------------------------------------------------------- /src/assets/img/icon_android@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_android@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_android@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_android@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_android@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_android@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_android@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_android@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_girl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_girl.png -------------------------------------------------------------------------------- /src/assets/img/icon_girl@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_girl@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_girl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_girl@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_girl@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_girl@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_ios.png -------------------------------------------------------------------------------- /src/assets/img/icon_ios@1.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_ios@1.5.png -------------------------------------------------------------------------------- /src/assets/img/icon_ios@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_ios@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_ios@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_ios@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_ios@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_ios@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_js.png -------------------------------------------------------------------------------- /src/assets/img/icon_js@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_js@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_js@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_js@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_js@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_js@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_me.png -------------------------------------------------------------------------------- /src/assets/img/icon_me@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_me@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_me@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_me@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_me@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_me@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right_ios.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right_ios@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right_ios@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right_ios@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right_ios@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right_ios@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right_ios@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_menu_right_ios@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_menu_right_ios@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_search.png -------------------------------------------------------------------------------- /src/assets/img/icon_search@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_search@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_search@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_search@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_search@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_search@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_search@4x.png -------------------------------------------------------------------------------- /src/assets/img/icon_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_video.png -------------------------------------------------------------------------------- /src/assets/img/icon_video@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_video@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/icon_video@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_video@2x.png -------------------------------------------------------------------------------- /src/assets/img/icon_video@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_video@3x.png -------------------------------------------------------------------------------- /src/assets/img/icon_video@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/icon_video@4x.png -------------------------------------------------------------------------------- /src/assets/img/recommendation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/recommendation.png -------------------------------------------------------------------------------- /src/assets/img/recommendation@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/recommendation@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/recommendation@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/recommendation@2x.png -------------------------------------------------------------------------------- /src/assets/img/recommendation@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/recommendation@3x.png -------------------------------------------------------------------------------- /src/assets/img/recommendation@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/recommendation@4x.png -------------------------------------------------------------------------------- /src/assets/img/resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/resource.png -------------------------------------------------------------------------------- /src/assets/img/resource@1.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/resource@1.5x.png -------------------------------------------------------------------------------- /src/assets/img/resource@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/resource@2x.png -------------------------------------------------------------------------------- /src/assets/img/resource@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/resource@3x.png -------------------------------------------------------------------------------- /src/assets/img/resource@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serge66/gank/d67d65578b1d488e10235b841097c0c62100deb6/src/assets/img/resource@4x.png -------------------------------------------------------------------------------- /src/components/ProgressComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | StyleSheet, 5 | View, 6 | Text, 7 | Modal, 8 | ActivityIndicator 9 | } from 'react-native'; 10 | 11 | const ANIMATION = ['none', 'slide', 'fade']; 12 | const SIZES = ['small', 'normal', 'large']; 13 | 14 | 15 | export default class ProgressComponent extends React.Component { 16 | 17 | constructor(props) { 18 | super(props); 19 | this.state = {visible: this.props.visible, textContent: this.props.textContent}; 20 | } 21 | 22 | static propTypes = { 23 | visible: PropTypes.bool, 24 | cancelable: PropTypes.bool, 25 | textContent: PropTypes.string, 26 | animation: PropTypes.oneOf(ANIMATION), 27 | color: PropTypes.string, 28 | size: PropTypes.oneOf(SIZES), 29 | overlayColor: PropTypes.string 30 | }; 31 | 32 | static defaultProps = { 33 | visible: false, 34 | cancelable: true, 35 | textContent: '', 36 | animation: 'none', 37 | color: 'white', 38 | size: 'large', // 'normal', 39 | overlayColor: 'rgba(0, 0, 0, 0.25)' 40 | }; 41 | 42 | close() { 43 | this.setState({visible: false}); 44 | } 45 | 46 | componentWillReceiveProps(nextProps) { 47 | const {visible, textContent} = nextProps; 48 | this.setState({visible, textContent}); 49 | } 50 | 51 | _handleOnRequestClose() { 52 | if (this.props.cancelable) { 53 | this.close(); 54 | } 55 | } 56 | 57 | _renderDefaultContent() { 58 | return ( 59 | 60 | 65 | 66 | {this.state.textContent} 67 | 68 | ); 69 | } 70 | 71 | _renderSpinner() { 72 | const {visible} = this.state; 73 | 74 | if (!visible) 75 | return null; 76 | 77 | const spinner = ( 78 | 82 | {this.props.children ? this.props.children : this._renderDefaultContent()} 83 | 84 | ); 85 | 86 | return ( 87 | this._handleOnRequestClose()} 90 | supportedOrientations={['landscape', 'portrait']} 91 | transparent 92 | visible={visible}> 93 | {spinner} 94 | 95 | ); 96 | 97 | } 98 | 99 | render() { 100 | return this._renderSpinner(); 101 | } 102 | 103 | } 104 | const styles = StyleSheet.create({ 105 | container: { 106 | flex: 1, 107 | backgroundColor: 'transparent', 108 | position: 'absolute', 109 | top: 0, 110 | bottom: 0, 111 | left: 0, 112 | right: 0 113 | }, 114 | background: { 115 | position: 'absolute', 116 | top: 0, 117 | bottom: 0, 118 | left: 0, 119 | right: 0, 120 | justifyContent: 'center', 121 | alignItems: 'center' 122 | }, 123 | textContainer: { 124 | flex: 1, 125 | top: 0, 126 | bottom: 0, 127 | left: 0, 128 | right: 0, 129 | justifyContent: 'center', 130 | alignItems: 'center', 131 | position: 'absolute' 132 | }, 133 | textContent: { 134 | top: 80, 135 | height: 50, 136 | fontSize: 20, 137 | fontWeight: 'bold' 138 | } 139 | }); 140 | 141 | /* 142 | *使用: 143 | * import ProgressComponent from '../xxxxxxxxx/components/ProgressComponent'; 144 | * 145 | * 146 | * 详情请看:https://github.com/joinspontaneous/react-native-loading-spinner-overlay 147 | * */ -------------------------------------------------------------------------------- /src/components/RightMenu.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, {Component} from 'react'; 4 | import { 5 | StyleSheet, 6 | Text, 7 | View, 8 | TouchableOpacity, 9 | Image, 10 | Platform, 11 | Clipboard, Linking, 12 | } from 'react-native'; 13 | import Utils from '../utils/Utils'; 14 | import CommonStyles from '../styles/Common'; 15 | import Menu from 'teaset/components/Menu/Menu'; 16 | 17 | export default class RightMenu extends React.Component { 18 | 19 | // getDefaultProps() { 20 | // return { 21 | // propsPara: null, 22 | // } 23 | // }, 24 | 25 | static defaultProps = { 26 | propsPara: null, 27 | 28 | } 29 | 30 | _search() { 31 | this.props.propsPara('Search'); // open drawer 32 | 33 | } 34 | _copyURL(url){ 35 | Clipboard.setString(url); 36 | } 37 | _openBrowser(url){ 38 | Linking.canOpenURL(url).then(supported => { 39 | if (!supported) { 40 | console.log('Can\'t handle url: ' + url); 41 | } else { 42 | return Linking.openURL(url); 43 | } 44 | }).catch(err => console.error('An error occurred', err)); 45 | } 46 | 47 | show(view, align) { 48 | view.measureInWindow((x, y, width, height) => { 49 | let items = [ 50 | {title: 'Copy URL', onPress: () => this._copyURL(this.props.url)}, 51 | {title: 'Open Browser', onPress: () => this._openBrowser(this.props.url)}, 52 | ]; 53 | Menu.show({x, y, width, height}, items, {align}); 54 | }); 55 | } 56 | 57 | 58 | _iosComponent() { 59 | return ( 60 | this.show(this.refs['touchable'], 'end')} 63 | style={[styles.container]}> 64 | 68 | 69 | ) 70 | } 71 | 72 | _androidComponent() { 73 | return ( 74 | this.show(this.refs['touchable'], 'end')} 77 | style={[styles.container]}> 78 | 82 | 83 | ) 84 | } 85 | 86 | render() { 87 | 88 | if (Platform.OS == 'ios') { 89 | return this._iosComponent(); 90 | } 91 | return this._androidComponent(); 92 | } 93 | } 94 | 95 | const styles = StyleSheet.create({ 96 | container: { 97 | flex: 1, 98 | // width: Utils.getWidth(50), 99 | // height: Utils.getHeight(30), 100 | flexDirection: 'row', 101 | justifyContent: 'center', 102 | alignItems: 'center', 103 | }, 104 | img: { 105 | width: Utils.getWidth(50), 106 | height: Utils.getHeight(30), 107 | margin: 1, 108 | }, 109 | text: { 110 | textAlign: 'center', 111 | color: '#333333', 112 | fontSize: Utils.getWidth(17), 113 | }, 114 | }); 115 | -------------------------------------------------------------------------------- /src/components/TitleBar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, {Component} from 'react'; 4 | import { 5 | StyleSheet, 6 | Text, 7 | View, 8 | TouchableOpacity, 9 | Image 10 | } from 'react-native'; 11 | import Utils from '../utils/Utils'; 12 | import CommonStyles from '../styles/Common'; 13 | 14 | 15 | export default class TitleBar extends React.Component { 16 | 17 | // getDefaultProps() { 18 | // return { 19 | // propsPara: null, 20 | // } 21 | // }, 22 | static defaultProps = { 23 | propsPara: null, 24 | } 25 | 26 | _search() { 27 | this.props.propsPara('Search'); // open drawer 28 | 29 | } 30 | 31 | _menu() { 32 | this.props.propsPara('DrawerOpen'); // open drawer 33 | // this.props.navigation.navigate('DrawerClose'); // close drawer 34 | } 35 | 36 | 37 | componentWillMount() { 38 | 39 | } 40 | 41 | render() { 42 | return ( 43 | 44 | 45 | this._menu()}> 48 | 51 | 52 | 53 | 54 | {this.props.title} 55 | 56 | 57 | this._search()}> 60 | 63 | 64 | 65 | 66 | ); 67 | } 68 | } 69 | 70 | const styles = StyleSheet.create({ 71 | container: { 72 | // flex: 1, 73 | width: Utils.size.width, 74 | height: Utils.getHeight(60), 75 | flexDirection: 'row', 76 | justifyContent: 'space-between', 77 | alignItems: 'center', 78 | backgroundColor: '#ffffff', 79 | paddingLeft: Utils.getWidth(15), 80 | paddingRight: Utils.getWidth(15), 81 | borderBottomWidth: Utils.getHeight(1), 82 | borderBottomColor: '#cccccc', 83 | paddingTop: Utils.getHeight(20), 84 | }, 85 | img: { 86 | width: Utils.getWidth(20), 87 | height: Utils.getHeight(20), 88 | margin: 1, 89 | }, 90 | text: { 91 | textAlign: 'center', 92 | color: '#333333', 93 | fontSize: Utils.getWidth(17), 94 | }, 95 | }); 96 | -------------------------------------------------------------------------------- /src/navigators/CustomDrawerContentComponent.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import React from 'react'; 3 | import {DrawerItems} from 'react-navigation'; 4 | import { 5 | StyleSheet, 6 | View, 7 | Text, 8 | ScrollView, 9 | TouchableOpacity 10 | } from 'react-native'; 11 | import {connect} from "react-redux"; 12 | import Utils from "../utils/Utils"; 13 | import CommonStyles from '../styles/Common'; 14 | import FastImage from 'react-native-fast-image'; 15 | 16 | class CustomDrawerContentComponent extends React.Component { 17 | 18 | render() { 19 | console.log('抽屉 加载图片 start') 20 | console.log(this) 21 | 22 | if (this && this.props && this.props.all && this.props.all.data) { 23 | console.log('抽屉 加载图片 doing') 24 | 25 | for (let index in this.props.all.data) { 26 | if ('福利' == this.props.all.data[index].type) { 27 | console.log('抽屉 加载图片 success ' + this.props.all.data[index].url) 28 | return ( 29 | 30 | 31 | 32 | 33 | { 36 | this.props.items.navigation 37 | .navigate('GirlDetail', 38 | { 39 | title: this.props.all.data[index].desc, 40 | url: this.props.all.data[index].url 41 | }); 42 | }}> 43 | Gank 44 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ) 58 | } 59 | } 60 | } 61 | return ( 62 | 63 | 64 | 65 | 66 | { 69 | // this.props.items.navigation.navigate('All'); 70 | }}> 71 | Gank 72 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ) 83 | } 84 | }; 85 | 86 | const styles = StyleSheet.create({ 87 | container: { 88 | flex: 1, 89 | }, 90 | touchable: { 91 | // flex: 1, 92 | justifyContent: 'center', 93 | alignItems: 'center', 94 | // height:'100%', 95 | width: '100%', 96 | 97 | }, 98 | tabImg: { 99 | alignItems: 'center', 100 | justifyContent: 'center', 101 | }, 102 | img: { 103 | height: Utils.getHeight(200), 104 | width: Utils.getWidth(200), 105 | }, 106 | titleMsg: { 107 | textAlign: 'center', 108 | color: '#000', 109 | // paddingTop: 30, 110 | paddingBottom: Utils.getHeight(5), 111 | fontSize: Utils.getFontSize(20) 112 | } 113 | }); 114 | 115 | //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。 116 | function mapStateToProps(state) { 117 | const {all} = state; 118 | return {all} 119 | } 120 | 121 | export default connect(mapStateToProps)(CustomDrawerContentComponent); -------------------------------------------------------------------------------- /src/navigators/Index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import React from 'react'; 3 | import {StackNavigator, DrawerItems, DrawerNavigator} from "react-navigation"; 4 | import { 5 | Text, 6 | Image, 7 | View, 8 | StyleSheet 9 | } from 'react-native'; 10 | import All from '../views/AllView'; 11 | import Android from '../views/AndroidView'; 12 | import Ios from '../views/IosView'; 13 | import Javascript from '../views/JavascriptView'; 14 | import GirlView from '../views/GirlView'; 15 | import Utils from '../utils/Utils'; 16 | import CustomDrawerContentComponent from './CustomDrawerContentComponent'; 17 | import SearchView from "../views/SearchView"; 18 | import DetailsView from "../views/DetailsView"; 19 | import GirlDetailView from '../views/GirlDetailView'; 20 | import VideoView from '../views/VideoView'; 21 | import MeView from '../views/MeView'; 22 | import Recommendation from '../views/RecommendationView'; 23 | import Resources from '../views/ResourcesView'; 24 | import AppView from '../views/AppView'; 25 | 26 | const MyApp = DrawerNavigator({ 27 | All: { 28 | screen: All, 29 | navigationOptions: { 30 | drawerLabel: 'All', 31 | drawerIcon: ({tintColor}) => ( 32 | 35 | ), 36 | }, 37 | 38 | }, 39 | Android: { 40 | screen: Android, 41 | navigationOptions: { 42 | drawerLabel: 'Android', 43 | drawerIcon: ({tintColor}) => ( 44 | 47 | ), 48 | }, 49 | }, 50 | iOS: { 51 | screen: Ios, 52 | navigationOptions: { 53 | drawerLabel: 'iOS', 54 | drawerIcon: ({tintColor}) => ( 55 | 58 | ), 59 | }, 60 | }, 61 | JS: { 62 | screen: Javascript, 63 | navigationOptions: { 64 | drawerLabel: 'JS', 65 | drawerIcon: ({tintColor}) => ( 66 | 69 | ), 70 | }, 71 | }, 72 | Girl: { 73 | screen: GirlView, 74 | navigationOptions: { 75 | drawerLabel: 'Girl', 76 | drawerIcon: ({tintColor}) => ( 77 | 80 | ), 81 | }, 82 | }, 83 | Video: { 84 | screen: VideoView, 85 | navigationOptions: { 86 | drawerLabel: 'Video', 87 | drawerIcon: ({tintColor}) => ( 88 | 89 | 93 | 94 | ), 95 | }, 96 | }, 97 | Recommendation: { 98 | screen: Recommendation, 99 | navigationOptions: { 100 | drawerLabel: 'Recommendation', 101 | drawerIcon: ({tintColor}) => ( 102 | 105 | ), 106 | }, 107 | }, 108 | Resources: { 109 | screen: Resources, 110 | navigationOptions: { 111 | drawerLabel: 'Resources', 112 | drawerIcon: ({tintColor}) => ( 113 | 116 | ), 117 | }, 118 | }, 119 | AppView: { 120 | screen: AppView, 121 | navigationOptions: { 122 | drawerLabel: 'App', 123 | drawerIcon: ({tintColor}) => ( 124 | 127 | ), 128 | }, 129 | }, 130 | About: { 131 | screen: MeView, 132 | navigationOptions: { 133 | drawerLabel: 'About', 134 | drawerIcon: ({tintColor}) => ( 135 | 138 | ), 139 | }, 140 | }, 141 | }, { 142 | drawerWidth: Utils.size.width / 4 * 3, 143 | drawerPosition: 'left', 144 | backBehavior: 'initialRoute', 145 | contentComponent: props => (), 146 | contentOptions: { 147 | initialRouteName: 'All', 148 | activeItemKey: 'All',//识别活动路线的钥匙 149 | labelStyle: { 150 | height: Utils.getHeight(20), 151 | fontSize:Utils.getWidth(15), 152 | textAlign:'center', 153 | 154 | }, 155 | activeTintColor: 'white', 156 | activeBackgroundColor: '#cccccc', 157 | inactiveTintColor: '#000', 158 | inactiveBackgroundColor: '#fff', 159 | style: { 160 | marginVertical: 0, 161 | // alignItems:'flex-start', 162 | justifyContent:'center', 163 | }, 164 | } 165 | }); 166 | 167 | const App = StackNavigator({ 168 | Root: { 169 | screen: MyApp, 170 | navigationOptions: { 171 | header:null, 172 | }, 173 | }, 174 | Search:{ 175 | screen:SearchView, 176 | 177 | }, 178 | Details:{ 179 | screen:DetailsView, 180 | // navigationOptions:{ 181 | // headerTitle:'Details' 182 | // } 183 | }, 184 | GirlDetail:{ 185 | screen:GirlDetailView, 186 | }, 187 | }); 188 | 189 | const styles = StyleSheet.create({ 190 | container: { 191 | flex: 1, 192 | justifyContent: 'center', 193 | alignItems: 'center', 194 | backgroundColor: '#ffffff', 195 | }, 196 | icon: { 197 | // padding: 8, 198 | width:Utils.getWidth(30), 199 | height:Utils.getHeight(30), 200 | }, 201 | }); 202 | 203 | export default App; -------------------------------------------------------------------------------- /src/redux/actions/AllAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/15/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.all.ALL_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.all.ALL_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.all.ALL_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/AndroidAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.android.ANDROD_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.android.ANDROD_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.android.ANDROD_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/AppAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.app.APP_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.app.APP_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.app.APP_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/GirlAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/10' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.girl.GIRL_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.girl.GIRL_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.girl.GIRL_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/IosAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.ios.IOS_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.ios.IOS_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.ios.IOS_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/JsAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.js.JS_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.js.JS_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.js.JS_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/QueryAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/search/query/' + opt.content 12 | + '/category/all/count/20/page/' + opt.num, { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | if (responseJson.results == null || responseJson.results.length == 0) { 48 | ToastUtils.show('no data!') 49 | } 50 | mTotalData = mTotalData.concat(responseJson.results); 51 | mDispatch(query_done(mTotalData)); 52 | console.log('mTotalData 22222:-----'); 53 | console.log(mTotalData); 54 | } else { 55 | ToastUtils.show("网络连接失败,请重连后重试"); 56 | mDispatch(query_error(mTotalData)); 57 | } 58 | } 59 | 60 | function _catch(error) { 61 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 62 | mDispatch(query_error(mTotalData)); 63 | } 64 | 65 | export function doDoing(opt) { 66 | return (dispatch) => { 67 | mDispatch = dispatch; 68 | if (opt.isRefreshing) { 69 | mTotalData = []; 70 | } 71 | dispatch(query_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 72 | 73 | let result = fetch(_requestObj(opt)) 74 | .then(_status) 75 | .then(_json) 76 | .then(_parseJson) 77 | .catch(_catch); 78 | 79 | } 80 | } 81 | 82 | function query_doing(data, isRefreshing, isLoadging) { 83 | return { 84 | type: Type.query.QUERY_DOING, data: data, 85 | isRefreshing: isRefreshing, isLoading: isLoadging 86 | } 87 | } 88 | 89 | function query_done(data) { 90 | return {type: Type.query.QUERY_DONE, data: data} 91 | } 92 | 93 | function query_error(data) { 94 | return {type: Type.query.QUERY_ERROR, data: data} 95 | } -------------------------------------------------------------------------------- /src/redux/actions/RecommendationAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.recommondation.RECOMMONDATION_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.recommondation.RECOMMONDATION_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.recommondation.RECOMMONDATION_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/ResourcesAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.resources.RESOURCES_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.resources.RESOURCES_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.resources.RESOURCES_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/actions/Types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const types = { 4 | all: { 5 | ALL_DOING: 'ALL_DOING', 6 | ALL_DONE: 'ALL_DONE', 7 | ALL_ERROR: 'ALL_ERROR', 8 | }, 9 | android: { 10 | ANDROD_DOING: 'ANDROD_DOING', 11 | ANDROD_DONE: 'ANDROD_DONE', 12 | ANDROD_ERROR: 'ANDROD_ERROR', 13 | }, 14 | query: { 15 | QUERY_DOING: 'QUERY_DOING', 16 | QUERY_DONE: 'QUERY_DONE', 17 | QUERY_ERROR: 'QUERY_ERROR', 18 | }, 19 | 20 | girl: { 21 | GIRL_DOING: 'GIRL_DOING', 22 | GIRL_DONE: 'GIRL_DONE', 23 | GIRL_ERROR: 'GIRL_ERROR', 24 | }, 25 | app: { 26 | APP_DOING: 'APP_DOING', 27 | APP_DONE: 'APP_DONE', 28 | APP_ERROR: 'APP_ERROR', 29 | }, 30 | recommondation: { 31 | RECOMMONDATION_DOING: 'RECOMMONDATION_DOING', 32 | RECOMMONDATION_DONE: 'RECOMMONDATION_DONE', 33 | RECOMMONDATION_ERROR: 'RECOMMONDATION_ERROR', 34 | }, 35 | resources: { 36 | RESOURCES_DOING: 'RESOURCES_DOING', 37 | RESOURCES_DONE: 'RESOURCES_DONE', 38 | RESOURCES_ERROR: 'RESOURCES_ERROR', 39 | }, 40 | video: { 41 | VIDEO_DOING: 'VIDEO_DOING', 42 | VIDEO_DONE: 'VIDEO_DONE', 43 | VIDEO_ERROR: 'VIDEO_ERROR', 44 | }, 45 | js: { 46 | JS_DOING: 'JS_DOING', 47 | JS_DONE: 'JS_DONE', 48 | JS_ERROR: 'JS_ERROR', 49 | }, 50 | ios: { 51 | IOS_DOING: 'IOS_DOING', 52 | IOS_DONE: 'IOS_DONE', 53 | IOS_ERROR: 'IOS_ERROR', 54 | }, 55 | 56 | } 57 | 58 | export default types; 59 | -------------------------------------------------------------------------------- /src/redux/actions/VideoAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Type from './Types'; 4 | import ToastUtils from '../../utils/ToastUtils'; 5 | 6 | let mDispatch; 7 | let mTotalData = []; 8 | 9 | function _requestObj(opt) { 10 | 11 | return new Request('http://gank.io/api/data/' + opt.type + '/10/' + opt.num 12 | + (opt.isImage ? '?imageView2/0/w/100' : ''), { 13 | method: 'GET', 14 | mode: 'cors', 15 | credentials: 'include' 16 | }); 17 | } 18 | 19 | function _status(response) { 20 | //是否正常返回,ok代表状态码在200-299之间==(response.status >= 200 && response.status < 300) 21 | if (response.ok) { 22 | 23 | console.log(response.status); 24 | // console.log(response.statusText); 25 | // console.log(response.type); 26 | console.log(response.url); 27 | console.log('------------------'); 28 | 29 | return Promise.resolve(response) 30 | } else { 31 | //TODO 此处登录失败会出现红色弹窗,需优化 return Promise.reject(new Error(response.statusText)) 32 | return Promise.resolve(response) 33 | 34 | } 35 | } 36 | 37 | function _json(res) { 38 | return res.json() 39 | } 40 | 41 | function _parseJson(responseJson) { 42 | // console.log('androidview responsejson:' + responseJson); 43 | if (!responseJson.error) { 44 | // ToastUtils.show("网络连接成功"); 45 | console.log('mTotalData 11111:-----'); 46 | console.log(mTotalData); 47 | 48 | mTotalData = mTotalData.concat(responseJson.results); 49 | mDispatch(android_done(mTotalData)); 50 | console.log('mTotalData 22222:-----'); 51 | console.log(mTotalData); 52 | } else { 53 | ToastUtils.show("网络连接失败,请重连后重试"); 54 | mDispatch(android_error(mTotalData)); 55 | } 56 | } 57 | 58 | function _catch(error) { 59 | // console.error(error); ToastUtils.show("网络连接失败,请重连后重试"); 60 | mDispatch(android_error(mTotalData)); 61 | } 62 | 63 | export function doDoing(opt) { 64 | return (dispatch) => { 65 | mDispatch = dispatch; 66 | if (opt.isRefreshing) { 67 | mTotalData = []; 68 | } 69 | dispatch(android_doing(mTotalData, opt.isRefreshing, opt.isLoading)); 70 | 71 | let result = fetch(_requestObj(opt)) 72 | .then(_status) 73 | .then(_json) 74 | .then(_parseJson) 75 | .catch(_catch); 76 | 77 | } 78 | } 79 | 80 | function android_doing(data, isRefreshing, isLoadging) { 81 | return { 82 | type: Type.video.VIDEO_DOING, data: data, 83 | isRefreshing: isRefreshing, isLoading: isLoadging 84 | } 85 | } 86 | 87 | function android_done(data) { 88 | return {type: Type.video.VIDEO_DONE, data: data} 89 | } 90 | 91 | function android_error(data) { 92 | return {type: Type.video.VIDEO_ERROR, data: data} 93 | } -------------------------------------------------------------------------------- /src/redux/reducers/AllReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.all.ALL_DOING: 19 | console.log('reducers all: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.all.ALL_DONE: 30 | console.log('reducers all: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.all.ALL_ERROR: 41 | console.log('reducers all: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers all: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/AndroidReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.android.ANDROD_DOING: 19 | console.log('reducers android: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.android.ANDROD_DONE: 30 | console.log('reducers android: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.android.ANDROD_ERROR: 41 | console.log('reducers android: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers android: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/AppReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.app.APP_DOING: 19 | console.log('reducers app: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.app.APP_DONE: 30 | console.log('reducers app: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.app.APP_ERROR: 41 | console.log('reducers app: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers app: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/GirlReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.girl.GIRL_DOING: 19 | console.log('reducers girl: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.girl.GIRL_DONE: 30 | console.log('reducers girl: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.girl.GIRL_ERROR: 41 | console.log('reducers girl: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers girl: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/Index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {combineReducers} from 'redux'; 4 | import android from './AndroidReducer'; // 导入登录的redux处理过程 5 | import query from './QueryReducer'; // 导入登录的redux处理过程 6 | import all from './AllReducer'; 7 | import ios from './IosReducer'; 8 | import js from './JsReducer'; 9 | import girl from './GirlReducer'; 10 | import recommendation from './RecomendationReducer'; 11 | import resources from './ResourcesReducer'; 12 | import video from './VideoReducer'; 13 | import app from './AppReducer'; 14 | 15 | const rootReducer = combineReducers({ // 将所有的redux处理逻辑包装在一起 16 | all:all, 17 | android: android, 18 | query: query, 19 | ios: ios, 20 | js: js, 21 | girl: girl, 22 | recommendation: recommendation, 23 | resources: resources, 24 | video: video, 25 | app: app, 26 | }); 27 | 28 | export default rootReducer; // 导出,作为统一入口 -------------------------------------------------------------------------------- /src/redux/reducers/IosReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.ios.IOS_DOING: 19 | console.log('reducers iOS: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.ios.IOS_DONE: 30 | console.log('reducers iOS: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.ios.IOS_ERROR: 41 | console.log('reducers iOS: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers iOS: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/JsReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.js.JS_DOING: 19 | console.log('reducers js: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.js.JS_DONE: 30 | console.log('reducers js: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.js.JS_ERROR: 41 | console.log('reducers js: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers js: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/QueryReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.query.QUERY_DOING: 19 | console.log('reducers query: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.query.QUERY_DONE: 30 | console.log('reducers query: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.query.QUERY_ERROR: 41 | console.log('reducers query: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers query: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/RecomendationReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.recommondation.RECOMMONDATION_DOING: 19 | console.log('reducers recommendation: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.recommondation.RECOMMONDATION_DONE: 30 | console.log('reducers recommendation: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.recommondation.RECOMMONDATION_ERROR: 41 | console.log('reducers recommendation: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers recommendation: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/ResourcesReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.resources.RESOURCES_DOING: 19 | console.log('reducers resources: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.resources.RESOURCES_DONE: 30 | console.log('reducers resources: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.resources.RESOURCES_ERROR: 41 | console.log('reducers resources: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers resources: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/reducers/VideoReducer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Types from '../actions/Types'; // 导入事件类别,用来做事件类别的判断 4 | 5 | // 初始状态 6 | 7 | const initialState = { 8 | status: 'init', 9 | isSuccess: false, 10 | data: null, 11 | isRefreshing:false, 12 | isLoading:false, 13 | } 14 | // 不同类别的事件使用switch对应处理过程 15 | 16 | export default function loginIn(state = initialState, action) { 17 | switch (action.type) { 18 | case Types.video.VIDEO_DOING: 19 | console.log('reducers video: doing'); 20 | return { 21 | ...state, 22 | status: 'doing', 23 | isSuccess: false, 24 | data: action.data, 25 | isRefreshing:action.isRefreshing, 26 | isLoading:action.isLoading, 27 | } 28 | break; 29 | case Types.video.VIDEO_DONE: 30 | console.log('reducers video: success'); 31 | return { 32 | ...state, 33 | status: 'success', 34 | isSuccess: true, 35 | data: action.data, 36 | isRefreshing:false, 37 | isLoading:false, 38 | } 39 | break; 40 | case Types.video.VIDEO_ERROR: 41 | console.log('reducers video: error'); 42 | return { 43 | ...state, 44 | status: 'error', 45 | isSuccess: false, 46 | data: action.data, 47 | isRefreshing:false, 48 | isLoading:false, 49 | } 50 | break; 51 | default: 52 | console.log('reducers video: default'); 53 | return state; 54 | 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/store/Index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {applyMiddleware, createStore,compose} from 'redux'; 4 | import thunk from 'redux-thunk'; 5 | import thunkMiddleware from 'redux-thunk'; 6 | import {persistStore, autoRehydrate} from 'redux-persist'; 7 | import {AsyncStorage} from 'react-native'; 8 | import rootReducer from '../reducers/Index'; 9 | // import {createLogger} from 'redux-logger'; 10 | 11 | const logger = store => next => action => { 12 | if (typeof action === 'function') 13 | console.log('dispatching a function'); 14 | else 15 | console.log('dispatching:', action); 16 | 17 | let result = next(action); 18 | console.log('result ACTION:', result); 19 | console.log('next state:', store.getState()); 20 | return result; 21 | } 22 | 23 | let middlewares = [logger, thunk]; 24 | 25 | let createAppStore = applyMiddleware(...middlewares)(createStore); 26 | 27 | export default function configureStore(onComplete: () => void) { 28 | const store = autoRehydrate()(createAppStore)(rootReducer); 29 | let opt = { 30 | storage: AsyncStorage, 31 | transform: [], 32 | // whitelist: ['userStore'], 33 | }; 34 | persistStore(store, opt, onComplete); 35 | return store; 36 | } 37 | 38 | 39 | /*const createStoreWithMiddleware = applyMiddleware(thunk)(createStore); 40 | export default function configureStore(initialState) { 41 | const store = createStoreWithMiddleware(rootReducer, initialState); 42 | 43 | return store; 44 | }*/ 45 | 46 | 47 | /*// middleware that logs actions 48 | const loggerMiddleware = createLogger({ predicate: (getState, action) => __DEV__ }); 49 | 50 | export default function configureStore(initialState) { 51 | const enhancer = compose( 52 | applyMiddleware( 53 | thunkMiddleware, // lets us dispatch() functions 54 | loggerMiddleware, 55 | ), 56 | ); 57 | return createStore(rootReducer, initialState, enhancer); 58 | }*/ 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/styles/Common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @class StylesCommon 4 | * @desc 通用样式 5 | * */ 6 | 7 | import React from 'react'; 8 | import { 9 | Platform, 10 | StyleSheet, 11 | PixelRatio, 12 | Dimensions, 13 | } from 'react-native' 14 | import Util from "../utils/Utils"; 15 | 16 | var cell_w = Dimensions.get('window').width; 17 | 18 | function adaptiveTopiOS() { 19 | if (Platform.OS == 'ios') { 20 | return 30 21 | } else { 22 | return 0 23 | } 24 | } 25 | 26 | var CommonStyles = StyleSheet.create({ 27 | wrapper: { 28 | flex: 1, 29 | }, 30 | bgColor: { 31 | backgroundColor: '#F5FCFF' 32 | }, 33 | mgt5: { 34 | marginTop: 5, 35 | }, 36 | mgb5: { 37 | marginBottom: 5, 38 | }, 39 | pdt5: { 40 | paddingTop: 5, 41 | }, 42 | pdb5: { 43 | paddingBottom: 5, 44 | }, 45 | textCenter: { 46 | textAlign: 'center', 47 | flex: 1, 48 | }, 49 | textAli: { 50 | textAlign: 'right', 51 | }, 52 | navbar: { 53 | flexDirection: 'row', 54 | borderBottomColor: '#000000', 55 | borderBottomWidth: 1 / PixelRatio.get(), 56 | }, 57 | justAlign: { 58 | alignItems: 'center', 59 | justifyContent: 'center', 60 | }, 61 | 62 | modal: { 63 | height: 100, 64 | width: 100, 65 | backgroundColor: 'rgba(0, 0, 0, 0.8)', 66 | }, 67 | 68 | viewList: { 69 | padding: 10, 70 | fontSize: 15, 71 | }, 72 | 73 | flexRow: { 74 | flexDirection: 'row', 75 | }, 76 | 77 | flex1: { 78 | flex: 1, 79 | }, 80 | adaptiveTopiOS: { 81 | paddingTop: adaptiveTopiOS(), 82 | backgroundColor: '#ffffff' 83 | }, 84 | item:{ 85 | // height:Util.getHeight(67), 86 | // backgroundColor: '#F5FCFF', 87 | // paddingLeft:Util.getWidth(7), 88 | // paddingRight:Util.getWidth(7), 89 | // paddingTop:Util.getHeight(5), 90 | // paddingBottom:Util.getHeight(5), 91 | justifyContent:'center', 92 | flex: 1, 93 | backgroundColor: 'white', 94 | padding: Util.getWidth(10), 95 | paddingVertical:Util.getHeight(20), 96 | marginLeft: Util.getWidth(5), 97 | marginRight:Util.getHeight(5), 98 | marginVertical: Util.getHeight(5), 99 | borderColor: '#dddddd', 100 | borderStyle: null, 101 | borderWidth: Util.getWidth(0.5), 102 | borderRadius: Util.getWidth(2), 103 | shadowColor: 'gray', // 设置阴影 104 | shadowOffset: {width:0.5, height: 0.5}, 105 | shadowOpacity: 0.4, // 透明度 106 | shadowRadius: 1, 107 | elevation:Util.getHeight(2) // 高度,设置Z轴,可以产生立体效果 108 | 109 | }, 110 | itemTop:{ 111 | fontSize:Util.getFontSize(15), 112 | color: 'blue', 113 | }, 114 | itemBottom:{ 115 | fontSize:Util.getFontSize(15), 116 | color: 'black', 117 | // textAlign:'right', 118 | marginTop:Util.getHeight(2), 119 | textAlignVertical:'bottom', 120 | marginBottom:Util.getHeight(2), 121 | }, 122 | itemBottomRight:{ 123 | fontSize:Util.getFontSize(15), 124 | color: 'black', 125 | textAlign:'right', 126 | marginTop:Util.getHeight(2), 127 | textAlignVertical:'bottom', 128 | marginBottom:Util.getHeight(2), 129 | }, 130 | separator:{ 131 | backgroundColor:'#cccccccc', 132 | height:Util.getHeight(0.5), 133 | paddingLeft:Util.getWidth(7), 134 | paddingRight:Util.getWidth(7), 135 | }, 136 | }); 137 | 138 | export default CommonStyles; 139 | -------------------------------------------------------------------------------- /src/utils/ClickUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | 防止重复点击工具类,目前时间定为3秒 3 | 返回true,代表可以点击,false,代表不可以点击 4 | */ 5 | const minClickDelayTime = 1000; // 延迟时间 6 | let lastClickTime = 0; // 最后点击时间 7 | 8 | const noDoubleClick = () => { 9 | const currentTime = new Date().getTime(); 10 | if (currentTime - lastClickTime > minClickDelayTime) { 11 | lastClickTime = currentTime; 12 | return true; 13 | } 14 | return false; 15 | }; 16 | 17 | // 清空点击时间 18 | const resetLastTime = () => { 19 | lastClickTime = 0; 20 | }; 21 | 22 | export default {noDoubleClick, resetLastTime}; 23 | 24 | /* 25 | 26 | 使用方法: 27 | 1.在需要的js文件中,首先导入 28 | import ClickUtil from '../文件路径/文件名称'; 29 | 30 | 2.在点击时间处,加上下面的方法就可以了 31 | if (ClickUtil.noDoubleClick()) 32 | { 33 | this.pushToSetting(); 34 | } 35 | */ 36 | -------------------------------------------------------------------------------- /src/utils/Constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Constants= { 4 | ActiveOpacityNum: 0.8, 5 | //内网 6 | BASE_URL:'http://192.168.31.4:48080/zkpms-api/', 7 | //自验证环境 8 | // BASE_URL:'http://121.43.163.28:18080/zkpms-api/', 9 | }; 10 | 11 | global.constants = Constants; 12 | 13 | export default Constants; -------------------------------------------------------------------------------- /src/utils/ToastUtils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Toast from "react-native-root-toast"; 4 | 5 | var ToastUtils = { 6 | 7 | show: function (message: string): void { 8 | Toast.show(message, { 9 | duration: 1000, 10 | // duration: Toast.durations.SHORT, 11 | position: Toast.positions.BOTTOM, 12 | shadow: false, 13 | animation: true, 14 | hideOnPress: true, 15 | delay: 0, 16 | // onShow: () => { 17 | // // calls on toast\`s appear animation start 18 | // }, 19 | // onShown: () => { 20 | // // calls on toast\`s appear animation end. 21 | // }, 22 | // onHide: () => { 23 | // // calls on toast\`s hide animation start. 24 | // }, 25 | // onHidden: () => { 26 | // // calls on toast\`s hide animation end. 27 | // } 28 | }); 29 | }, 30 | }; 31 | export default ToastUtils; -------------------------------------------------------------------------------- /src/utils/Utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import {Dimensions} from 'react-native'; 5 | 6 | const BASE_WIN_HEIGHT = 667; 7 | const BASE_WIN_WIDTH = 375; 8 | 9 | const Utils = { 10 | size: { 11 | width: Dimensions.get('window').width, 12 | height: Dimensions.get('window').height 13 | }, 14 | 15 | /** 根据实际屏幕尺寸转换对应的像素高度 */ 16 | getHeight(h) { 17 | if (!this.height) { 18 | this.height = Utils.size.height; 19 | this.width = Utils.size.width; 20 | } 21 | return h * (this.height / BASE_WIN_HEIGHT); 22 | }, 23 | 24 | /** 根据实际屏幕尺寸转换对应的像素宽度 */ 25 | getWidth(w) { 26 | if (!this.width) { 27 | this.height = Utils.size.height; 28 | this.width = Utils.size.width; 29 | } 30 | return w * (this.width / BASE_WIN_WIDTH); 31 | }, 32 | getFontSize(w) { 33 | if (!this.width) { 34 | this.height = Utils.size.height; 35 | this.width = Utils.size.width; 36 | } 37 | return w * (this.width / BASE_WIN_WIDTH); 38 | } 39 | 40 | }; 41 | 42 | 43 | export default Utils; -------------------------------------------------------------------------------- /src/views/AppView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React, {Component} from "react"; 3 | import { 4 | FlatList, 5 | StyleSheet, 6 | Text, 7 | View, 8 | RefreshControl, 9 | ActivityIndicator, 10 | } from "react-native"; 11 | import TitleBar from '../components/TitleBar'; 12 | import Utils from '../utils/Utils'; 13 | import {doDoing} from '../redux/actions/AppAction'; 14 | import Progress from '../components/ProgressComponent'; 15 | import {connect} from 'react-redux'; 16 | import commonStyles from "../styles/Common"; 17 | import MyItem from '../components/MyItem'; 18 | import ClickUtil from '../utils/ClickUtil'; 19 | 20 | let mCurPage; 21 | let isFirstRefresh; 22 | let thiz; 23 | 24 | class AppView extends Component { 25 | 26 | componentWillMount() { 27 | thiz = this; 28 | mCurPage = 1; 29 | } 30 | 31 | componentDidMount() { 32 | isFirstRefresh = true; 33 | this._refreshing(); 34 | } 35 | 36 | componentDidUpdate() { 37 | // if (this.props.app && this.props.app.data) { 38 | // isFirst = false; 39 | // } 40 | } 41 | 42 | renderLoadingView() { 43 | return ( 44 | 45 | 46 | 47 | 48 | ); 49 | } 50 | 51 | //加载失败view 52 | renderErrorView() { 53 | return ( 54 | 55 | 56 | 57 | 网络连接出错,请检查后重试! 58 | 59 | 60 | ); 61 | } 62 | 63 | //点击列表点击每一行 64 | _clickItem(item, index) { 65 | if (!ClickUtil.noDoubleClick()) { 66 | return; 67 | } 68 | thiz.props.navigation.navigate('Details',{title:item.desc,url:item.url}); 69 | } 70 | 71 | //返回itemView 72 | _renderItemView({item, index}) { 73 | return ( 74 | 83 | ); 84 | } 85 | 86 | _header() { 87 | return ( 88 | 89 | header 90 | 91 | ); 92 | } 93 | 94 | _footer() { 95 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 96 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 97 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 isFirstRefresh 98 | if (isFirstRefresh) { 99 | console.log('sssssssssssssssssssss1'); 100 | return thiz._foot_no_loading(); 101 | } else if (!thiz.props.app.isLoading) { 102 | console.log('sssssssssssssssssssss2'); 103 | return thiz._foot_no_loading(); 104 | } else { 105 | console.log('sssssssssssssssssssss3'); 106 | return thiz._foot_loading(); 107 | } 108 | } 109 | 110 | _foot_loading() { 111 | return ( 112 | 113 | 118 | 119 | ); 120 | } 121 | 122 | _foot_no_loading() { 123 | return ( 124 | 125 | 126 | ); 127 | } 128 | 129 | _separator() { 130 | return ( 131 | 132 | 133 | ); 134 | } 135 | 136 | _listEmptyComponent() { 137 | return ( 138 | 139 | 140 | 数据为空! 141 | 142 | 143 | 144 | ); 145 | } 146 | 147 | _refreshing() { 148 | mCurPage = 1; 149 | let opt = { 150 | num: mCurPage, 151 | isRefreshing: true, 152 | isLoading: false, 153 | type:'App', 154 | }; 155 | thiz 156 | .props 157 | .dispatch(doDoing(opt)); 158 | console.log('xxxxxxxxxxxxxxxx刷新成功'); 159 | } 160 | 161 | _onload() { 162 | // ToastUtils.show('到达底部'); 163 | mCurPage++; 164 | let opt = { 165 | num: mCurPage, 166 | isRefreshing: false, 167 | isLoading: true, 168 | type:'App', 169 | }; 170 | thiz 171 | .props 172 | .dispatch(doDoing(opt)); 173 | console.log('xxxxxxxxxxxxxxxx加载更多'); 174 | } 175 | 176 | _sourceData() { 177 | if (this.props.app && this.props.app.data) { 178 | // isFirst = false; 179 | return this.props.app.data 180 | } else { 181 | return null 182 | } 183 | } 184 | 185 | //此函数用于为给定的item生成一个不重复的key 186 | _keyExtractor = (item, index) => item._id; 187 | 188 | renderData() { 189 | console.log(this.props.app) 190 | return ( 191 | 192 | 193 | 212 | } 213 | onEndReachedThreshold={0.1} 214 | onEndReached={() => { 215 | this._onload()//此处需要的是方法 用箭头函数也可以 216 | }} 217 | //如果设置了getItemLayout,那么renderItem的高度必须和这个高度一样, 218 | // 否则加载一段列表后就会出现错乱和显示空白。 219 | getItemLayout={(data, index) => ( 220 | {length: Utils.getHeight(67), offset: Utils.getHeight(67) * index, index} 221 | )} 222 | /> 223 | 224 | ); 225 | } 226 | 227 | render() { 228 | console.log('----this.props.app.status:' + this.props.app.status); 229 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 230 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 231 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 232 | if (this.props.app.status == 'success') { 233 | isFirstRefresh = false; 234 | } 235 | if (this.props.app.status == 'error') { 236 | //请求失败view 237 | return this.renderErrorView(); 238 | } else if (this.props.app.status == 'init') { 239 | return null; 240 | } 241 | //加载数据 242 | return this.renderData(); 243 | } 244 | } 245 | 246 | const styles = StyleSheet.create({ 247 | container: { 248 | flex: 1, 249 | justifyContent: 'flex-start', 250 | alignItems: 'center', 251 | // position:'absolute' 252 | }, 253 | errorText: { 254 | flex: 1, 255 | // position:'absolute', 256 | alignSelf: 'center', 257 | // justifyContent: 'center', 258 | // alignItems: 'center', 259 | marginTop: Utils.size.height / 5 * 2, 260 | }, 261 | emptyData: { 262 | flex: 1, 263 | // position:'absolute', 264 | alignSelf: 'center', 265 | // justifyContent: 'center', 266 | // alignItems: 'center', 267 | marginTop: Utils.size.height / 5 * 2, 268 | }, 269 | title: { 270 | fontSize: Utils.getFontSize(15), 271 | color: 'blue' 272 | }, 273 | content: { 274 | fontSize: Utils.getFontSize(15), 275 | color: 'black' 276 | } 277 | 278 | }); 279 | 280 | //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。 281 | function mapStateToProps(state) { 282 | const {app} = state; 283 | return {app} 284 | } 285 | 286 | export default connect(mapStateToProps)(AppView); -------------------------------------------------------------------------------- /src/views/DetailsView.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, {Component} from 'react'; 4 | import { 5 | AppRegistry, 6 | StyleSheet, 7 | Text, 8 | View, 9 | Image, 10 | WebView 11 | } from 'react-native'; 12 | import Utils from '../utils/Utils'; 13 | import commonStyles from "../styles/Common"; 14 | import RightMenu from '../components/RightMenu'; 15 | 16 | const BGWASH = 'rgba(255,255,255,0.8)'; 17 | 18 | export default class DetailsView extends Component { 19 | static navigationOptions = ({navigation, screenProps}) => ({ 20 | headerTitle: navigation.state.params.title, 21 | headerTitleStyle: { 22 | fontSize: Utils.getFontSize(13), 23 | }, 24 | headerRight: ( 25 | 26 | ), 27 | } 28 | ) 29 | 30 | _renderErrorView() { 31 | return ( 32 | 33 | 34 | 网络连接出错,请检查后重试! 35 | 36 | 37 | ) 38 | } 39 | 40 | render() { 41 | return ( 42 | 43 | this.webview = webview} 45 | automaticallyAdjustContentInsets={false} 46 | style={styles.webView} 47 | source={{uri: this.props.navigation.state.params.url}} 48 | javaScriptEnabled={true} 49 | domStorageEnabled={true} 50 | decelerationRate="normal" 51 | // onNavigationStateChange={this._onNavigationStateChange} 52 | // onShouldStartLoadWithRequest={this._onShouldStartLoadWithRequest} 53 | startInLoadingState={true} 54 | // onMessage={this._onMessage} 55 | scalesPageToFit={true} 56 | //allowsInlineMediaPlayback bool 57 | //指定HTML5视频是在网页当前位置播放还是使用原生的全屏播放器播放。 默认值为false。 58 | //注意 : 要让视频在网页中播放,不光要将这个属性设为true,HTML中的视频元素本身也需要包含webkit-playsinline属性。 59 | allowsInlineMediaPlayback={true} 60 | dataDetectorTypes={'link'} 61 | //设置页面中的HTML5音视频是否需要在用户点击后再开始播放。默认值为true. 62 | mediaPlaybackRequiresUserAction={true} 63 | // onError={}//加载失败时调用。 64 | // onLoad={}//加载成功时调用 65 | renderError={this._renderErrorView}//设置一个函数,返回一个视图用于显示错误。 66 | // renderLoading={}//设置一个函数,返回一个加载指示器 67 | scrollEnabled={true} 68 | mixedContentMode={'compatibility'} 69 | saveFormDataDisabled={true} 70 | bounces={true}//指定滑动到边缘后是否有回弹效果。ios 71 | /> 72 | 73 | ); 74 | } 75 | } 76 | 77 | const styles = StyleSheet.create({ 78 | webView: { 79 | backgroundColor: BGWASH, 80 | height: Utils.size.height, 81 | width: Utils.size.width, 82 | }, 83 | container: { 84 | flex: 1, 85 | justifyContent: 'center', 86 | alignItems: 'center', 87 | backgroundColor: '#F5FCFF', 88 | }, 89 | welcome: { 90 | fontSize: 20, 91 | textAlign: 'center', 92 | margin: 10, 93 | }, 94 | instructions: { 95 | textAlign: 'center', 96 | color: '#333333', 97 | marginBottom: 5, 98 | }, 99 | errorText: { 100 | flex: 1, 101 | // position:'absolute', 102 | alignSelf: 'center', 103 | // justifyContent: 'center', 104 | // alignItems: 'center', 105 | marginTop: Utils.size.height / 5 * 2, 106 | }, 107 | }); 108 | 109 | //javaScriptEnabled:是否启用JavaScript (android),ios无此属性,已经默认true 110 | //domStorageEnabled:是否开启DOM本地存储(android) 111 | //decelerationRate:指定一个浮点数,用于设置触摸停止后,多块速度停止滚动,也可传字符串,normal,fast 112 | //onNavigationStateChange:当webview加载开始或者结束回调函数 113 | //onShouldStartLoadWithRequest:允许为webview发起的请求运行一个自定义的处理函数。返回true或false表示是否要继续执行响应的请求(ios) 114 | //startInLoadingState:强制WebView在第一次加载时先显示loading视图。默认为true 115 | //scalesPageToFit:设置是否要把网页缩放到适应视图的大小,以及是否允许用户改变缩放比例。 116 | //automaticallyAdjustContentInsets 控制是否调整放置在导航条、标签栏或工具栏后面的web视图的内容。默认值是true 117 | //source={require('./helloworld.html')可以加载html 118 | //postMessage RN发送消息给html 119 | //onMessage:html发送消息给RN 120 | //injectJavaScript注入脚本 -------------------------------------------------------------------------------- /src/views/GirlDetailView.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, {Component} from 'react'; 4 | import { 5 | StyleSheet, 6 | Text, 7 | View, 8 | ActivityIndicator, 9 | } from 'react-native'; 10 | import Utils from '../utils/Utils'; 11 | import commonStyles from "../styles/Common"; 12 | const BGWASH = 'rgba(255,255,255,0.8)' 13 | import FastImage from 'react-native-fast-image'; 14 | import {createImageProgress} from 'react-native-image-progress'; 15 | 16 | const Image = createImageProgress(FastImage); 17 | export default class GirlDetailView extends Component { 18 | static navigationOptions = ({navigation, screenProps}) => ({ 19 | headerTitle: navigation.state.params.title, 20 | headerTitleStyle: { 21 | fontSize: Utils.getFontSize(16), 22 | }, 23 | } 24 | ) 25 | 26 | _renderErrorView() { 27 | // console.log('加载图片出错,URL:'+this.props.navigation.state.params.url) 28 | // console.log('加载图片出错,error:'+error) 29 | // console.log(error) 30 | return ( 31 | 32 | 33 | 网络连接出错,请检查后重试! 34 | 35 | 36 | ) 37 | } 38 | 39 | render() { 40 | return ( 41 | 42 | { 44 | }} 45 | indicator={ActivityIndicator} 46 | source={{ 47 | uri: this.props.navigation.state.params.url, 48 | priority: FastImage.priority.normal, 49 | }} 50 | resizeMode={FastImage.resizeMode.contain} 51 | renderError={this._renderErrorView} 52 | style={{ 53 | width: Utils.size.width, 54 | height: Utils.size.height, 55 | }} 56 | /> 57 | 58 | ); 59 | } 60 | } 61 | 62 | const styles = StyleSheet.create({ 63 | webView: { 64 | backgroundColor: BGWASH, 65 | height: Utils.size.height, 66 | width: Utils.size.width, 67 | }, 68 | container: { 69 | flex: 1, 70 | justifyContent: 'center', 71 | alignItems: 'center', 72 | backgroundColor: '#F5FCFF', 73 | }, 74 | welcome: { 75 | fontSize: 20, 76 | textAlign: 'center', 77 | margin: 10, 78 | }, 79 | instructions: { 80 | textAlign: 'center', 81 | color: '#333333', 82 | marginBottom: 5, 83 | }, 84 | errorText: { 85 | flex: 1, 86 | // position:'absolute', 87 | alignSelf: 'center', 88 | // justifyContent: 'center', 89 | // alignItems: 'center', 90 | marginTop: Utils.size.height / 5 * 2, 91 | }, 92 | }); 93 | -------------------------------------------------------------------------------- /src/views/IosView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React, {Component} from "react"; 3 | import { 4 | Animated, 5 | FlatList, 6 | ScrollView, 7 | StyleSheet, 8 | Text, 9 | View, 10 | TouchableOpacity, 11 | RefreshControl, 12 | ActivityIndicator, 13 | } from "react-native"; 14 | import TitleBar from '../components/TitleBar'; 15 | import Utils from '../utils/Utils'; 16 | import {doDoing} from '../redux/actions/IosAction'; 17 | import Progress from '../components/ProgressComponent'; 18 | import {connect} from 'react-redux'; 19 | import commonStyles from "../styles/Common"; 20 | import ToastUtils from "../utils/ToastUtils"; 21 | import ProgressComponent from '../components/ProgressComponent'; 22 | import MyItem from '../components/MyItem'; 23 | 24 | const AnimatedFlatList = Animated.createAnimatedComponent(FlatList); 25 | let mCurPage; 26 | let isFirstRefresh; 27 | let thiz; 28 | 29 | class IosView extends Component { 30 | 31 | componentWillMount() { 32 | thiz = this; 33 | mCurPage = 1; 34 | } 35 | 36 | componentDidMount() { 37 | isFirstRefresh = true; 38 | this._refreshing(); 39 | } 40 | 41 | componentDidUpdate() { 42 | // if (this.props.ios && this.props.ios.data) { 43 | // isFirst = false; 44 | // } 45 | } 46 | 47 | renderLoadingView() { 48 | return ( 49 | 50 | 51 | 52 | 53 | ); 54 | } 55 | 56 | //加载失败view 57 | renderErrorView() { 58 | return ( 59 | 60 | 61 | 62 | 网络连接出错,请检查后重试! 63 | 64 | 65 | ); 66 | } 67 | 68 | //返回itemView 69 | _renderItemView({item, index}) { 70 | return ( 71 | 80 | ); 81 | } 82 | 83 | _header() { 84 | return ( 85 | 86 | header 87 | 88 | ); 89 | } 90 | 91 | _footer() { 92 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 93 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 94 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 isFirstRefresh 95 | if (isFirstRefresh) { 96 | return thiz._foot_no_loading(); 97 | } else if (!thiz.props.ios.isLoading) { 98 | return thiz._foot_no_loading(); 99 | } else { 100 | return thiz._foot_loading(); 101 | } 102 | } 103 | 104 | _foot_loading() { 105 | return ( 106 | 107 | 112 | 113 | ); 114 | } 115 | 116 | _foot_no_loading() { 117 | return ( 118 | 119 | 120 | ); 121 | } 122 | 123 | _separator() { 124 | return ( 125 | 126 | 127 | ); 128 | } 129 | 130 | _listEmptyComponent() { 131 | return ( 132 | 133 | 134 | 数据为空! 135 | 136 | 137 | 138 | ); 139 | } 140 | 141 | _refreshing() { 142 | mCurPage = 1; 143 | let opt = { 144 | num: mCurPage, 145 | isRefreshing: true, 146 | isLoading: false, 147 | type:'iOS', 148 | }; 149 | thiz 150 | .props 151 | .dispatch(doDoing(opt)); 152 | console.log('xxxxxxxxxxxxxxxx刷新成功'); 153 | } 154 | 155 | _onload() { 156 | // ToastUtils.show('到达底部'); 157 | mCurPage++; 158 | let opt = { 159 | num: mCurPage, 160 | isRefreshing: false, 161 | isLoading: true, 162 | type:'iOS', 163 | }; 164 | thiz 165 | .props 166 | .dispatch(doDoing(opt)); 167 | console.log('xxxxxxxxxxxxxxxx加载更多'); 168 | } 169 | 170 | _sourceData() { 171 | if (this.props.ios && this.props.ios.data) { 172 | // isFirst = false; 173 | return this.props.ios.data 174 | } else { 175 | return null 176 | } 177 | } 178 | 179 | //此函数用于为给定的item生成一个不重复的key 180 | _keyExtractor = (item, index) => item._id; 181 | 182 | renderData() { 183 | console.log(this.props.ios) 184 | return ( 185 | 186 | 187 | 206 | } 207 | onEndReachedThreshold={0.1} 208 | onEndReached={() => { 209 | this._onload()//此处需要的是方法 用箭头函数也可以 210 | }} 211 | //如果设置了getItemLayout,那么renderItem的高度必须和这个高度一样, 212 | // 否则加载一段列表后就会出现错乱和显示空白。 213 | getItemLayout={(data, index) => ( 214 | {length: Utils.getHeight(67), offset: Utils.getHeight(67) * index, index} 215 | )} 216 | /> 217 | 218 | ); 219 | } 220 | 221 | render() { 222 | console.log('----this.props.ios.status:' + this.props.ios.status); 223 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 224 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 225 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 226 | if (this.props.ios.status == 'success') { 227 | isFirstRefresh = false; 228 | } 229 | if (this.props.ios.status == 'error') { 230 | //请求失败view 231 | return this.renderErrorView(); 232 | } else if (this.props.ios.status == 'init') { 233 | return null; 234 | } 235 | //加载数据 236 | return this.renderData(); 237 | } 238 | } 239 | 240 | const styles = StyleSheet.create({ 241 | container: { 242 | flex: 1, 243 | justifyContent: 'flex-start', 244 | alignItems: 'center', 245 | // position:'absolute' 246 | }, 247 | errorText: { 248 | flex: 1, 249 | // position:'absolute', 250 | alignSelf: 'center', 251 | // justifyContent: 'center', 252 | // alignItems: 'center', 253 | marginTop: Utils.size.height / 5 * 2, 254 | }, 255 | emptyData: { 256 | flex: 1, 257 | // position:'absolute', 258 | alignSelf: 'center', 259 | // justifyContent: 'center', 260 | // alignItems: 'center', 261 | marginTop: Utils.size.height / 5 * 2, 262 | }, 263 | title: { 264 | fontSize: Utils.getFontSize(15), 265 | color: 'blue' 266 | }, 267 | content: { 268 | fontSize: Utils.getFontSize(15), 269 | color: 'black' 270 | } 271 | 272 | }); 273 | 274 | //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。 275 | function mapStateToProps(state) { 276 | const {ios} = state; 277 | return {ios} 278 | } 279 | 280 | export default connect(mapStateToProps)(IosView); -------------------------------------------------------------------------------- /src/views/JavascriptView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React, {Component} from "react"; 3 | import { 4 | Animated, 5 | FlatList, 6 | ScrollView, 7 | StyleSheet, 8 | Text, 9 | View, 10 | TouchableOpacity, 11 | RefreshControl, 12 | ActivityIndicator, 13 | } from "react-native"; 14 | import TitleBar from '../components/TitleBar'; 15 | import Utils from '../utils/Utils'; 16 | import {doDoing} from '../redux/actions/JsAction'; 17 | import Progress from '../components/ProgressComponent'; 18 | import {connect} from 'react-redux'; 19 | import commonStyles from "../styles/Common"; 20 | import ToastUtils from "../utils/ToastUtils"; 21 | import ProgressComponent from '../components/ProgressComponent'; 22 | import MyItem from '../components/MyItem'; 23 | 24 | const AnimatedFlatList = Animated.createAnimatedComponent(FlatList); 25 | let mCurPage; 26 | let isFirstRefresh; 27 | let thiz; 28 | 29 | class JavascriptView extends Component { 30 | 31 | componentWillMount() { 32 | thiz = this; 33 | mCurPage = 1; 34 | } 35 | 36 | componentDidMount() { 37 | isFirstRefresh = true; 38 | this._refreshing(); 39 | } 40 | 41 | componentDidUpdate() { 42 | // if (this.props.js && this.props.js.data) { 43 | // isFirst = false; 44 | // } 45 | } 46 | 47 | renderLoadingView() { 48 | return ( 49 | 50 | 51 | 52 | 53 | ); 54 | } 55 | 56 | //加载失败view 57 | renderErrorView() { 58 | return ( 59 | 60 | 61 | 62 | 网络连接出错,请检查后重试! 63 | 64 | 65 | ); 66 | } 67 | 68 | //返回itemView 69 | _renderItemView({item, index}) { 70 | return ( 71 | 80 | ); 81 | } 82 | 83 | _header() { 84 | return ( 85 | 86 | header 87 | 88 | ); 89 | } 90 | 91 | _footer() { 92 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 93 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 94 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 isFirstRefresh 95 | if (isFirstRefresh) { 96 | console.log('sssssssssssssssssssss1'); 97 | return thiz._foot_no_loading(); 98 | } else if (!thiz.props.js.isLoading) { 99 | console.log('sssssssssssssssssssss2'); 100 | return thiz._foot_no_loading(); 101 | } else { 102 | console.log('sssssssssssssssssssss3'); 103 | return thiz._foot_loading(); 104 | } 105 | } 106 | 107 | _foot_loading() { 108 | return ( 109 | 110 | 115 | 116 | ); 117 | } 118 | 119 | _foot_no_loading() { 120 | return ( 121 | 122 | 123 | ); 124 | } 125 | 126 | _separator() { 127 | return ( 128 | 129 | 130 | ); 131 | } 132 | 133 | _listEmptyComponent() { 134 | return ( 135 | 136 | 137 | 数据为空! 138 | 139 | 140 | 141 | ); 142 | } 143 | 144 | _refreshing() { 145 | mCurPage = 1; 146 | let opt = { 147 | num: mCurPage, 148 | isRefreshing: true, 149 | isLoading: false, 150 | type:'前端', 151 | }; 152 | thiz 153 | .props 154 | .dispatch(doDoing(opt)); 155 | console.log('xxxxxxxxxxxxxxxx刷新成功'); 156 | } 157 | 158 | _onload() { 159 | // ToastUtils.show('到达底部'); 160 | mCurPage++; 161 | let opt = { 162 | num: mCurPage, 163 | isRefreshing: false, 164 | isLoading: true, 165 | type:'前端', 166 | }; 167 | thiz 168 | .props 169 | .dispatch(doDoing(opt)); 170 | console.log('xxxxxxxxxxxxxxxx加载更多'); 171 | } 172 | 173 | _sourceData() { 174 | if (this.props.js && this.props.js.data) { 175 | // isFirst = false; 176 | return this.props.js.data 177 | } else { 178 | return null 179 | } 180 | } 181 | 182 | //此函数用于为给定的item生成一个不重复的key 183 | _keyExtractor = (item, index) => item._id; 184 | 185 | renderData() { 186 | console.log(this.props.js) 187 | return ( 188 | 189 | 190 | 209 | } 210 | onEndReachedThreshold={0.1} 211 | onEndReached={() => { 212 | this._onload()//此处需要的是方法 用箭头函数也可以 213 | }} 214 | //如果设置了getItemLayout,那么renderItem的高度必须和这个高度一样, 215 | // 否则加载一段列表后就会出现错乱和显示空白。 216 | getItemLayout={(data, index) => ( 217 | {length: Utils.getHeight(67), offset: Utils.getHeight(67) * index, index} 218 | )} 219 | /> 220 | 221 | ); 222 | } 223 | 224 | render() { 225 | console.log('----this.props.js.status:' + this.props.js.status); 226 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 227 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 228 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 229 | if (this.props.js.status == 'success') { 230 | isFirstRefresh = false; 231 | } 232 | if (this.props.js.status == 'error') { 233 | //请求失败view 234 | return this.renderErrorView(); 235 | } else if (this.props.js.status == 'init') { 236 | return null; 237 | } 238 | //加载数据 239 | return this.renderData(); 240 | } 241 | } 242 | 243 | const styles = StyleSheet.create({ 244 | container: { 245 | flex: 1, 246 | justifyContent: 'flex-start', 247 | alignItems: 'center', 248 | // position:'absolute' 249 | }, 250 | errorText: { 251 | flex: 1, 252 | // position:'absolute', 253 | alignSelf: 'center', 254 | // justifyContent: 'center', 255 | // alignItems: 'center', 256 | marginTop: Utils.size.height / 5 * 2, 257 | }, 258 | emptyData: { 259 | flex: 1, 260 | // position:'absolute', 261 | alignSelf: 'center', 262 | // justifyContent: 'center', 263 | // alignItems: 'center', 264 | marginTop: Utils.size.height / 5 * 2, 265 | }, 266 | title: { 267 | fontSize: Utils.getFontSize(15), 268 | color: 'blue' 269 | }, 270 | content: { 271 | fontSize: Utils.getFontSize(15), 272 | color: 'black' 273 | } 274 | 275 | }); 276 | 277 | //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。 278 | function mapStateToProps(state) { 279 | const {js} = state; 280 | return {js} 281 | } 282 | 283 | export default connect(mapStateToProps)(JavascriptView); -------------------------------------------------------------------------------- /src/views/MeView.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React, {Component} from 'react'; 4 | import { 5 | StyleSheet, 6 | Text, 7 | View, 8 | Linking, 9 | Image, 10 | TouchableOpacity 11 | } from 'react-native'; 12 | import TitleBar from '../components/TitleBar'; 13 | import CommonStyles from "../styles/Common"; 14 | import ClickUtil from "../utils/ClickUtil"; 15 | import Utils from "../utils/Utils"; 16 | 17 | export default class MeView extends Component { 18 | 19 | _onClick(title,url) { 20 | if (!ClickUtil.noDoubleClick()) { 21 | return; 22 | } 23 | this.props.navigation.navigate('Details',{title:title,url:url}); 24 | } 25 | _onClickOpenEmail(url){ 26 | Linking.canOpenURL(url).then(supported => { 27 | if (!supported) { 28 | console.log('Can\'t handle url: ' + url); 29 | } else { 30 | return Linking.openURL(url); 31 | } 32 | }).catch(err => console.error('An error occurred', err)); 33 | } 34 | 35 | render() { 36 | return ( 37 | 38 | 39 | 40 | 41 | { 44 | // this.props.items.navigation.navigate('All'); 45 | }}> 46 | Gank 47 | 50 | 51 | 52 | 53 | 54 | CSDN: 55 | 56 | this._onClick('CSDN','http://blog.csdn.net/vitamio')}> 59 | http://blog.csdn.net/vitamio 60 | 61 | 62 | 63 | 64 | 65 | 简书: 66 | 67 | this._onClick('简书','http://www.jianshu.com/u/2ac1317d87c9')}> 70 | http://www.jianshu.com/u/2ac1317d87c9 71 | 72 | 73 | 74 | 75 | 76 | Email: 77 | 78 | this._onClickOpenEmail("mailto:lishengjiework@gmail.com")}> 81 | lishengjiework@gmail.com 82 | 83 | 84 | 85 | 86 | Demo: 87 | 88 | this._onClick('Gank','https://github.com/serge66/gank')}> 91 | https://github.com/serge66/gank 92 | 93 | 94 | 95 | 96 | Api: 97 | 98 | this._onClick('Gank','http://gank.io')}> 101 | http://gank.io 102 | 103 | 104 | 105 | 106 | Version: 1.0 107 | 108 | 109 | 110 | ); 111 | } 112 | } 113 | 114 | const styles = StyleSheet.create({ 115 | link: { 116 | color: '#2e3c99' 117 | }, 118 | container: { 119 | flex: 1, 120 | justifyContent: 'center', 121 | alignItems: 'center', 122 | backgroundColor: '#F5FCFF', 123 | }, 124 | welcome: { 125 | fontSize: Utils.getFontSize(16), 126 | textAlign: 'center', 127 | margin: 10, 128 | }, 129 | instructions: { 130 | textAlign: 'center', 131 | color: '#333333', 132 | marginBottom: Utils.getWidth(5), 133 | }, 134 | touchable: { 135 | // flex: 1, 136 | justifyContent: 'center', 137 | alignItems: 'center', 138 | // height:'100%', 139 | width: '100%', 140 | }, 141 | img: { 142 | height: Utils.getHeight(200), 143 | width: Utils.getWidth(200), 144 | }, 145 | titleMsg: { 146 | textAlign: 'center', 147 | color: '#000', 148 | // paddingTop: 30, 149 | paddingBottom: Utils.getWidth(5), 150 | fontSize: Utils.getFontSize(30) 151 | } 152 | }); 153 | 154 | -------------------------------------------------------------------------------- /src/views/ResourcesView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React, {Component} from "react"; 3 | import { 4 | Animated, 5 | FlatList, 6 | StyleSheet, 7 | Text, 8 | View, 9 | TouchableOpacity, 10 | RefreshControl, 11 | ActivityIndicator, 12 | } from "react-native"; 13 | import TitleBar from '../components/TitleBar'; 14 | import Utils from '../utils/Utils'; 15 | import {doDoing} from '../redux/actions/ResourcesAction'; 16 | import Progress from '../components/ProgressComponent'; 17 | import {connect} from 'react-redux'; 18 | import commonStyles from "../styles/Common"; 19 | import MyItem from '../components/MyItem'; 20 | 21 | let mCurPage; 22 | let isFirstRefresh; 23 | let thiz; 24 | 25 | class ResourcesView extends Component { 26 | 27 | componentWillMount() { 28 | thiz = this; 29 | mCurPage = 1; 30 | } 31 | 32 | componentDidMount() { 33 | isFirstRefresh = true; 34 | this._refreshing(); 35 | } 36 | 37 | componentDidUpdate() { 38 | // if (this.props.resources && this.props.resources.data) { 39 | // isFirst = false; 40 | // } 41 | } 42 | 43 | renderLoadingView() { 44 | return ( 45 | 46 | 47 | 48 | 49 | ); 50 | } 51 | 52 | //加载失败view 53 | renderErrorView() { 54 | return ( 55 | 56 | 57 | 58 | 网络连接出错,请检查后重试! 59 | 60 | 61 | ); 62 | } 63 | 64 | //返回itemView 65 | _renderItemView({item, index}) { 66 | return ( 67 | 76 | ); 77 | } 78 | 79 | _header() { 80 | return ( 81 | 82 | header 83 | 84 | ); 85 | } 86 | 87 | _footer() { 88 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 89 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 90 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 isFirstRefresh 91 | if (isFirstRefresh) { 92 | console.log('sssssssssssssssssssss1'); 93 | return thiz._foot_no_loading(); 94 | } else if (!thiz.props.resources.isLoading) { 95 | console.log('sssssssssssssssssssss2'); 96 | return thiz._foot_no_loading(); 97 | } else { 98 | console.log('sssssssssssssssssssss3'); 99 | return thiz._foot_loading(); 100 | } 101 | } 102 | 103 | _foot_loading() { 104 | return ( 105 | 106 | 111 | 112 | ); 113 | } 114 | 115 | _foot_no_loading() { 116 | return ( 117 | 118 | 119 | ); 120 | } 121 | 122 | _separator() { 123 | return ( 124 | 125 | 126 | ); 127 | } 128 | 129 | _listEmptyComponent() { 130 | return ( 131 | 132 | 133 | 数据为空! 134 | 135 | 136 | 137 | ); 138 | } 139 | 140 | _refreshing() { 141 | mCurPage = 1; 142 | let opt = { 143 | num: mCurPage, 144 | isRefreshing: true, 145 | isLoading: false, 146 | type:'拓展资源', 147 | }; 148 | thiz 149 | .props 150 | .dispatch(doDoing(opt)); 151 | console.log('xxxxxxxxxxxxxxxx刷新成功'); 152 | } 153 | 154 | _onload() { 155 | // ToastUtils.show('到达底部'); 156 | mCurPage++; 157 | let opt = { 158 | num: mCurPage, 159 | isRefreshing: false, 160 | isLoading: true, 161 | type:'拓展资源', 162 | }; 163 | thiz 164 | .props 165 | .dispatch(doDoing(opt)); 166 | console.log('xxxxxxxxxxxxxxxx加载更多'); 167 | } 168 | 169 | _sourceData() { 170 | if (this.props.resources && this.props.resources.data) { 171 | // isFirst = false; 172 | return this.props.resources.data 173 | } else { 174 | return null 175 | } 176 | } 177 | 178 | //此函数用于为给定的item生成一个不重复的key 179 | _keyExtractor = (item, index) => item._id; 180 | 181 | renderData() { 182 | console.log(this.props.resources) 183 | return ( 184 | 185 | 186 | 205 | } 206 | onEndReachedThreshold={0.1} 207 | onEndReached={() => { 208 | this._onload()//此处需要的是方法 用箭头函数也可以 209 | }} 210 | //如果设置了getItemLayout,那么renderItem的高度必须和这个高度一样, 211 | // 否则加载一段列表后就会出现错乱和显示空白。 212 | getItemLayout={(data, index) => ( 213 | {length: Utils.getHeight(67), offset: Utils.getHeight(67) * index, index} 214 | )} 215 | /> 216 | 217 | ); 218 | } 219 | 220 | render() { 221 | console.log('----this.props.resources.status:' + this.props.resources.status); 222 | //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素 223 | // 的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的), 224 | // 这个事件也会被触发,请自行做标记过滤。 下面这个标记尚未彻底解决问题 225 | if (this.props.resources.status == 'success') { 226 | isFirstRefresh = false; 227 | } 228 | if (this.props.resources.status == 'error') { 229 | //请求失败view 230 | return this.renderErrorView(); 231 | } else if (this.props.resources.status == 'init') { 232 | return null; 233 | } 234 | //加载数据 235 | return this.renderData(); 236 | } 237 | } 238 | 239 | const styles = StyleSheet.create({ 240 | container: { 241 | flex: 1, 242 | justifyContent: 'flex-start', 243 | alignItems: 'center', 244 | // position:'absolute' 245 | }, 246 | errorText: { 247 | flex: 1, 248 | // position:'absolute', 249 | alignSelf: 'center', 250 | // justifyContent: 'center', 251 | // alignItems: 'center', 252 | marginTop: Utils.size.height / 5 * 2, 253 | }, 254 | emptyData: { 255 | flex: 1, 256 | // position:'absolute', 257 | alignSelf: 'center', 258 | // justifyContent: 'center', 259 | // alignItems: 'center', 260 | marginTop: Utils.size.height / 5 * 2, 261 | }, 262 | title: { 263 | fontSize: Utils.getFontSize(15), 264 | color: 'blue' 265 | }, 266 | content: { 267 | fontSize: Utils.getFontSize(15), 268 | color: 'black' 269 | } 270 | 271 | }); 272 | 273 | //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。 274 | function mapStateToProps(state) { 275 | const {resources} = state; 276 | return {resources} 277 | } 278 | 279 | export default connect(mapStateToProps)(ResourcesView); --------------------------------------------------------------------------------