├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── LICENSE ├── README.md ├── android ├── app │ ├── BUCK │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ ├── fonts │ │ │ └── Ionicons.ttf │ │ ├── index.android.bundle │ │ └── index.android.bundle.meta │ │ ├── java │ │ └── com │ │ │ └── bao │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── drawable-hdpi │ │ └── node_modules_reactnavigation_src_views_assets_backicon.png │ │ ├── drawable-land-hdpi │ │ └── launch_screen.png │ │ ├── drawable-land-ldpi │ │ └── launch_screen.png │ │ ├── drawable-land-mdpi │ │ └── launch_screen.png │ │ ├── drawable-land-xhdpi │ │ └── launch_screen.png │ │ ├── drawable-land-xxhdpi │ │ └── launch_screen.png │ │ ├── drawable-land-xxxhdpi │ │ └── launch_screen.png │ │ ├── drawable-mdpi │ │ ├── node_modules_reactnativerouterflux_images_back_chevron.png │ │ ├── node_modules_reactnativerouterflux_images_menu_burger.png │ │ ├── node_modules_reactnavigation_src_views_assets_backicon.png │ │ ├── src_assets_imgs_icon_dark.png │ │ └── src_assets_imgs_icon_light.png │ │ ├── drawable-port-hdpi │ │ └── launch_screen.png │ │ ├── drawable-port-ldpi │ │ └── launch_screen.png │ │ ├── drawable-port-mdpi │ │ └── launch_screen.png │ │ ├── drawable-port-xhdpi │ │ └── launch_screen.png │ │ ├── drawable-port-xxhdpi │ │ └── launch_screen.png │ │ ├── drawable-port-xxxhdpi │ │ └── launch_screen.png │ │ ├── drawable-xhdpi │ │ └── node_modules_reactnavigation_src_views_assets_backicon.png │ │ ├── drawable-xxhdpi │ │ └── node_modules_reactnavigation_src_views_assets_backicon.png │ │ ├── drawable-xxxhdpi │ │ └── node_modules_reactnavigation_src_views_assets_backicon.png │ │ ├── layout │ │ └── launch_screen.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-ldpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── 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.json ├── banner.jpg ├── index.js ├── ios ├── BAO-tvOS │ └── Info.plist ├── BAO-tvOSTests │ └── Info.plist ├── BAO.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── BAO-tvOS.xcscheme │ │ └── BAO.xcscheme ├── BAO │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon-1024@1x.png │ │ │ ├── icon-20@1x.png │ │ │ ├── icon-20@2x.png │ │ │ ├── icon-20@3x.png │ │ │ ├── icon-29@1x.png │ │ │ ├── icon-29@2x.png │ │ │ ├── icon-29@3x.png │ │ │ ├── icon-40@1x.png │ │ │ ├── icon-40@2x.png │ │ │ ├── icon-40@3x.png │ │ │ ├── icon-50@1x.png │ │ │ ├── icon-50@2x.png │ │ │ ├── icon-57@1x.png │ │ │ ├── icon-57@2x.png │ │ │ ├── icon-60@2x.png │ │ │ ├── icon-60@3x.png │ │ │ ├── icon-72@1x.png │ │ │ ├── icon-72@2x.png │ │ │ ├── icon-76@1x.png │ │ │ ├── icon-76@2x.png │ │ │ └── icon-83.5@2x.png │ │ ├── Contents.json │ │ └── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── Default-568h@2x.png │ │ │ ├── Default-667h@2x.png │ │ │ ├── Default-Landscape-736h@3x.png │ │ │ ├── Default-Landscape-812h@3x.png │ │ │ ├── Default-Landscape.png │ │ │ ├── Default-Landscape@2x.png │ │ │ ├── Default-Portrait-736h@3x.png │ │ │ ├── Default-Portrait-812h@3x.png │ │ │ ├── Default-Portrait.png │ │ │ ├── Default-Portrait@2x.png │ │ │ └── Default@2x.png │ ├── Info.plist │ └── main.m └── BAOTests │ ├── BAOTests.m │ └── Info.plist ├── package-lock.json ├── package.json ├── preview.jpg └── src ├── App.js ├── RNAsyncStorage.js ├── Router.js ├── actions ├── AuthActions.js ├── ColorActions.js ├── FeedsActions.js ├── index.js └── types.js ├── assets └── imgs │ ├── icon.png │ ├── icon_dark.png │ ├── icon_light.png │ └── splash.png ├── components ├── About.js ├── AddFeed.js ├── ArticleDetail.js ├── Drawer.js ├── FeedDetail.js ├── Feeds.js ├── LoginForm.js └── common │ ├── BottomAlert.js │ ├── Confirm.js │ ├── Container.js │ ├── HTMLContent.js │ ├── Input.js │ ├── ItemCard.js │ ├── Timer.js │ └── index.js └── reducers ├── AuthReducer.js ├── ColorReducer.js ├── ErrorReducer.js ├── FeedsReducer.js └── index.js /.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 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | module.system=haste 33 | 34 | munge_underscores=true 35 | 36 | 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' 37 | 38 | module.file_ext=.js 39 | module.file_ext=.jsx 40 | module.file_ext=.json 41 | module.file_ext=.native.js 42 | 43 | suppress_type=$FlowIssue 44 | suppress_type=$FlowFixMe 45 | suppress_type=$FlowFixMeProps 46 | suppress_type=$FlowFixMeState 47 | 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 51 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 52 | 53 | [version] 54 | ^0.67.0 55 | -------------------------------------------------------------------------------- /.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://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 JUN CHEN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

5 | A pure RSS reader app built with React Native 6 |

7 | 8 |

9 | *No longer under maintenance* 10 |

11 | 12 |
13 | 14 | ## Preview 15 | 16 | 17 | 18 |
19 | 20 | ## Dependencies 21 | 22 | * [react](https://github.com/facebook/react): 16.3.1 23 | * [react-native](https://github.com/facebook/react-native): 0.55.4 24 | * [redux](https://github.com/reduxjs/redux): 3.7.2 25 | * [react-redux](https://github.com/reduxjs/react-redux): 5.0.7 26 | * [redux-thunk](https://github.com/reduxjs/redux-thunk): 2.2.0 27 | * [react-native-router-flux](https://github.com/aksonov/react-native-router-flux): 4.0.0-beta.28 28 | * [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons): 4.5.0 29 | * [react-native-storage](https://github.com/sunnylqm/react-native-storage): 0.2.2 30 | * [react-native-splash-screen](https://github.com/crazycodeboy/react-native-splash-screen): 3.0.7 31 | * [react-native-render-html](https://github.com/archriss/react-native-render-html): 3.10.0 32 | * [react-native-modal](https://github.com/react-native-community/react-native-modal): 6.0.0 33 | * [react-native-elevated-view](https://github.com/alekhurst/react-native-elevated-view): 0.0.6 34 | * [leancloud-storage](https://github.com/leancloud/javascript-sdk): 3.6.3 35 | 36 |
37 | 38 | ## Running 39 | 40 | #### Clone & install 41 | 42 | * Clone this repo `git clone https://github.com/jaychenjun/BAO.git` 43 | * Run`cd BAO` 44 | * Run `npm install` 45 | 46 | #### iOS 47 | 48 | * Run `react-native run-ios` 49 | 50 | #### Android 51 | 52 | * Run `react-native run-android` 53 | 54 | Backend service is running on a free BaaS plan,
55 | supported by [LeanCloud](https://leancloud.cn/) with limited API reuqest per day,
56 | [Test Account: Bao, Password: aaa] ENJOY~ 57 |
58 | 59 | ## License 60 | 61 | Released under the [MIT License](http://opensource.org/licenses/MIT). 62 | -------------------------------------------------------------------------------- /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.bao", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.bao", 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 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion 26 98 | 99 | defaultConfig { 100 | applicationId "com.bao" 101 | minSdkVersion 16 102 | targetSdkVersion 26 103 | versionCode 1 104 | versionName "1.0" 105 | ndk { 106 | abiFilters "armeabi-v7a", "x86" 107 | } 108 | } 109 | 110 | splits { 111 | abi { 112 | reset() 113 | enable enableSeparateBuildPerCPUArchitecture 114 | universalApk false // If true, also generate a universal APK 115 | include "armeabi-v7a", "x86" 116 | } 117 | } 118 | 119 | signingConfigs { 120 | release { 121 | storeFile file("/Users/chenjun/workspace/BAO/android/app/my-release-key.keystore") 122 | storePassword 'freespeech' 123 | keyAlias 'my-key-alias' 124 | keyPassword 'freespeech' 125 | } 126 | } 127 | 128 | buildTypes { 129 | release { 130 | minifyEnabled enableProguardInReleaseBuilds 131 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 132 | signingConfig signingConfigs.release 133 | } 134 | } 135 | // applicationVariants are e.g. debug, release 136 | applicationVariants.all { variant -> 137 | variant.outputs.each { output -> 138 | // For each separate APK per architecture, set a unique version code as described here: 139 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 140 | def versionCodes = ["armeabi-v7a":1, "x86":2] 141 | def abi = output.getFilter(OutputFile.ABI) 142 | if (abi != null) { // null for the universal-debug, universal-release variants 143 | output.versionCodeOverride = 144 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 145 | } 146 | } 147 | } 148 | } 149 | 150 | dependencies { 151 | compile project(':react-native-vector-icons') 152 | compile project(':react-native-splash-screen') 153 | compile fileTree(dir: "libs", include: ["*.jar"]) 154 | compile "com.android.support:appcompat-v7:23.0.1" 155 | compile "com.facebook.react:react-native:+" // From node_modules 156 | } 157 | 158 | // Run this once to be able to run the application with BUCK 159 | // puts all compile dependencies into folder libs for BUCK to use 160 | task copyDownloadableDepsToLibs(type: Copy) { 161 | from configurations.compile 162 | into 'libs' 163 | } 164 | -------------------------------------------------------------------------------- /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 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/index.android.bundle.meta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/assets/index.android.bundle.meta -------------------------------------------------------------------------------- /android/app/src/main/java/com/bao/MainActivity.java: -------------------------------------------------------------------------------- 1 | // package com.bao; 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 "BAO"; 14 | // } 15 | // } 16 | package com.bao; 17 | 18 | import android.os.Bundle; // here 19 | // react-native-splash-screen >= 0.3.1 20 | import org.devio.rn.splashscreen.SplashScreen; // here 21 | import com.facebook.react.ReactActivity; 22 | 23 | public class MainActivity extends ReactActivity { 24 | 25 | /** 26 | * Returns the name of the main component registered from JavaScript. 27 | * This is used to schedule rendering of the component. 28 | */ 29 | @Override 30 | protected String getMainComponentName() { 31 | return "BAO"; 32 | } 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | SplashScreen.show(this, true); // here 37 | super.onCreate(savedInstanceState); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/bao/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.bao; 2 | 3 | import android.app.Application; 4 | import com.facebook.react.ReactApplication; 5 | import org.devio.rn.splashscreen.SplashScreenReactPackage; 6 | import com.oblador.vectoricons.VectorIconsPackage; 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 VectorIconsPackage(), 28 | new SplashScreenReactPackage() 29 | ); 30 | } 31 | 32 | @Override 33 | protected String getJSMainModuleName() { 34 | return "index"; 35 | } 36 | }; 37 | 38 | @Override 39 | public ReactNativeHost getReactNativeHost() { 40 | return mReactNativeHost; 41 | } 42 | 43 | @Override 44 | public void onCreate() { 45 | super.onCreate(); 46 | SoLoader.init(this, /* native exopackage */ false); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-hdpi/node_modules_reactnavigation_src_views_assets_backicon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-hdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-land-hdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-ldpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-land-ldpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-mdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-land-mdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-land-xhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xxhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-land-xxhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xxxhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-land-xxxhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/node_modules_reactnativerouterflux_images_back_chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-mdpi/node_modules_reactnativerouterflux_images_back_chevron.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/node_modules_reactnativerouterflux_images_menu_burger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-mdpi/node_modules_reactnativerouterflux_images_menu_burger.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_src_views_assets_backicon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/src_assets_imgs_icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-mdpi/src_assets_imgs_icon_dark.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/src_assets_imgs_icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-mdpi/src_assets_imgs_icon_light.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-hdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-port-hdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-ldpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-port-ldpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-mdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-port-mdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-port-xhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xxhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-port-xxhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xxxhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-port-xxxhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_src_views_assets_backicon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_src_views_assets_backicon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_src_views_assets_backicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_src_views_assets_backicon.png -------------------------------------------------------------------------------- /android/app/src/main/res/layout/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BAO 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /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 | // add google() here 7 | google() 8 | } 9 | dependencies { 10 | // classpath 'com.android.tools.build:gradle:2.2.3' 11 | // update from 2.2.3 to 3.1.0 12 | classpath 'com.android.tools.build:gradle:3.1.0' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | mavenLocal() 22 | jcenter() 23 | // add google() here 24 | google() 25 | maven { 26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 27 | url "$rootDir/../node_modules/react-native/android" 28 | } 29 | } 30 | } 31 | 32 | subprojects { 33 | project.configurations.all { 34 | resolutionStrategy.eachDependency { details -> 35 | if (details.requested.group == 'com.android.support' 36 | && !details.requested.name.contains('multidex') ) 37 | { 38 | details.useVersion "26.1.0" 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /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.enableAapt2=false // < --- add here 21 | org.gradle.configureondemand=true 22 | 23 | android.useDeprecatedNdk=true 24 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/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-2.14.1-all.zip 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | # distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip -------------------------------------------------------------------------------- /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 = 'BAO' 2 | include ':react-native-vector-icons' 3 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') 4 | include ':react-native-splash-screen' 5 | project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android') 6 | include ':app' 7 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BAO", 3 | "displayName": "BAO" 4 | } -------------------------------------------------------------------------------- /banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/banner.jpg -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import AV from 'leancloud-storage'; 4 | 5 | AV.initialize('53kSrxDfpv6V1xlK049NvuyL-gzGzoHsz', 'wMkllOE8SD5gNqxEWfLJ2TPT'); 6 | 7 | AppRegistry.registerComponent('BAO', () => App); 8 | -------------------------------------------------------------------------------- /ios/BAO-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/BAO-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/BAO.xcodeproj/xcshareddata/xcschemes/BAO-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/BAO.xcodeproj/xcshareddata/xcschemes/BAO.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/BAO/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow *window; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ios/BAO/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import "SplashScreen.h" // here 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | NSURL *jsCodeLocation; 19 | 20 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 21 | 22 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 23 | moduleName:@"BAO" 24 | initialProperties:nil 25 | launchOptions:launchOptions]; 26 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 27 | 28 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 29 | UIViewController *rootViewController = [UIViewController new]; 30 | rootViewController.view = rootView; 31 | self.window.rootViewController = rootViewController; 32 | [self.window makeKeyAndVisible]; 33 | [SplashScreen show]; // here 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ios/BAO/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/BAO/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon-29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "icon-29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon-40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "icon-40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "icon-57@1x.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "icon-57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "icon-60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "icon-60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "icon-20@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "icon-20@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "icon-29@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "icon-29@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "icon-40@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "icon-40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "icon-50@1x.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "icon-50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "icon-72@1x.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "icon-72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "icon-76@1x.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "icon-76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "icon-83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "icon-1024@1x.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-1024@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-20@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-29@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-40@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-50@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-50@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-57@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-57@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-72@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-72@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-76@1x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation": "portrait", 5 | "idiom": "iphone", 6 | "extent": "full-screen", 7 | "minimum-system-version": "11.0", 8 | "filename": "Default-Portrait-812h@3x.png", 9 | "subtype": "2436h", 10 | "scale": "3x" 11 | }, 12 | { 13 | "orientation": "landscape", 14 | "idiom": "iphone", 15 | "extent": "full-screen", 16 | "filename": "Default-Landscape-812h@3x.png", 17 | "minimum-system-version": "11.0", 18 | "subtype": "2436h", 19 | "scale": "3x" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "idiom" : "iphone", 24 | "subtype" : "736h", 25 | "filename" : "Default-Portrait-736h@3x.png", 26 | "minimum-system-version" : "8.0", 27 | "orientation" : "portrait", 28 | "scale" : "3x" 29 | }, 30 | { 31 | "extent" : "full-screen", 32 | "idiom" : "iphone", 33 | "subtype" : "736h", 34 | "filename" : "Default-Landscape-736h@3x.png", 35 | "minimum-system-version" : "8.0", 36 | "orientation" : "landscape", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "extent" : "full-screen", 41 | "idiom" : "iphone", 42 | "subtype" : "667h", 43 | "filename" : "Default-667h@2x.png", 44 | "minimum-system-version" : "8.0", 45 | "orientation" : "portrait", 46 | "scale" : "2x" 47 | }, 48 | { 49 | "orientation" : "portrait", 50 | "idiom" : "iphone", 51 | "filename" : "Default@2x.png", 52 | "extent" : "full-screen", 53 | "minimum-system-version" : "7.0", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "extent" : "full-screen", 58 | "idiom" : "iphone", 59 | "subtype" : "retina4", 60 | "filename" : "Default-568h@2x.png", 61 | "minimum-system-version" : "7.0", 62 | "orientation" : "portrait", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "orientation" : "landscape", 67 | "idiom" : "ipad", 68 | "filename" : "Default-Landscape.png", 69 | "extent" : "full-screen", 70 | "minimum-system-version" : "7.0", 71 | "scale" : "1x" 72 | }, 73 | { 74 | "orientation" : "landscape", 75 | "idiom" : "ipad", 76 | "filename" : "Default-Landscape@2x.png", 77 | "extent" : "full-screen", 78 | "minimum-system-version" : "7.0", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "orientation" : "portrait", 83 | "idiom" : "iphone", 84 | "extent" : "full-screen", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "orientation" : "portrait", 89 | "idiom" : "iphone", 90 | "extent" : "full-screen", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "orientation" : "portrait", 95 | "idiom" : "iphone", 96 | "extent" : "full-screen", 97 | "subtype" : "retina4", 98 | "scale" : "2x" 99 | }, 100 | { 101 | "orientation" : "portrait", 102 | "idiom" : "ipad", 103 | "filename" : "Default-Portrait.png", 104 | "extent" : "full-screen", 105 | "scale" : "1x" 106 | }, 107 | { 108 | "orientation" : "portrait", 109 | "idiom" : "ipad", 110 | "filename" : "Default-Portrait@2x.png", 111 | "extent" : "full-screen", 112 | "scale" : "2x" 113 | }, 114 | { 115 | "orientation" : "portrait", 116 | "idiom" : "ipad", 117 | "filename" : "Default-Portrait.png", 118 | "extent" : "full-screen", 119 | "minimum-system-version" : "7.0", 120 | "scale" : "1x" 121 | }, 122 | { 123 | "orientation" : "portrait", 124 | "idiom" : "ipad", 125 | "filename" : "Default-Portrait@2x.png", 126 | "extent" : "full-screen", 127 | "minimum-system-version" : "7.0", 128 | "scale" : "2x" 129 | }, 130 | ], 131 | "info" : { 132 | "version" : 1, 133 | "author" : "xcode" 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape-812h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape-812h@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait-812h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait-812h@3x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png -------------------------------------------------------------------------------- /ios/BAO/Images.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/ios/BAO/Images.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /ios/BAO/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | BAO 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | 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 | NSAllowsArbitraryLoads 44 | 45 | 46 | UIAppFonts 47 | 48 | Ionicons.ttf 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/BAO/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ios/BAOTests/BAOTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 16 | 17 | @interface BAOTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation BAOTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 44 | if (level >= RCTLogLevelError) { 45 | redboxError = message; 46 | } 47 | }); 48 | 49 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 50 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 51 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 52 | 53 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 54 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 55 | return YES; 56 | } 57 | return NO; 58 | }]; 59 | } 60 | 61 | RCTSetLogFunction(RCTDefaultLogFunction); 62 | 63 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 64 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /ios/BAOTests/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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bbb", 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 | "leancloud-storage": "3.6.3", 11 | "react": "16.3.1", 12 | "react-native": "0.55.4", 13 | "react-native-elevated-view": "0.0.6", 14 | "react-native-modal": "6.0.0", 15 | "react-native-render-html": "3.10.0", 16 | "react-native-router-flux": "4.0.0-beta.28", 17 | "react-native-splash-screen": "3.0.7", 18 | "react-native-storage": "0.2.2", 19 | "react-native-vector-icons": "4.5.0", 20 | "react-redux": "5.0.7", 21 | "redux": "3.7.2", 22 | "redux-thunk": "2.2.0" 23 | }, 24 | "devDependencies": { 25 | "babel-jest": "22.4.3", 26 | "babel-preset-react-native": "4.0.0", 27 | "jest": "22.4.3", 28 | "react-test-renderer": "16.3.0-alpha.3" 29 | }, 30 | "jest": { 31 | "preset": "react-native" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/preview.jpg -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { createStore, applyMiddleware } from 'redux'; 4 | import reducers from './reducers'; 5 | import ReduxThunk from 'redux-thunk'; // a middleware 6 | import Router from './Router'; 7 | import storage from './RNAsyncStorage'; 8 | 9 | class App extends Component { 10 | 11 | render() { 12 | // the second arg is for any initial state you wanna pass 13 | // the third arg is called store enhancers 14 | const store = createStore(reducers, {}, applyMiddleware(ReduxThunk)); 15 | 16 | return ( 17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | 24 | 25 | 26 | export default App; -------------------------------------------------------------------------------- /src/RNAsyncStorage.js: -------------------------------------------------------------------------------- 1 | import Storage from 'react-native-storage'; 2 | import { AsyncStorage } from 'react-native'; 3 | 4 | const storage = new Storage({ 5 | size: 10, 6 | storageBackend: AsyncStorage, 7 | defaultExpires: null, 8 | enableCache: true, 9 | }); 10 | 11 | global.storage = storage; -------------------------------------------------------------------------------- /src/Router.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import AV from 'leancloud-storage'; 3 | import { connect } from 'react-redux'; 4 | import { Text, View, Dimensions, Easing, Animated } from 'react-native'; 5 | import { Scene, Router, Stack, Actions, Tabs } from 'react-native-router-flux'; 6 | import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator'; 7 | import Feeds from './components/Feeds'; 8 | import FeedDetail from './components/FeedDetail'; 9 | import ArticleDetail from './components/ArticleDetail'; 10 | import AddFeed from './components/AddFeed'; 11 | import About from './components/About'; 12 | import Drawer from './components/Drawer'; 13 | import LoginForm from './components/LoginForm'; 14 | import { currentUserCheck, switchThemes } from './actions'; 15 | import Icon from 'react-native-vector-icons/Ionicons'; 16 | import SplashScreen from 'react-native-splash-screen' 17 | 18 | const { width } = Dimensions.get('window'); 19 | 20 | class RouterComponent extends PureComponent { 21 | 22 | componentWillMount() { 23 | global.storage.load({ key: 'theme' }) 24 | .then(theme => { 25 | if (theme == 'dark') this.props.switchThemes(); 26 | }); 27 | AV.User.currentAsync() 28 | .then((currentUser)=>{ 29 | this.props.currentUserCheck(currentUser); 30 | }) 31 | } 32 | 33 | componentDidMount() { 34 | this.timer = setTimeout(() => { 35 | SplashScreen.hide(); 36 | }, 1200); 37 | } 38 | 39 | componentWillUnmount() { 40 | clearTimeout(this.timer); 41 | } 42 | 43 | render() { 44 | return ( 45 | 46 | 54 | ({ 59 | transitionSpec: { 60 | duration: 450, 61 | easing: Easing.in(Easing.bezier(0.2833, 0.99, 0.31833, 0.99)), 62 | timing: Animated.timing, 63 | }, 64 | screenInterpolator: CardStackStyleInterpolator.forHorizontal 65 | }) } 66 | gestureResponseDistance={{ horizontal: 50 }} 67 | gesturesEnabled={ true } 68 | > 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ); 79 | } 80 | }; 81 | 82 | export default connect( null, {currentUserCheck, switchThemes } )(RouterComponent); 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/actions/AuthActions.js: -------------------------------------------------------------------------------- 1 | import AV from 'leancloud-storage'; 2 | import { Actions } from 'react-native-router-flux'; 3 | import { 4 | CURRENT_USER, 5 | USERNAME_CHANGED, 6 | PASSWORD_CHANGED, 7 | LOGIN_USER_SUCCESS, 8 | LOGIN_USER_FAIL, 9 | RESET, 10 | EMAIL_CHANGED, 11 | LOGOUT_SUCCESS, 12 | LOADING, 13 | ERROR_MESSAGE, 14 | CLEAR_ERROR 15 | } from './types'; 16 | 17 | export const reset = (currentForm) => { 18 | return { 19 | type: RESET, 20 | payload: currentForm 21 | } 22 | } 23 | 24 | export const currentUserCheck = (currentUser) => { 25 | return (dispatch) => { 26 | if (currentUser) { 27 | currentUserChecked(dispatch, currentUser); 28 | } 29 | } 30 | } 31 | 32 | export const currentUserChecked = (dispatch, user) =>{ 33 | dispatch({ 34 | type: CURRENT_USER, 35 | payload: user 36 | }) 37 | } 38 | 39 | export const usernameChanged = (text) => { 40 | return { 41 | type: USERNAME_CHANGED, 42 | payload: text 43 | }; 44 | }; 45 | 46 | export const passwordChanged = (text) => { 47 | return { 48 | type: PASSWORD_CHANGED, 49 | payload: text 50 | } 51 | }; 52 | 53 | export const emailChanged = (text) => { 54 | return { 55 | type: EMAIL_CHANGED, 56 | payload: text 57 | } 58 | } 59 | 60 | export const logInUser = ({ username, password }) => { 61 | return (dispatch) => { 62 | dispatch({ type: LOADING }); 63 | if ( username === '' || password === '' ) { 64 | loginUserFail(dispatch); 65 | authErrorMessage(dispatch, "error"); 66 | } 67 | AV.User.logIn(username, password) 68 | .then(user => { 69 | loginUserSuccess(dispatch, user) 70 | }) 71 | .catch((error) => { 72 | authErrorMessage(dispatch, "error") 73 | loginUserFail(dispatch); 74 | }); 75 | }; 76 | }; 77 | 78 | export const signUpUser = ({ username, email, password }) => { 79 | return (dispatch) => { 80 | dispatch({ type: LOADING }); 81 | if ( username == '' || email == '' || password == '') { 82 | loginUserFail(dispatch); 83 | authErrorMessage(dispatch, "error"); 84 | } 85 | else { 86 | let user = new AV.User(); 87 | const RSSFeeds = []; 88 | user.setUsername(username); 89 | user.setPassword(password); 90 | user.setEmail(email); 91 | 92 | user.signUp() 93 | .then( user => { 94 | user.set('RSSFeeds', RSSFeeds); 95 | user.save() 96 | .then(() => loginUserSuccess(dispatch, user) ) 97 | .catch(() => { 98 | loginUserFail(dispatch); 99 | authErrorMessage(dispatch, "error") 100 | }) 101 | }) 102 | .catch( () => { 103 | loginUserFail(dispatch); 104 | authErrorMessage(dispatch, "error") 105 | }); 106 | } 107 | } 108 | } 109 | 110 | export const logOut = () => { 111 | return (dispatch) => { 112 | AV.User.logOut() 113 | .then( () => logOutSuccess(dispatch) ) 114 | .catch( () => authErrorMessage(dispatch, "error") ) 115 | } 116 | } 117 | 118 | export const logOutSuccess = (dispatch) => { 119 | Actions.pop({refresh: {test: Math.random()}}) 120 | dispatch({ 121 | type: LOGOUT_SUCCESS 122 | }); 123 | } 124 | 125 | const loginUserSuccess = (dispatch, user) => { 126 | Actions.pop({refresh: {test: Math.random()}}) 127 | dispatch({ 128 | type: LOGIN_USER_SUCCESS, 129 | payload: user 130 | }); 131 | }; 132 | 133 | const loginUserFail = (dispatch) => { 134 | dispatch({ 135 | type: LOGIN_USER_FAIL 136 | }) 137 | } 138 | 139 | const authErrorMessage = (dispatch, error) => { 140 | dispatch({ 141 | type: ERROR_MESSAGE, 142 | payload: error 143 | }) 144 | } 145 | 146 | export const clearError = () => { 147 | return { 148 | type: CLEAR_ERROR 149 | } 150 | } 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /src/actions/ColorActions.js: -------------------------------------------------------------------------------- 1 | import { Actions } from 'react-native-router-flux'; 2 | import { 3 | SWITCH_THEMES 4 | } from './types'; 5 | 6 | export const switchThemes = () => { 7 | return ({ 8 | type: SWITCH_THEMES 9 | }) 10 | } -------------------------------------------------------------------------------- /src/actions/FeedsActions.js: -------------------------------------------------------------------------------- 1 | import AV from 'leancloud-storage'; 2 | import { Actions } from 'react-native-router-flux'; 3 | import { 4 | FEED_ADD, 5 | FEED_DELETE, 6 | FEED_EDIT, 7 | FEED_ADD_FAIL, 8 | FEED_ADD_SUCCESS, 9 | ADD_FEED_LOADING, 10 | FEEDS_FETCH_SUCCESS, 11 | FEEDS_CLEAR, 12 | FEED_DETAIL_FETCH_SUCCESS, 13 | FEED_DETAIL_FETCH_FAIL, 14 | HEADER_IS_REFRESHING, 15 | FOOTER_IS_REFRESHING, 16 | STOP_FOOTER_REFRESHING, 17 | MORE_FEED_DETAIL_FETCH_SUCCESS, 18 | NO_MORE, 19 | NO_MORE_RESET, 20 | ERROR_MESSAGE, 21 | CURRENT_FEED 22 | } from './types'; 23 | 24 | const api = 'https://api.rss2json.com/v1/api.json?rss_url='; 25 | const apiParams = '&api_key=b5gxz7tkpefjdnwkyv2vbttmaybnft653sfhxc83&count=30&order_by=pubDate'; 26 | 27 | export const setCurrentFeed = ( currentFeed ) => { 28 | return ({ 29 | type: CURRENT_FEED, 30 | payload: currentFeed 31 | }) 32 | } 33 | 34 | export const feedDetailFetch = ({ rss_url, rss_title, initialLoading }) => { 35 | return (dispatch) => { 36 | if (!initialLoading) { 37 | // disable initial headerrefreshing 38 | headerRefreashing(dispatch); 39 | } 40 | let feedsQuery = new AV.Query('Feeds'); 41 | feedsQuery.equalTo('owner', rss_url); 42 | feedsQuery.limit(18); 43 | feedsQuery.addDescending('pubDate'); 44 | feedsQuery.find() 45 | .then((oldFeeds) => { 46 | if ( oldFeeds.length == 0 ) { 47 | const paramsJson = { 48 | rss_url: rss_url, 49 | rss_title: rss_title 50 | }; 51 | AV.Cloud.run('firstFeedDetailFetch', paramsJson).then( () => { 52 | feedsQuery.find() 53 | .then((feeds) => { 54 | dispatch({ 55 | type: FEED_DETAIL_FETCH_SUCCESS, 56 | payload: feeds 57 | }) 58 | }) 59 | }, (err) => {}); 60 | } 61 | else { 62 | dispatch({ 63 | type: FEED_DETAIL_FETCH_SUCCESS, 64 | payload: oldFeeds 65 | }) 66 | } 67 | }) 68 | } 69 | } 70 | 71 | export const feedDetailFetchMore = ({ rss_url, num }) => { 72 | return (dispatch) => { 73 | dispatch({ 74 | type: FOOTER_IS_REFRESHING, 75 | }) 76 | let feedsQuery = new AV.Query('Feeds'); 77 | feedsQuery.equalTo('owner', rss_url); 78 | feedsQuery.addDescending('pubDate'); 79 | feedsQuery.skip(num) 80 | feedsQuery.limit(15); 81 | feedsQuery.find() 82 | .then((moreFeeds) => { 83 | if (moreFeeds.length == 0) { 84 | dispatch({ 85 | type: NO_MORE 86 | }) 87 | } 88 | else{ 89 | dispatch({ 90 | type: MORE_FEED_DETAIL_FETCH_SUCCESS, 91 | payload: moreFeeds 92 | }) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | export const noMoreReset = () => { 99 | return({ type: NO_MORE_RESET }) 100 | } 101 | 102 | export const stopFooterRefreshing = () => { 103 | return { type: STOP_FOOTER_REFRESHING }; 104 | } 105 | 106 | 107 | export const headerRefreashing = (dispatch) => { 108 | dispatch({ 109 | type: HEADER_IS_REFRESHING, 110 | }) 111 | }; 112 | 113 | export const feedsFetch = () => { 114 | return (dispatch) => { 115 | AV.User.currentAsync() 116 | .then( (currentUser) => { 117 | AV.User.become(currentUser._sessionToken) 118 | .then((user) => { 119 | return user.get('RSSFeeds'); 120 | }) 121 | .then((data) => { 122 | dispatch({ 123 | type: FEEDS_FETCH_SUCCESS, 124 | payload: data 125 | }); 126 | }); 127 | }) 128 | } 129 | } 130 | 131 | export const clearFeeds = () => { 132 | return ({ 133 | type: FEEDS_CLEAR 134 | }) 135 | } 136 | 137 | export const feedAdd = ({ rss_url }) => { 138 | return (dispatch) => { 139 | dispatch({ type: ADD_FEED_LOADING, }); 140 | if (rss_url === '') { 141 | feedAddFail(dispatch); 142 | errorMessage(dispatch, 'Empty url'); 143 | return false; 144 | } 145 | fetch(api + rss_url + apiParams) 146 | .then(response => response.json()) 147 | .then((data) => { 148 | if (data.status === 'ok') { 149 | AV.User.currentAsync() 150 | .then((currentUser) => { 151 | let RSSFeeds = currentUser.get('RSSFeeds'); 152 | for (let i = 0; i < RSSFeeds.length; i++) { 153 | // already exist 154 | if (RSSFeeds[i].rss_url == rss_url) { 155 | feedAddFail(dispatch) 156 | errorMessage(dispatch, 'Feed already exist') 157 | return false; 158 | } 159 | }; 160 | if (!data.feed.title) { 161 | feedAddFail(dispatch); 162 | errorMessage(dispatch, 'Failed'); 163 | return false; 164 | } 165 | let RSSFeed = { 166 | rss_url: rss_url, 167 | rss_title: data.feed.title 168 | }; 169 | RSSFeeds[RSSFeeds.length] = RSSFeed; 170 | currentUser.set('RSSFeeds', RSSFeeds); 171 | currentUser.save() 172 | .then(() => { 173 | AV.Cloud.run('addFeed', RSSFeed).then( () => { 174 | Actions.pop({refresh: {test: Math.random()}}) 175 | feedAddSuccess(dispatch, RSSFeeds); 176 | }, (err) => { 177 | feedAddFail(dispatch) 178 | errorMessage(dispatch, 'Failed') 179 | return false; 180 | }); 181 | 182 | }) 183 | .catch((error) => { 184 | feedAddFail(dispatch) 185 | errorMessage(dispatch, 'Failed') 186 | }) 187 | .catch( (error) => { 188 | feedAddFail(dispatch) 189 | errorMessage(dispatch, 'Failed') 190 | }); 191 | }) 192 | } 193 | else{ 194 | feedAddFail(dispatch) 195 | errorMessage(dispatch, 'Invalid url') 196 | } 197 | }) 198 | .catch((error)=> { 199 | feedAddFail(dispatch) 200 | errorMessage(dispatch, 'Invalid url') 201 | }) 202 | } 203 | }; 204 | 205 | export const feedAddFail = (dispatch) => { 206 | dispatch({ 207 | type: FEED_ADD_FAIL, 208 | }) 209 | }; 210 | 211 | export const feedAddSuccess = (dispatch, data) => { 212 | dispatch({ 213 | type: FEED_ADD_SUCCESS, 214 | payload: data 215 | }) 216 | }; 217 | 218 | export const feedDelete = ({ item }) => { 219 | return (dispatch) => { 220 | AV.User.currentAsync() 221 | .then((currentUser) => { 222 | let RSSFeeds = currentUser.get('RSSFeeds'); 223 | let num = false; 224 | for (let i = 0; i < RSSFeeds.length; i++) { 225 | if (RSSFeeds[i].rss_title == item.rss_title) { 226 | num = i; 227 | break; 228 | } 229 | } 230 | if (num === false) { 231 | errorMessage(dispatch, 'Failed') 232 | return false; 233 | } 234 | RSSFeeds.splice(num,1); 235 | currentUser.set('RSSFeeds', RSSFeeds); 236 | currentUser.save() 237 | .then(() => { 238 | Actions.pop({refresh: {test:Math.random()}}) 239 | dispatch({ 240 | type: FEED_DELETE, 241 | payload: RSSFeeds 242 | }) 243 | }) 244 | }) 245 | } 246 | }; 247 | 248 | export const feedEdit = ({ rss_url }) => { 249 | return { 250 | type: FEED_EDIT, 251 | payload: { rss_url } 252 | }; 253 | } 254 | 255 | export const eixtMessage = (error) => { 256 | return { 257 | type: ERROR_MESSAGE, 258 | payload: error 259 | } 260 | } 261 | 262 | export const errorMessage = (dispatch, error) => { 263 | dispatch({ 264 | type: ERROR_MESSAGE, 265 | payload: error 266 | }) 267 | } -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | export * from './AuthActions'; 2 | export * from './FeedsActions'; 3 | export * from './ColorActions'; 4 | -------------------------------------------------------------------------------- /src/actions/types.js: -------------------------------------------------------------------------------- 1 | // auth 2 | export const CURRENT_USER = 'current_user'; 3 | export const PASSWORD_CHANGED = 'password_changed'; 4 | export const USERNAME_CHANGED = 'username_changed'; 5 | export const EMAIL_CHANGED = 'email_changed'; 6 | export const LOGIN_USER_SUCCESS = 'login_user_success'; 7 | export const LOGIN_USER_FAIL = 'login_user_fail'; 8 | export const LOGOUT_SUCCESS = 'log_out_success' 9 | export const RESET = 'reset'; 10 | export const LOADING = 'loading'; 11 | 12 | // error 13 | export const ERROR_MESSAGE = 'error_message'; 14 | export const CLEAR_ERROR = 'clear_error'; 15 | 16 | // feed fetch&add 17 | export const FEED_ADD = 'feed_add'; 18 | export const FEED_DELETE = 'feed_delete'; 19 | export const FEED_EDIT = 'feed_edit'; 20 | export const FEED_ADD_FAIL = 'feed_add_fail'; 21 | export const FEED_ADD_SUCCESS = 'feed_add_success'; 22 | export const FEEDS_FETCH_SUCCESS = 'feeds_fetch_success'; 23 | export const FEEDS_CLEAR = "feeds_clear"; 24 | export const ADD_FEED_LOADING = 'add_feed_loading'; 25 | export const CURRENT_FEED = "current_feed"; 26 | 27 | // header&footer refresh 28 | export const FEED_DETAIL_FETCH_SUCCESS = 'feed_detail_fetch_success'; 29 | export const FEED_DETAIL_FETCH_FAIL = 'feed_detail_fetch_fail'; 30 | export const HEADER_IS_REFRESHING = 'header_is_refreshing'; 31 | export const FOOTER_IS_REFRESHING = 'footer_is_refreshing'; 32 | export const STOP_FOOTER_REFRESHING = 'stop_footer_refreshing'; 33 | export const MORE_FEED_DETAIL_FETCH_SUCCESS = 'more_feed_detail_fetch-success'; 34 | export const NO_MORE = 'no_more'; 35 | export const NO_MORE_RESET = 'no_more_reset'; 36 | 37 | // themes 38 | export const SWITCH_THEMES = 'switch_themes'; 39 | 40 | -------------------------------------------------------------------------------- /src/assets/imgs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/src/assets/imgs/icon.png -------------------------------------------------------------------------------- /src/assets/imgs/icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/src/assets/imgs/icon_dark.png -------------------------------------------------------------------------------- /src/assets/imgs/icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/src/assets/imgs/icon_light.png -------------------------------------------------------------------------------- /src/assets/imgs/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchenjun/BAO/9b4e6aee755362aac5dbfb5e7627086244df19fc/src/assets/imgs/splash.png -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Text, ScrollView, TouchableOpacity, View, Linking, StyleSheet, BackHandler, Image, Dimensions } from 'react-native'; 3 | import { ItemCard, Container } from './common'; 4 | import { Actions } from 'react-native-router-flux'; 5 | import Icon from 'react-native-vector-icons/Ionicons'; 6 | import { connect } from 'react-redux'; 7 | 8 | const { height } = Dimensions.get('window'); 9 | 10 | class About extends PureComponent{ 11 | 12 | onGithubPress() { 13 | Linking.openURL('https://github.com/jaychenjun') 14 | } 15 | 16 | onEmailPress() { 17 | Linking.openURL("mailto:jaychen97@foxmail.com") 18 | } 19 | 20 | onBlogPress() { 21 | Linking.openURL('http://mountaincity.me/') 22 | } 23 | 24 | onBackPress() { 25 | Actions.pop() 26 | } 27 | 28 | render() { 29 | const { themeColor, titleColor, icon } = this.props; 30 | const { headerContainerStyle, buttonContainerStyle, buttonStyle, textStyle, iconStyle } = styles; 31 | return( 32 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | BAO 44 | 45 | A pure RSS reader 46 | 47 | 48 | 49 | 50 | CONTACT 51 | 52 | 53 | 57 | 62 | 63 | 67 | 72 | 73 | 77 | 82 | 83 | 84 | 85 | 86 | 87 | ) 88 | } 89 | } 90 | 91 | const styles = StyleSheet.create({ 92 | headerContainerStyle: { 93 | alignSelf: 'flex-start', 94 | paddingLeft: 22, 95 | paddingRight: 30, 96 | paddingTop: 10, 97 | paddingBottom: 10, 98 | }, 99 | textStyle: { 100 | color:'#333', 101 | fontSize: 16, 102 | lineHeight: 26, 103 | }, 104 | buttonContainerStyle: { 105 | flexDirection: 'row', 106 | justifyContent: 'flex-start', 107 | alignItems: 'center', 108 | alignSelf: 'center', 109 | }, 110 | buttonStyle: { 111 | paddingLeft: 25 112 | }, 113 | iconStyle: { 114 | width: 200, 115 | height: 200, 116 | margin: 30, 117 | marginBottom: height*0.09 118 | } 119 | }) 120 | 121 | const mapStateToProps = state => { 122 | return { 123 | titleColor: state.themes.titleColor, 124 | themeColor: state.themes.themeColor, 125 | icon: state.themes.icon 126 | }; 127 | }; 128 | 129 | export default connect(mapStateToProps)(About); 130 | -------------------------------------------------------------------------------- /src/components/AddFeed.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Text, View, TouchableOpacity, ActivityIndicator, TextInput, BackHandler, StyleSheet } from 'react-native'; 3 | import { Actions } from 'react-native-router-flux'; 4 | import { Container, ItemCard, Input } from './common'; 5 | import { feedAdd, feedDelete, feedEdit } from '../actions'; 6 | import { connect } from 'react-redux'; 7 | import Icon from 'react-native-vector-icons/Ionicons'; 8 | 9 | 10 | class AddFeed extends PureComponent{ 11 | 12 | constructor(props){ 13 | super(props); 14 | this.onUrlChange = this.onUrlChange.bind(this); 15 | this.onAddPress = this.onAddPress.bind(this); 16 | } 17 | 18 | componentWillMount() { 19 | BackHandler.addEventListener('hardwareBackPress', this.onBackAndroid); 20 | } 21 | 22 | componentWillUnmount() { 23 | this.props.feedEdit({ rss_url: '' }) 24 | BackHandler.removeEventListener('hardwareBackPress', this.onBackAndroid); 25 | } 26 | 27 | onBackAndroid = () => { 28 | return this.props.loading? true: false; 29 | } 30 | 31 | onAddPress() { 32 | this.props.feedAdd({ rss_url: this.props.rss_url }) 33 | } 34 | 35 | onBackPress() { 36 | Actions.pop() 37 | } 38 | 39 | onUrlChange(text) { 40 | if (!this.props.loading) { 41 | this.props.feedEdit({ rss_url: text }) 42 | } 43 | } 44 | 45 | renderButton() { 46 | const { titleColor } = this.props; 47 | if (!this.props.loading) { 48 | return( 49 | 50 | 51 | 52 | Add 53 | 54 | 55 | 56 | ); 57 | } else { 58 | return( 59 | 60 | 61 | 62 | ) 63 | } 64 | 65 | } 66 | 67 | renderBackButton() { 68 | const { themeColor } = this.props; 69 | const { backIconStyle } = styles; 70 | if (!this.props.loading) { 71 | return( 72 | 73 | 74 | 75 | 76 | 77 | ); 78 | } else { 79 | return( 80 | 81 | 82 | 83 | ) 84 | } 85 | } 86 | 87 | render() { 88 | const { 89 | inputCardStyle 90 | } = styles; 91 | 92 | return( 93 | 94 | { this.renderBackButton() } 95 | 96 | 98 | 107 | 108 | { this.renderButton() } 109 | 110 | 111 | ); 112 | } 113 | } 114 | 115 | const styles = StyleSheet.create({ 116 | backIconStyle:{ 117 | alignSelf: 'flex-end', 118 | marginRight: 40, 119 | marginTop: 20, 120 | marginBottom: 10 121 | }, 122 | buttonStyle: { 123 | margin: 3, 124 | color: 'purple', 125 | alignSelf: 'flex-end', 126 | borderRadius: 4, 127 | borderWidth: 1, 128 | marginTop: 15, 129 | marginRight: 8, 130 | padding: 5, 131 | paddingLeft: 17, 132 | paddingRight: 17, 133 | justifyContent: 'center', 134 | }, 135 | loadingStyle: { 136 | alignSelf: 'flex-end', 137 | marginTop: 15, 138 | marginRight: 8, 139 | padding: 5, 140 | paddingLeft: 20, 141 | paddingRight: 31, 142 | justifyContent: 'center', 143 | }, 144 | inputCardStyle: { 145 | width: 300, 146 | paddingLeft: 4, 147 | paddingRight: 4, 148 | padding: 2, 149 | } 150 | }) 151 | 152 | const mapStateToProps = state => { 153 | return { 154 | rss_title: state.feed.rss_title, 155 | rss_url: state.feed.rss_url, 156 | loading: state.feed.loading, 157 | titleColor: state.themes.titleColor, 158 | themeColor: state.themes.themeColor 159 | }; 160 | }; 161 | 162 | export default connect(mapStateToProps, { feedAdd, feedDelete, feedEdit })(AddFeed); 163 | -------------------------------------------------------------------------------- /src/components/ArticleDetail.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Text, TouchableOpacity, View, ScrollView, Dimensions, Linking, StyleSheet } from 'react-native'; 3 | import { Container, HTMLContent, Confirm } from './common'; 4 | import { Actions } from 'react-native-router-flux'; 5 | import Icon from 'react-native-vector-icons/Ionicons'; 6 | import { connect } from 'react-redux'; 7 | import ElevatedView from 'react-native-elevated-view'; 8 | 9 | const { width } = Dimensions.get('window'); 10 | const articleWidth = width*0.93; 11 | const headerPadding = width*0.05; 12 | 13 | class ArticleDetail extends PureComponent { 14 | 15 | constructor(props){ 16 | super(props); 17 | this.state = { 18 | showContent: false, 19 | showModal: false 20 | }; 21 | this.onAccept = this.onAccept.bind(this); 22 | this.onDecline = this.onDecline.bind(this); 23 | this.onOpenButtonPress = this.onOpenButtonPress.bind(this); 24 | this.onFooterPress = this.onFooterPress.bind(this); 25 | } 26 | 27 | componentDidMount() { 28 | this.timer = setTimeout( 29 | () => { this.setState({ ...this.state, showContent: true }) }, 30 | 100 31 | ); 32 | } 33 | 34 | componentWillUnmount() { 35 | clearTimeout(this.timer); 36 | } 37 | 38 | onAccept() { 39 | this.setState({ showModal: false }); 40 | Linking.openURL(this.props.item.url); 41 | } 42 | 43 | onDecline() { 44 | this.setState({ showModal: false }); 45 | } 46 | 47 | onOpenButtonPress() { 48 | this.setState({ showModal: true }) 49 | } 50 | 51 | onHeaderTitlePress() { 52 | Actions.pop() 53 | } 54 | 55 | onFooterPress() { 56 | Linking.openURL(this.props.item.url) 57 | } 58 | 59 | renderUnderTitle() { 60 | const { 61 | author, 62 | sourceTitle, 63 | } = this.props.item; 64 | 65 | const { articleAuthorsStyle } = styles; 66 | 67 | if (this.props.item.author) { 68 | return( 69 | 70 | 75 | { sourceTitle } / by { author } 76 | 77 | 78 | ) 79 | } 80 | else { 81 | return( 82 | 83 | 88 | { sourceTitle } 89 | 90 | 91 | ) 92 | } 93 | } 94 | 95 | renderTitle() { 96 | const { 97 | articleTitleContainerStyle, 98 | articleTitleStyle, 99 | } = styles; 100 | 101 | const { itemCardBackgroundColor, titleColor } = this.props; 102 | 103 | const { 104 | sourceTitle, 105 | title, 106 | content 107 | } = this.props.item; 108 | return( 109 | 110 | 111 | 112 | { title.trim() } 113 | 114 | 115 | { this.renderUnderTitle() } 116 | 117 | ) 118 | } 119 | 120 | renderHTML() { 121 | if (this.state.showContent) { 122 | return( 123 | 126 | ) 127 | } 128 | } 129 | 130 | renderArticleFooter() { 131 | const { footterContainerStyle, footterButtonStyle, footterTextStyle } = styles; 132 | const { itemCardBackgroundColor, themeColor } = this.props; 133 | if (this.state.showContent) { 134 | return( 135 | 136 | 140 | Visit Website 141 | 142 | 143 | ) 144 | } 145 | } 146 | 147 | render() { 148 | const { 149 | headerContainerStyle, 150 | headerTitleStyle, 151 | headerTitleTextStyle, 152 | buttonStyle, 153 | contentContainerStyle, 154 | elevatedViewStyle 155 | } = styles; 156 | 157 | const { 158 | sourceTitle, 159 | title, 160 | content 161 | } = this.props.item; 162 | 163 | const { 164 | themeColor, titleColor, headerTitleColor 165 | } = this.props; 166 | 167 | return ( 168 | 169 | 170 | 171 | 172 | 173 | 178 | { sourceTitle } 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | { this.renderTitle() } 191 | { this.renderHTML() } 192 | { this.renderArticleFooter() } 193 | 194 | 195 | 200 | Open in Browser 201 | 202 | 203 | ); 204 | } 205 | 206 | }; 207 | const styles = StyleSheet.create({ 208 | contentContainerStyle: { 209 | width: articleWidth+6, 210 | alignSelf:'center' 211 | }, 212 | elevatedViewStyle: { 213 | margin: 3, 214 | marginTop: 2, 215 | marginBottom: 16, 216 | borderRadius: 5 217 | }, 218 | headerTitleStyle:{ 219 | flexDirection: 'row', 220 | padding: 2, 221 | paddingLeft: headerPadding, 222 | paddingRight: headerPadding, 223 | alignItems: 'center' 224 | }, 225 | headerTitleTextStyle: { 226 | fontSize: 20, 227 | lineHeight: 30, 228 | paddingLeft: 10, 229 | }, 230 | headerContainerStyle: { 231 | flexDirection: 'row', 232 | justifyContent: 'space-between', 233 | alignItems: 'center', 234 | height: 46, 235 | width: width, 236 | alignSelf: 'center' 237 | }, 238 | buttonStyle: { 239 | padding: 2, 240 | paddingLeft: headerPadding, 241 | paddingRight: headerPadding 242 | }, 243 | articleTitleContainerStyle: { 244 | width: articleWidth, 245 | padding: 12, 246 | paddingBottom: 20 247 | }, 248 | articleTitleStyle: { 249 | fontSize: 25, 250 | lineHeight: 36, 251 | marginBottom: 10, 252 | fontWeight: 'bold' 253 | }, 254 | articleAuthorsStyle: { 255 | fontSize: 14, 256 | color: '#888', 257 | }, 258 | footterContainerStyle: { 259 | justifyContent: 'center', 260 | alignItems: 'stretch', 261 | width: articleWidth 262 | }, 263 | footterButtonStyle: { 264 | borderWidth: 1, 265 | borderRadius: 5, 266 | height: 46, 267 | flex: 1, 268 | justifyContent: 'center', 269 | alignItems: 'center', 270 | margin: 20, 271 | }, 272 | footterTextStyle: { 273 | fontSize: 14, 274 | } 275 | }); 276 | 277 | const mapStateToProps = state => { 278 | return { 279 | themeColor: state.themes.themeColor, 280 | titleColor: state.themes.titleColor, 281 | headerTitleColor: state.themes.headerTitleColor, 282 | itemCardBackgroundColor: state.themes.itemCardBackgroundColor 283 | }; 284 | }; 285 | 286 | export default connect(mapStateToProps)(ArticleDetail); -------------------------------------------------------------------------------- /src/components/Drawer.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { View, Text, ScrollView, TouchableOpacity, Dimensions, StyleSheet, Image } from 'react-native'; 3 | import { connect } from 'react-redux'; 4 | import { Container, Confirm } from './common'; 5 | import { Actions } from 'react-native-router-flux'; 6 | import LoginForm from './LoginForm'; 7 | import Icon from 'react-native-vector-icons/Ionicons'; 8 | import { logOut, switchThemes } from '../actions'; 9 | 10 | const { width, height } = Dimensions.get('window'); 11 | 12 | class Drawer extends PureComponent { 13 | 14 | constructor(props){ 15 | super(props); 16 | this.state = { showModal: false}; 17 | this.onLogOutButtonPress = this.onLogOutButtonPress.bind(this); 18 | this.onAccept = this.onAccept.bind(this); 19 | this.onDecline = this.onDecline.bind(this); 20 | this.onSwitchThemesPress = this.onSwitchThemesPress.bind(this) 21 | } 22 | 23 | onAccept() { 24 | this.props.logOut(); 25 | this.setState({ showModal: false }); 26 | } 27 | 28 | onDecline() { 29 | this.setState({ showModal: false }); 30 | } 31 | 32 | onLogOutButtonPress() { 33 | this.setState({ showModal: true }) 34 | } 35 | 36 | onSwitchThemesPress() { 37 | this.props.switchThemes() 38 | } 39 | 40 | onHomePress() { 41 | Actions.drawerClose() 42 | } 43 | 44 | onAboutPress() { 45 | Actions.push('About') 46 | } 47 | 48 | onLoginPress() { 49 | Actions.LoginForm() 50 | } 51 | 52 | renderUserStatus() { 53 | const { 54 | userInfoContainerStyle, 55 | userInfoTextStyle, 56 | logInIconStyle 57 | } = styles; 58 | const { currentUser, themeColor, borderColor } = this.props; 59 | 60 | if (currentUser) { 61 | return ( 62 | 63 | 64 | { currentUser.attributes.username } 65 | 66 | 67 | ); 68 | } 69 | 70 | else { 71 | return ( 72 | 73 | 78 | 79 | Log In 80 | 81 | 82 | 83 | 84 | ); 85 | } 86 | } 87 | 88 | renderAfterLoggedIn() { 89 | const { 90 | containerStyle, 91 | textStyle 92 | } = styles; 93 | const { titleColor } = this.props; 94 | if (this.props.currentUser) { 95 | return ( 96 | 97 | 98 | 99 | Switch themes 100 | 101 | 102 | 103 | 104 | Log out 105 | 106 | 107 | 112 | Log Out 113 | 114 | 115 | ); 116 | } 117 | } 118 | 119 | render() { 120 | const { 121 | containerStyle, 122 | textStyle, 123 | iconStyle 124 | } = styles; 125 | const { titleColor, icon } = this.props; 126 | return( 127 | 128 | 129 | { this.renderUserStatus() } 130 | 131 | 132 | Home 133 | 134 | 135 | { this.renderAfterLoggedIn() } 136 | 137 | 138 | About 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | ); 147 | }; 148 | }; 149 | 150 | const styles = StyleSheet.create({ 151 | userInfoContainerStyle:{ 152 | marginTop: height*0.15, 153 | marginBottom: height*0.17, 154 | marginLeft: 10, 155 | marginRight: 10, 156 | paddingLeft: 15, 157 | paddingBottom: height*0.05, 158 | flexDirection: 'row', 159 | alignItems: 'center', 160 | borderBottomWidth: 1 161 | }, 162 | userInfoTextStyle:{ 163 | fontSize: 30, 164 | }, 165 | logInIconStyle: { 166 | marginLeft: 7, 167 | marginTop: 6 168 | }, 169 | containerStyle:{ 170 | flexDirection: 'row', 171 | justifyContent: 'flex-start', 172 | alignItems: 'center', 173 | margin: 10, 174 | marginLeft: 30, 175 | width: 150, 176 | flex:1, 177 | }, 178 | textStyle: { 179 | color: '#444', 180 | fontSize: 16 181 | }, 182 | iconStyle: { 183 | width: 35, 184 | height: 35, 185 | position: 'absolute', 186 | bottom: 30, 187 | right: 30, 188 | } 189 | }) 190 | 191 | const mapStateToProps = state => { 192 | return { 193 | currentUser: state.auth.currentUser, 194 | themeColor: state.themes.themeColor, 195 | titleColor: state.themes.titleColor, 196 | borderColor: state.themes.borderColor, 197 | icon: state.themes.icon 198 | } ; 199 | }; 200 | 201 | export default connect(mapStateToProps, { logOut, switchThemes })(Drawer); -------------------------------------------------------------------------------- /src/components/FeedDetail.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import AV from 'leancloud-storage'; 3 | import { Text, TouchableOpacity, View, ActivityIndicator, FlatList, RefreshControl, Dimensions, StyleSheet } from 'react-native'; 4 | import { ItemCard, Container, Confirm, Timer } from './common'; 5 | import { Actions } from 'react-native-router-flux'; 6 | import { connect } from 'react-redux'; 7 | import { feedDetailFetch, feedDelete, feedDetailFetchMore, stopFooterRefreshing, noMoreReset } from '../actions'; 8 | import Icon from 'react-native-vector-icons/Ionicons'; 9 | 10 | const { width } = Dimensions.get('window'); 11 | const headerPadding = width*0.05; 12 | const touchableWidth = width*0.9+7; 13 | 14 | class FeedDetail extends PureComponent { 15 | 16 | constructor(props){ 17 | super(props); 18 | this.state = { showModal: false }; 19 | this.renderItem = this.renderItem.bind(this); 20 | this.onHeaderRefresh = this.onHeaderRefresh.bind(this); 21 | this.onFooterRefresh = this.onFooterRefresh.bind(this); 22 | this.onAccept = this.onAccept.bind(this); 23 | this.onDecline = this.onDecline.bind(this); 24 | this.onDeleteButtonPress = this.onDeleteButtonPress.bind(this); 25 | } 26 | 27 | componentWillMount() { 28 | const { rss_url, rss_title } = this.props.item; 29 | const { feedDetail } = this.props; 30 | const { 31 | footerRefreshing, 32 | stopFooterRefreshing, 33 | feedDetailFetch, 34 | noMore, 35 | noMoreReset 36 | } = this.props; 37 | if (footerRefreshing) { stopFooterRefreshing() } 38 | if (noMore) { noMoreReset() } 39 | } 40 | 41 | onAccept() { 42 | const { item } = this.props; 43 | this.props.feedDelete({ item }) 44 | this.setState({ showModal: false }); 45 | } 46 | 47 | onDecline() { 48 | this.setState({ showModal: false }); 49 | } 50 | 51 | onDeleteButtonPress() { 52 | this.setState({ showModal: true }) 53 | } 54 | 55 | onBackPress() { 56 | Actions.pop() 57 | } 58 | 59 | onHeaderRefresh() { 60 | if (!this.props.footerRefreshing) { 61 | const { rss_url, rss_title } = this.props.item; 62 | this.props.feedDetailFetch({ 63 | rss_url: rss_url, 64 | rss_title: rss_title, 65 | initialLoading: false 66 | }) 67 | } 68 | } 69 | 70 | onFooterRefresh() { 71 | const { 72 | headerRefreashing, 73 | footerRefreshing, 74 | feedDetailFetchMore, 75 | noMore 76 | } = this.props; 77 | 78 | if (footerRefreshing == false && headerRefreashing == false && noMore == false) { 79 | let num = this.props.feedDetail.length; 80 | const { rss_url, rss_title } = this.props.item; 81 | feedDetailFetchMore({ rss_url: rss_url, num: num }); 82 | } 83 | } 84 | 85 | renderFooterRefresh() { 86 | const { 87 | headerRefreashing, 88 | footerRefreshing, 89 | noMore, 90 | themeColor 91 | } = this.props; 92 | const { footerRefreshStyle } = styles; 93 | 94 | if (noMore) { 95 | return( 96 | 97 | - The End - 98 | 99 | ) 100 | } 101 | if (footerRefreshing == true && headerRefreashing == false) { 102 | return( 103 | 104 | 105 | 106 | ) 107 | } 108 | } 109 | 110 | renderItem({ item }) { 111 | const { attributes: article } = item; 112 | const { titleStyle, touchableStyle } = styles; 113 | const { titleColor } = this.props; 114 | 115 | return ( 116 | Actions.ArticleDetail({ item: article }) } style={ touchableStyle }> 117 | 118 | 123 | { article.title } 124 | 125 | { article.pubDate } 126 | 127 | 128 | ); 129 | } 130 | 131 | renderList() { 132 | const { rss_url, rss_title } = this.props.item; 133 | const { feedDetail, headerRefreashing, footerRefreshing, feedDetailFetch, themeColor } = this.props; 134 | const { loadingStyle } = styles; 135 | 136 | if (!feedDetail[0] || feedDetail[0].attributes.owner != rss_url) { 137 | feedDetailFetch({ rss_url: rss_url, rss_title: rss_title, initialLoading: true }); 138 | return ( 139 | 140 | 141 | 142 | ); 143 | } 144 | else if (feedDetail[0].attributes.owner === rss_url) { 145 | return ( 146 | 154 | } 155 | onEndReached={ this.onFooterRefresh } 156 | onEndReachedThreshold={ 0.1 } 157 | ListFooterComponent={ this.renderFooterRefresh() } 158 | data={ feedDetail } 159 | renderItem={ this.renderItem } 160 | keyExtractor={ (item) => item.id } 161 | extraData={ this.props } 162 | // including the margins 163 | // getItemLayout={(data, index) => ( 164 | // {length: 94, offset: 94 * index, index} 165 | // )} 166 | initialListSize={ 18 } 167 | /> 168 | ) 169 | } 170 | } 171 | 172 | render() { 173 | const { 174 | headerContainerStyle, 175 | headerTitleStyle, 176 | headerTitleTextStyle, 177 | buttonStyle 178 | } = styles; 179 | const { rss_title, rss_url } = this.props.item; 180 | const { themeColor } = this.props; 181 | 182 | return ( 183 | 184 | 185 | 186 | 187 | 188 | 189 | 194 | { rss_title } 195 | 196 | 197 | 198 | 199 | 200 | 201 | { this.renderList() } 202 | 207 | Unfollow 208 | 209 | 210 | ); 211 | } 212 | } 213 | 214 | const styles = StyleSheet.create({ 215 | titleStyle: { 216 | flex: 1, 217 | fontSize: 16, 218 | lineHeight: 25 219 | }, 220 | headerTitleStyle:{ 221 | flex: 1 222 | }, 223 | headerTitleTextStyle: { 224 | fontSize: 20, 225 | lineHeight: 30 226 | }, 227 | headerContainerStyle: { 228 | flexDirection: 'row', 229 | justifyContent: 'space-between', 230 | alignItems: 'center', 231 | height: 46, 232 | width: width, 233 | alignSelf: 'center' 234 | }, 235 | buttonStyle: { 236 | padding: 2, 237 | paddingLeft: headerPadding, 238 | paddingRight: headerPadding 239 | }, 240 | loadingStyle: { 241 | marginTop: 100 242 | }, 243 | footerRefreshStyle: { 244 | flex: 1, 245 | height: 94 , 246 | alignItems: 'center', 247 | justifyContent: 'center' 248 | }, 249 | touchableStyle: { 250 | width: touchableWidth, 251 | alignSelf:'center', 252 | flex: 1 253 | } 254 | }) 255 | 256 | const mapStateToProps = state => { 257 | return { 258 | feedDetail: state.feed.feedDetail, 259 | themeColor: state.themes.themeColor, 260 | titleColor: state.themes.titleColor, 261 | headerRefreashing: state.feed.headerRefreashing, 262 | footerRefreshing: state.feed.footerRefreshing, 263 | noMore: state.feed.noMore 264 | } 265 | }; 266 | 267 | export default connect(mapStateToProps, { 268 | feedDetailFetch, 269 | feedDelete, 270 | feedDetailFetchMore, 271 | stopFooterRefreshing, 272 | noMoreReset 273 | })(FeedDetail); -------------------------------------------------------------------------------- /src/components/Feeds.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { View, Text, FlatList, TouchableOpacity, ActivityIndicator, Dimensions, BackHandler, StyleSheet } from 'react-native'; 3 | import { Card, Container, ItemCard } from './common'; 4 | import AV from 'leancloud-storage'; 5 | import LoginForm from './LoginForm'; 6 | import { Actions } from 'react-native-router-flux'; 7 | import { connect } from 'react-redux'; 8 | import { feedsFetch, eixtMessage, setCurrentFeed, clearFeeds } from '../actions'; 9 | import Icon from 'react-native-vector-icons/Ionicons'; 10 | 11 | const { width } = Dimensions.get('window'); 12 | const headerPadding = width*0.05; 13 | const touchableWidth = width*0.9+7; 14 | 15 | class Feeds extends PureComponent { 16 | constructor(props){ 17 | super(props) 18 | this.renderItem = this.renderItem.bind(this) 19 | } 20 | 21 | onBackAndroid = () => { 22 | if (Actions.currentScene != 'Feeds') return false; 23 | if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) { 24 | // BackHandler.exitApp(); 25 | return false; 26 | } 27 | this.lastBackPressed = Date.now(); 28 | this.props.eixtMessage('Press again to exit the app'); 29 | return true; 30 | } 31 | 32 | componentWillMount() { 33 | this.props.feedsFetch(); 34 | BackHandler.addEventListener('hardwareBackPress', this.onBackAndroid); 35 | } 36 | 37 | componentWillReceiveProps(nextProps) { 38 | if ( nextProps.currentUser && (this.props.currentUser !== nextProps.currentUser)) { 39 | this.props.feedsFetch(); 40 | } 41 | if (!nextProps.currentUser) { 42 | this.props.clearFeeds(); 43 | } 44 | } 45 | 46 | componentWillUnmount() { 47 | BackHandler.removeEventListener('hardwareBackPress', this.onBackAndroid); 48 | } 49 | 50 | onDrawerOpenPress() { 51 | Actions.drawerOpen() 52 | } 53 | 54 | onAddIconPress() { 55 | Actions.AddFeed() 56 | } 57 | 58 | renderAddIcon() { 59 | const { currentUser, themeColor } = this.props; 60 | const { buttonStyle } = styles; 61 | 62 | return currentUser?( 63 | 64 | 65 | 66 | ):( 67 | 68 | 69 | 70 | ) 71 | } 72 | 73 | renderItem({ item }) { 74 | const { bigTitleColor } = this.props; 75 | const { titleStyle, touchableStyle } = styles; 76 | return ( 77 | { 79 | this.props.setCurrentFeed(item.rss_title) 80 | Actions.FeedDetail({ item }); 81 | } } 82 | style={ touchableStyle } 83 | > 84 | 85 | 90 | { item.rss_title } 91 | 92 | 93 | 94 | ); 95 | } 96 | 97 | renderList() { 98 | const { loadingStyle, listStyle } = styles; 99 | const { currentUser, RSSFeeds, themeColor, feedsFetch } = this.props; 100 | 101 | if (!currentUser) { 102 | return 103 | } 104 | if (!RSSFeeds) { 105 | if (currentUser) { 106 | return ( 107 | 108 | 109 | 110 | ) 111 | } 112 | else { 113 | return 114 | } 115 | } 116 | if (!RSSFeeds.length) { 117 | return 118 | } 119 | return( 120 | this.renderItem(item) } 123 | keyExtractor={(feed) => feed.rss_url } 124 | style = { listStyle } 125 | extraData={ this.props } 126 | /> 127 | ) 128 | } 129 | 130 | render() { 131 | const { headerContainerStyle, buttonStyle } = styles; 132 | const { themeColor } = this.props; 133 | return( 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | { this.renderAddIcon() } 145 | 146 | { this.renderList() } 147 | 148 | ); 149 | } 150 | } 151 | 152 | const styles = StyleSheet.create({ 153 | titleStyle: { 154 | lineHeight: 30, 155 | flex: 1, 156 | fontSize: 23, 157 | alignSelf: 'center', 158 | fontWeight: 'bold', 159 | }, 160 | headerContainerStyle: { 161 | flexDirection: 'row', 162 | justifyContent: 'space-between', 163 | alignItems: 'center', 164 | height: 45, 165 | width: width, 166 | alignSelf: 'center' 167 | }, 168 | buttonStyle: { 169 | padding: 2, 170 | paddingLeft: headerPadding, 171 | paddingRight: headerPadding 172 | }, 173 | listStyle: { 174 | marginBottom: 10 175 | }, 176 | loadingStyle: { 177 | marginTop: 100 178 | }, 179 | touchableStyle: { 180 | width: touchableWidth, 181 | alignSelf:'center' 182 | } 183 | }) 184 | 185 | const mapStateToProps = state => { 186 | return { 187 | currentUser: state.auth.currentUser, 188 | RSSFeeds: state.feed.feeds, 189 | themeColor: state.themes.themeColor, 190 | bigTitleColor: state.themes.bigTitleColor 191 | }; 192 | }; 193 | 194 | export default connect(mapStateToProps, { feedsFetch, eixtMessage, setCurrentFeed, clearFeeds })(Feeds); -------------------------------------------------------------------------------- /src/components/LoginForm.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Text, View, TouchableOpacity, ActivityIndicator, BackHandler, StyleSheet } from 'react-native'; 3 | import { Actions } from 'react-native-router-flux'; 4 | import { Container, Input, ItemCard } from './common'; 5 | import { connect } from 'react-redux'; 6 | import Icon from 'react-native-vector-icons/Ionicons'; 7 | import { 8 | usernameChanged, 9 | passwordChanged, 10 | emailChanged, 11 | logInUser, 12 | signUpUser, 13 | reset, 14 | } from '../actions'; 15 | 16 | const logIn = "logInForm"; 17 | const SignUp = 'SignUpForm'; 18 | 19 | class LoginForm extends PureComponent { 20 | 21 | constructor(props){ 22 | super(props); 23 | this.onSignUpButtonPress = this.onSignUpButtonPress.bind(this); 24 | this.onLogInButtonPress = this.onLogInButtonPress.bind(this); 25 | this.onUsernameChange = this.onUsernameChange.bind(this); 26 | this.onPasswordChange = this.onPasswordChange.bind(this); 27 | this.onEmailChange = this.onEmailChange.bind(this); 28 | } 29 | 30 | componentWillMount() { 31 | BackHandler.addEventListener('hardwareBackPress', this.onBackAndroid); 32 | } 33 | 34 | componentWillUnmount() { 35 | this.props.reset(logIn); 36 | BackHandler.removeEventListener('hardwareBackPress', this.onBackAndroid); 37 | } 38 | 39 | onBackAndroid = () => { 40 | return this.props.loading? true: false; 41 | } 42 | 43 | onUsernameChange(text) { 44 | if (!this.props.loading) { 45 | this.props.usernameChanged(text); 46 | } 47 | } 48 | 49 | onPasswordChange(text) { 50 | if (!this.props.loading) { 51 | this.props.passwordChanged(text); 52 | } 53 | } 54 | 55 | onEmailChange(text) { 56 | if (!this.props.loading) { 57 | this.props.emailChanged(text); 58 | } 59 | } 60 | 61 | onLogInButtonPress(){ 62 | if (!this.props.loading) { 63 | const { username, password } = this.props; 64 | this.props.logInUser({ username, password }); 65 | } 66 | } 67 | 68 | onSignUpButtonPress(){ 69 | if (!this.props.loading) { 70 | const { username, email ,password } = this.props; 71 | this.props.signUpUser({ username, email ,password }); 72 | } 73 | } 74 | 75 | renderSignUpSubmit() { 76 | const { 77 | loadingStyle, 78 | loginBottonStyle 79 | } = styles; 80 | const { titleColor } = this.props; 81 | 82 | if (this.props.loading) { 83 | return( 84 | 85 | 86 | 87 | ); 88 | } else { 89 | return( 90 | 91 | 92 | Submit 93 | 94 | 95 | ); 96 | } 97 | } 98 | 99 | renderLogInSubmit() { 100 | const { 101 | loadingStyle, 102 | loginBottonStyle 103 | } = styles; 104 | const { titleColor } = this.props; 105 | 106 | if (this.props.loading) { 107 | return( 108 | 109 | 110 | 111 | ); 112 | } else { 113 | return( 114 | 115 | 116 | Log In 117 | 118 | 119 | ); 120 | } 121 | } 122 | 123 | renderForm() { 124 | const { 125 | username, 126 | loading, 127 | password, 128 | email, 129 | titleColor, 130 | } = this.props; 131 | 132 | const { 133 | textStyle, 134 | bottonContainer, 135 | signupBottonStyle, 136 | inputCardStyle 137 | } = styles; 138 | 139 | if (this.props.currentForm === logIn) { 140 | return( 141 | 142 | 143 | Log In 144 | 145 | 146 | 147 | } 149 | placeholder = "username" 150 | onChangeText = { this.onUsernameChange } 151 | value = { username } 152 | editable = { !loading } 153 | autoCapitalize = "none" 154 | maxLength = { 10 } 155 | /> 156 | 157 | 158 | 159 | } 161 | placeholder="******" 162 | secureTextEntry 163 | onChangeText={ this.onPasswordChange } 164 | value={ password } 165 | editable= { !loading } 166 | autoCapitalize = "none" 167 | maxLength = { 15 } 168 | /> 169 | 170 | 171 | 172 | 173 | { 174 | if (!loading) { 175 | this.props.reset(SignUp); 176 | } 177 | } 178 | }> 179 | 180 | Sign Up 181 | 182 | 183 | { this.renderLogInSubmit() } 184 | 185 | 186 | ); 187 | } 188 | 189 | else{ 190 | return ( 191 | 192 | 193 | Sign Up 194 | 195 | 196 | 197 | } 199 | placeholder="username" 200 | onChangeText={ this.onUsernameChange } 201 | value={ username } 202 | editable= { !loading } 203 | autoCapitalize = "none" 204 | maxLength = { 10 } 205 | /> 206 | 207 | 208 | 209 | } 211 | placeholder="user@user.com" 212 | onChangeText={ this.onEmailChange } 213 | value={ email } 214 | editable= { !loading } 215 | autoCapitalize = "none" 216 | /> 217 | 218 | 219 | 220 | } 222 | placeholder="******" 223 | secureTextEntry 224 | onChangeText={ this.onPasswordChange } 225 | value={ password } 226 | editable= { !loading } 227 | autoCapitalize = "none" 228 | maxLength = { 15 } 229 | /> 230 | 231 | 232 | 233 | { 234 | if (!loading) { 235 | this.props.reset(logIn); 236 | } 237 | } 238 | }> 239 | 240 | Log In 241 | 242 | 243 | { this.renderSignUpSubmit() } 244 | 245 | 246 | ); 247 | } 248 | } 249 | 250 | onBackPress() { 251 | Actions.pop() 252 | } 253 | 254 | renderBackButton() { 255 | const { themeColor } = this.props; 256 | const { backIconStyle } = styles; 257 | if (!this.props.loading) { 258 | return( 259 | 260 | 261 | 262 | 263 | 264 | ); 265 | } 266 | else { 267 | return( 268 | 269 | 270 | 271 | ) 272 | } 273 | } 274 | 275 | render() { 276 | return ( 277 | 278 | 279 | { this.renderBackButton() } 280 | 281 | { this.renderForm() } 282 | 283 | 284 | ); 285 | } 286 | }; 287 | 288 | const styles = StyleSheet.create({ 289 | bottonContainer:{ 290 | width: 300, 291 | flexDirection: 'row', 292 | justifyContent: 'flex-end', 293 | marginTop: 15 294 | }, 295 | loginBottonStyle:{ 296 | fontSize: 14, 297 | color: 'purple', 298 | alignSelf: 'stretch', 299 | borderRadius: 4, 300 | borderWidth: 1, 301 | width: 70, 302 | marginRight: 7, 303 | paddingTop: 4, 304 | paddingBottom: 4, 305 | textAlign: 'center', 306 | }, 307 | signupBottonStyle:{ 308 | fontSize: 14, 309 | color: '#555', 310 | width: 65, 311 | paddingTop: 4, 312 | paddingBottom: 44, 313 | }, 314 | textStyle:{ 315 | fontSize: 40, 316 | alignSelf: 'flex-start', 317 | marginLeft: 10, 318 | marginBottom: 10, 319 | }, 320 | loadingStyle: { 321 | alignSelf: 'stretch', 322 | width: 70, 323 | marginRight: 7, 324 | paddingTop: 4, 325 | paddingBottom: 4, 326 | }, 327 | headerContainerStyle: { 328 | marginLeft: 12, 329 | marginBottom: 40, 330 | marginTop: 10 331 | }, 332 | backIconStyle:{ 333 | alignSelf: 'flex-end', 334 | marginRight: 40, 335 | marginTop: 20, 336 | marginBottom: 10 337 | }, 338 | inputCardStyle: { 339 | width: 290, 340 | paddingLeft: 2, 341 | paddingRight: 2, 342 | padding: 2, 343 | marginTop: 4 344 | } 345 | }); 346 | 347 | const mapStateToProps = state => { 348 | return { 349 | username: state.auth.username, 350 | password: state.auth.password, 351 | email: state.auth.email, 352 | user: state.auth.currentUser.attributes, 353 | loading: state.auth.loading, 354 | currentForm: state.auth.currentForm, 355 | currentForm: state.auth.currentForm, 356 | themeColor: state.themes.themeColor, 357 | titleColor: state.themes.titleColor 358 | }; 359 | }; 360 | 361 | export default connect( 362 | mapStateToProps, 363 | { 364 | usernameChanged, 365 | passwordChanged, 366 | logInUser, 367 | signUpUser, 368 | reset, 369 | emailChanged, 370 | } 371 | )(LoginForm); -------------------------------------------------------------------------------- /src/components/common/BottomAlert.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { View, Text, Dimensions, StyleSheet } from 'react-native'; 3 | import { connect } from 'react-redux'; 4 | import { clearError } from '../../actions'; 5 | 6 | const { width, height } = Dimensions.get('window'); 7 | 8 | class BottomAlert extends PureComponent { 9 | componentWillMount() { 10 | this.timer = setTimeout(() => { 11 | this.props.clearError() 12 | }, 1500); 13 | } 14 | 15 | componentWillUnmount() { 16 | clearTimeout(this.timer); 17 | } 18 | 19 | render() { 20 | const { msgBackgroundColor, themeColor, msgBorderColor } = this.props; 21 | return( 22 | 23 | { this.props.children } 24 | 25 | ); 26 | } 27 | }; 28 | 29 | const styles = StyleSheet.create({ 30 | containerStyle: { 31 | position: 'absolute', 32 | bottom: 0, 33 | width: width, 34 | height: height*0.1, 35 | justifyContent: 'center', 36 | borderTopWidth: 1, 37 | borderColor: '#000000' 38 | }, 39 | textStyle:{ 40 | alignSelf: 'center', 41 | fontSize: 16 42 | } 43 | }); 44 | 45 | const mapStateToProps = state => { 46 | return { 47 | themeColor: state.themes.themeColor, 48 | msgBackgroundColor: state.themes.msgBackgroundColor, 49 | msgBorderColor: state.themes.msgBorderColor 50 | }; 51 | }; 52 | 53 | BottomAlert = connect(mapStateToProps, { clearError })(BottomAlert); 54 | 55 | export { BottomAlert } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/components/common/Confirm.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Text, View, TouchableOpacity, Dimensions, StyleSheet } from 'react-native'; 3 | import Modal from "react-native-modal"; 4 | 5 | const width = (Dimensions.get('window').width-300)/2; 6 | 7 | class Confirm extends PureComponent { 8 | render() { 9 | return ( 10 | 19 | 20 | 21 | { this.props.children } 22 | 23 | 24 | 25 | 26 | Cancel 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | yesButtonStyle: { 36 | position: 'absolute', 37 | bottom: 88, 38 | left: width, 39 | right: 0, 40 | height: 45, 41 | width: 300, 42 | backgroundColor:'#FCFCFC', 43 | borderRadius: 25, 44 | borderWidth: 1, 45 | borderColor: 'transparent', 46 | justifyContent: 'center', 47 | }, 48 | yesTextStyle:{ 49 | alignSelf: 'center', 50 | color: 'red', 51 | fontSize: 18, 52 | }, 53 | noButtonStyle:{ 54 | position: 'absolute', 55 | bottom: 35, 56 | left: width, 57 | right: 0, 58 | height: 45, 59 | width: 300, 60 | borderRadius: 25, 61 | borderWidth: 1, 62 | borderColor: 'transparent', 63 | backgroundColor:'#FCFCFC', 64 | justifyContent: 'center', 65 | }, 66 | noTextStyle:{ 67 | alignSelf: 'center', 68 | color: '#000', 69 | fontSize: 18, 70 | } 71 | }); 72 | 73 | export { Confirm }; -------------------------------------------------------------------------------- /src/components/common/Container.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, StatusBar, Dimensions, StyleSheet, SafeAreaView } from 'react-native'; 3 | import { Actions } from 'react-native-router-flux'; 4 | import { connect } from 'react-redux'; 5 | import { BottomAlert } from './BottomAlert'; 6 | 7 | const { height } = Dimensions.get('window'); 8 | 9 | class Container extends Component { 10 | renderMsg() { 11 | if ( this.props.errorMsg ) { 12 | return ( 13 | { this.props.errorMsg } 14 | ); 15 | } 16 | } 17 | render() { 18 | const { children, style, containerBackgroundColor, barStyle } = this.props; 19 | return( 20 | 21 | 24 | 34 | 35 | ) 36 | } 37 | } 38 | 39 | const styles = StyleSheet.create({ 40 | containerStyle: { 41 | position: 'relative', 42 | flex: 1 43 | } 44 | }) 45 | 46 | const mapStateToProps = state => { 47 | return { 48 | containerBackgroundColor: state.themes.containerBackgroundColor, 49 | barStyle: state.themes.barStyle, 50 | errorMsg: state.error.errorMsg 51 | }; 52 | }; 53 | 54 | Container = connect(mapStateToProps)(Container); 55 | 56 | export { Container }; -------------------------------------------------------------------------------- /src/components/common/HTMLContent.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Dimensions, Linking, Text, StyleSheet } from 'react-native'; 3 | import HTML from 'react-native-render-html'; 4 | import { connect } from 'react-redux'; 5 | 6 | const { width } = Dimensions.get('window'); 7 | const articleWidth = width*0.93; 8 | 9 | class HTMLContent extends PureComponent { 10 | render() { 11 | const { html, itemCardBackgroundColor, titleColor, themeColor } = this.props; 12 | return( 13 | Linking.openURL(href) } 106 | listsPrefixesRenderers = {{ 107 | ul: (htmlAttribs, children, convertedCSSStyles, passProps) => { 108 | return ( 109 | · 110 | ); 111 | } 112 | }} 113 | /> 114 | ) 115 | } 116 | } 117 | 118 | const mapStateToProps = state => { 119 | return { 120 | themeColor: state.themes.themeColor, 121 | titleColor: state.themes.titleColor, 122 | itemCardBackgroundColor: state.themes.itemCardBackgroundColor 123 | }; 124 | }; 125 | 126 | HTMLContent = connect(mapStateToProps)(HTMLContent); 127 | 128 | export { HTMLContent }; -------------------------------------------------------------------------------- /src/components/common/Input.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { TextInput, View, Text, StyleSheet } from 'react-native'; 3 | import { connect } from 'react-redux'; 4 | 5 | class Input extends PureComponent { 6 | render() { 7 | const { label, value, onChangeText, placeholder, secureTextEntry, editable, maxLength, titleColor, themeColor } = this.props; 8 | const { inputStyle, labelStyle, containerStyle } = styles; 9 | 10 | if (label) { 11 | return ( 12 | 13 | 14 | { label } 15 | 16 | 31 | 32 | ); 33 | } 34 | else { 35 | return( 36 | 37 | 52 | 53 | ) 54 | } 55 | } 56 | }; 57 | 58 | const styles = StyleSheet.create({ 59 | inputStyle:{ 60 | paddingRight: 5, 61 | paddingLeft: 5, 62 | fontSize: 14, 63 | flex: 8, 64 | alignSelf:'center' 65 | }, 66 | labelStyle: { 67 | fontSize: 18, 68 | paddingLeft: 10, 69 | flex: 1, 70 | alignSelf: 'center' 71 | }, 72 | containerStyle: { 73 | flex: 1, 74 | flexDirection: 'row', 75 | alignItems: 'flex-start', 76 | minHeight: 42 77 | } 78 | }); 79 | 80 | const mapStateToProps = state => { 81 | return { 82 | titleColor: state.themes.titleColor, 83 | themeColor: state.themes.themeColor, 84 | }; 85 | }; 86 | 87 | Input = connect(mapStateToProps)(Input); 88 | 89 | export { Input }; 90 | -------------------------------------------------------------------------------- /src/components/common/ItemCard.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { View, Dimensions, Text, StyleSheet } from 'react-native'; 3 | import ElevatedView from 'react-native-elevated-view'; 4 | import { connect } from 'react-redux'; 5 | 6 | const cardWidth = Dimensions.get('window').width*0.9; 7 | 8 | class ItemCard extends PureComponent { 9 | render() { 10 | const { children, elevation = 3, style, itemCardBackgroundColor } = this.props; 11 | return ( 12 | 16 | { children } 17 | 18 | ) 19 | } 20 | } 21 | const styles = StyleSheet.create({ 22 | cardStyle:{ 23 | width: cardWidth, 24 | padding: 9, 25 | paddingLeft: 14, 26 | paddingRight: 14, 27 | justifyContent: 'center', 28 | flexDirection: 'row', 29 | alignSelf: 'center', 30 | margin: 4, 31 | marginTop: 2, 32 | borderRadius: 2 33 | } 34 | }) 35 | 36 | 37 | const mapStateToProps = state => { 38 | return { 39 | itemCardBackgroundColor: state.themes.itemCardBackgroundColor, 40 | }; 41 | }; 42 | 43 | ItemCard = connect(mapStateToProps)(ItemCard); 44 | 45 | export { ItemCard }; -------------------------------------------------------------------------------- /src/components/common/Timer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Text, View, StyleSheet } from 'react-native'; 3 | 4 | timeCal = (str) => { 5 | let tempStrs = str.split(" "); 6 | let dateStrs = tempStrs[0].split("-"); 7 | let year = parseInt(dateStrs[0], 10); 8 | let month = parseInt(dateStrs[1], 10) - 1; 9 | let day = parseInt(dateStrs[2], 10); 10 | let timeStrs = tempStrs[1].split(":"); 11 | // rss2json api is in 0 time zone 12 | let hour = parseInt(timeStrs [0], 10) + 8; 13 | let minute = parseInt(timeStrs[1], 10); 14 | let second = parseInt(timeStrs[2], 10); 15 | let date = new Date(year, month, day, hour, minute, second); 16 | let NowDate = new Date(); 17 | 18 | let diff = NowDate - date; 19 | 20 | let diffDays=Math.floor(diff/(24*3600*1000)); 21 | 22 | if (diffDays >= 1) { 23 | return diffDays + 'd'; 24 | } 25 | 26 | let diffHours=Math.floor(diff/(3600*1000)); 27 | 28 | if (diffHours >= 1) { 29 | return diffHours + 'h'; 30 | } 31 | 32 | let diffMinutes=Math.floor(diff/(60*1000)); 33 | 34 | if (diffMinutes >= 0) { 35 | return diffMinutes + 'm'; 36 | } 37 | 38 | 39 | }; 40 | 41 | class Timer extends Component { 42 | render() { 43 | return ( 44 | 45 | 46 | { timeCal(this.props.children) } 47 | 48 | 49 | ); 50 | } 51 | } 52 | 53 | const styles = StyleSheet.create({ 54 | timerStyle:{ 55 | textAlign: 'right', 56 | color: '#777', 57 | fontSize: 12 58 | } 59 | }); 60 | 61 | export { Timer }; -------------------------------------------------------------------------------- /src/components/common/index.js: -------------------------------------------------------------------------------- 1 | export * from './Input'; 2 | export * from './Container'; 3 | export * from './BottomAlert'; 4 | export * from './ItemCard'; 5 | export * from './Confirm'; 6 | export * from './Timer'; 7 | export * from './HTMLContent'; -------------------------------------------------------------------------------- /src/reducers/AuthReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | CURRENT_USER, 3 | USERNAME_CHANGED, 4 | PASSWORD_CHANGED, 5 | EMAIL_CHANGED, 6 | LOGIN_USER_SUCCESS, 7 | LOGIN_USER_FAIL, 8 | LOGOUT_SUCCESS, 9 | RESET, 10 | LOADING, 11 | } from '../actions/types'; 12 | 13 | const INITIAL_STATE = { 14 | username: '', 15 | password: '', 16 | email: '', 17 | currentUser: '', 18 | loading: false, 19 | currentForm: 'logInForm', 20 | }; 21 | 22 | export default (state = INITIAL_STATE, action) => { 23 | 24 | switch (action.type) { 25 | case USERNAME_CHANGED: 26 | return { ...state, username: action.payload }; 27 | 28 | case PASSWORD_CHANGED: 29 | return { ...state, password: action.payload }; 30 | 31 | case EMAIL_CHANGED: 32 | return { ...state, email: action.payload }; 33 | 34 | case LOGIN_USER_SUCCESS: 35 | return { ...state, ...INITIAL_STATE, currentUser: action.payload }; 36 | 37 | case LOGIN_USER_FAIL: 38 | return { ...state, loading: false, username: '', password: '', email: ''} 39 | 40 | case LOGOUT_SUCCESS: 41 | return INITIAL_STATE; 42 | 43 | case CURRENT_USER: 44 | return { ...state, ...INITIAL_STATE, currentUser: action.payload }; 45 | 46 | case RESET: 47 | return { ...state, username: '', password: '', email: '', loading: false, currentForm: action.payload }; 48 | 49 | case LOADING: 50 | return { ...state, loading: true }; 51 | 52 | default: 53 | return state; 54 | } 55 | }; -------------------------------------------------------------------------------- /src/reducers/ColorReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | SWITCH_THEMES 3 | } from '../actions/types'; 4 | 5 | const LIGHT = { 6 | name: 'light', 7 | containerBackgroundColor: '#E9E9EF', 8 | themeColor: '#000', 9 | itemCardBackgroundColor: '#FCFCFC', 10 | titleColor: '#333', 11 | bigTitleColor: '#555', 12 | borderColor: '#eee', 13 | headerTitleColor: '#aaa', 14 | barStyle: "dark-content", 15 | msgBackgroundColor: '#D2D3D8', 16 | msgBorderColor: '#D9DADE', 17 | icon: require("../assets/imgs/icon_light.png") 18 | } 19 | 20 | const DARK = { 21 | name: 'dark', 22 | containerBackgroundColor: '#242424', 23 | themeColor: '#ddd', 24 | itemCardBackgroundColor: '#313131', 25 | titleColor: '#aaa', 26 | bigTitleColor: '#aaa', 27 | borderColor: '#2D2C2E', 28 | headerTitleColor: '#343336', 29 | barStyle: "light-content", 30 | msgBackgroundColor: '#131314', 31 | msgBorderColor: '#000', 32 | icon: require("../assets/imgs/icon_dark.png") 33 | } 34 | 35 | export default (state = LIGHT, action) => { 36 | switch (action.type) { 37 | 38 | case SWITCH_THEMES: 39 | if (state.name == 'light') { 40 | state = DARK; 41 | global.storage.save({ 42 | key: 'theme', 43 | data: 'dark', 44 | }); 45 | } 46 | else { 47 | state = LIGHT; 48 | global.storage.save({ 49 | key: 'theme', 50 | data: 'light', 51 | }); 52 | }; 53 | 54 | default: 55 | return state; 56 | } 57 | }; -------------------------------------------------------------------------------- /src/reducers/ErrorReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | ERROR_MESSAGE, 3 | CLEAR_ERROR 4 | } from '../actions/types'; 5 | 6 | const INITIAL_STATE = { 7 | errorMsg: '', 8 | }; 9 | 10 | export default (state = INITIAL_STATE, action) => { 11 | switch (action.type) { 12 | case ERROR_MESSAGE: 13 | return { errorMsg: action.payload }; 14 | 15 | case CLEAR_ERROR: 16 | return { errorMsg: ''}; 17 | 18 | default: 19 | return state; 20 | } 21 | }; -------------------------------------------------------------------------------- /src/reducers/FeedsReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | FEED_ADD, 3 | FEED_DELETE, 4 | FEED_EDIT, 5 | FEED_ADD_FAIL, 6 | FEED_ADD_SUCCESS, 7 | ADD_FEED_LOADING, 8 | FEEDS_FETCH_SUCCESS, 9 | FEEDS_CLEAR, 10 | FEED_DETAIL_FETCH_SUCCESS, 11 | FEED_DETAIL_FETCH_FAIL, 12 | HEADER_IS_REFRESHING, 13 | FOOTER_IS_REFRESHING, 14 | STOP_FOOTER_REFRESHING, 15 | MORE_FEED_DETAIL_FETCH_SUCCESS, 16 | NO_MORE, 17 | NO_MORE_RESET, 18 | LOGOUT_SUCCESS, 19 | CURRENT_FEED 20 | } from '../actions/types'; 21 | 22 | const INITAIL_STATE = { 23 | rss_title: '', 24 | rss_url: '', 25 | loading: false, 26 | feeds: null, 27 | feedDetail: [], 28 | articleDetail: '', 29 | headerRefreashing: false, 30 | footerRefreshing: false, 31 | noMore: false, 32 | currentFeed: '', 33 | }; 34 | 35 | export default (state = INITAIL_STATE, action) => { 36 | switch (action.type) { 37 | case CURRENT_FEED: 38 | return { ...state, currentFeed: action.payload }; 39 | 40 | case FEED_ADD: 41 | return { ...state, rss_title: action.payload.rss_title ,rss_url: action.payload.rss_url }; 42 | 43 | case FEED_DELETE: 44 | return { ...state, feeds: action.payload }; 45 | 46 | case FEED_EDIT: 47 | return { ...state, rss_url: action.payload.rss_url }; 48 | 49 | case FEED_ADD_FAIL: 50 | return { ...state, rss_url:'', rss_title: '', loading: false }; 51 | 52 | case FEED_ADD_SUCCESS: 53 | return { ...state, rss_url:'', rss_title: '', loading: false, feeds: action.payload }; 54 | 55 | case FEEDS_FETCH_SUCCESS: 56 | return { ...state, feeds: action.payload }; 57 | 58 | case FEEDS_CLEAR: 59 | return INITAIL_STATE 60 | 61 | case ADD_FEED_LOADING: 62 | return { ...state, loading: true } 63 | 64 | case FEED_DETAIL_FETCH_SUCCESS: 65 | if ( state.currentFeed != action.payload[0].attributes.sourceTitle) { 66 | // in case unnecessary re-render 67 | return { ...state } 68 | } 69 | return { ...state, feedDetail: action.payload, headerRefreashing: false }; 70 | 71 | case FEED_DETAIL_FETCH_FAIL: 72 | return { ...state, headerRefreashing: false }; 73 | 74 | case HEADER_IS_REFRESHING: 75 | return { ...state, headerRefreashing: true, noMore: false }; 76 | 77 | case FOOTER_IS_REFRESHING: 78 | return { ...state, footerRefreshing: true }; 79 | 80 | case MORE_FEED_DETAIL_FETCH_SUCCESS: 81 | const moreFeeds = state.feedDetail.concat(action.payload); 82 | return { ...state, footerRefreshing: false, feedDetail: moreFeeds }; 83 | 84 | case STOP_FOOTER_REFRESHING: 85 | return { ...state, footerRefreshing: false }; 86 | 87 | case NO_MORE: 88 | return { ...state, noMore: true, footerRefreshing: false }; 89 | 90 | case NO_MORE_RESET: 91 | return { ...state, noMore: false }; 92 | 93 | default: 94 | return state; 95 | } 96 | }; -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import AuthReducer from './AuthReducer'; 3 | import FeedsReducer from './FeedsReducer'; 4 | import ErrorReducer from './ErrorReducer'; 5 | import ColorReducer from './ColorReducer'; 6 | 7 | export default combineReducers({ 8 | auth: AuthReducer, 9 | feed: FeedsReducer, 10 | error: ErrorReducer, 11 | themes: ColorReducer 12 | }); --------------------------------------------------------------------------------