├── .flowconfig ├── .gitignore ├── .watchmanconfig ├── LICENSE ├── README.md ├── android ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ ├── react.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ ├── ion.json │ │ └── ionicons.ttf │ │ ├── java │ │ └── com │ │ │ └── allyoop │ │ │ └── MainActivity.java │ │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── app ├── actions │ ├── application.js │ ├── game.js │ ├── player.js │ └── team.js ├── channel │ ├── address.js │ ├── index.js │ └── producer.js ├── components │ ├── game │ │ ├── GameDetail.js │ │ ├── GameList.js │ │ ├── GamePanel.js │ │ └── GamePlayers.js │ ├── player │ │ ├── PlayerDetail.js │ │ ├── PlayerIndex.js │ │ ├── PlayerLog.js │ │ ├── PlayerTrend.js │ │ └── PlayerTrendBarItem.js │ ├── share │ │ ├── NavigatorBar.js │ │ └── Tabbar.js │ └── team │ │ ├── TeamConference.js │ │ ├── TeamDetail.js │ │ ├── TeamDetailPlayer.js │ │ └── TeamList.js ├── constant.js ├── containers │ ├── App.js │ ├── Game.js │ ├── Player.js │ └── Team.js ├── lib │ ├── collection │ │ └── index.js │ └── userDefaults │ │ └── index.js ├── middleware │ └── logger.js ├── reducers │ ├── application.js │ ├── index.js │ ├── live.js │ ├── over.js │ ├── playerList.js │ ├── playerLoaded.js │ ├── standing.js │ ├── team.js │ └── unstart.js ├── root.js └── utils │ ├── create-reducer.js │ └── team-map.js ├── assets └── img │ ├── atl.png │ ├── bkn.png │ ├── bos.png │ ├── cha.png │ ├── chi.png │ ├── cle.png │ ├── dal.png │ ├── den.png │ ├── det.png │ ├── gsw.png │ ├── hou.png │ ├── hou2.png │ ├── ind.png │ ├── lac.png │ ├── lal.png │ ├── mem.png │ ├── mia.png │ ├── mil.png │ ├── min.png │ ├── nop.png │ ├── nyk.png │ ├── okc.png │ ├── orl.png │ ├── phi.png │ ├── phx.png │ ├── por.png │ ├── sac.png │ ├── sas.png │ ├── tor.png │ ├── uta.png │ └── was.png ├── circle.yml ├── index.android.js ├── index.ios.js ├── ios ├── FAKIcon.h ├── FAKIcon.m ├── FAKIonIcons.h ├── FAKIonIcons.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── atl.imageset │ │ ├── Contents.json │ │ └── atl.png │ ├── bkn.imageset │ │ ├── Contents.json │ │ └── bkn.png │ ├── bos.imageset │ │ ├── Contents.json │ │ └── bos.png │ ├── cha.imageset │ │ ├── Contents.json │ │ └── cha.png │ ├── chi.imageset │ │ ├── Contents.json │ │ └── chi.png │ ├── cle.imageset │ │ ├── Contents.json │ │ └── cle.png │ ├── dal.imageset │ │ ├── Contents.json │ │ └── dal.png │ ├── den.imageset │ │ ├── Contents.json │ │ └── den.png │ ├── det.imageset │ │ ├── Contents.json │ │ └── det.png │ ├── gsw.imageset │ │ ├── Contents.json │ │ └── gsw.png │ ├── hou.imageset │ │ ├── Contents.json │ │ └── hou2.png │ ├── hou2.imageset │ │ ├── Contents.json │ │ └── hou.png │ ├── ind.imageset │ │ ├── Contents.json │ │ └── ind.png │ ├── lac.imageset │ │ ├── Contents.json │ │ └── lac.png │ ├── lal.imageset │ │ ├── Contents.json │ │ └── lal.png │ ├── mem.imageset │ │ ├── Contents.json │ │ └── mem.png │ ├── mia.imageset │ │ ├── Contents.json │ │ └── mia.png │ ├── mil.imageset │ │ ├── Contents.json │ │ └── mil.png │ ├── min.imageset │ │ ├── Contents.json │ │ └── min.png │ ├── nop.imageset │ │ ├── Contents.json │ │ └── nop.png │ ├── nyk.imageset │ │ ├── Contents.json │ │ └── nyk.png │ ├── okc.imageset │ │ ├── Contents.json │ │ └── okc.png │ ├── orl.imageset │ │ ├── Contents.json │ │ └── orl.png │ ├── phi.imageset │ │ ├── Contents.json │ │ └── phi.png │ ├── phx.imageset │ │ ├── Contents.json │ │ └── phx.png │ ├── por.imageset │ │ ├── Contents.json │ │ └── por.png │ ├── sac.imageset │ │ ├── Contents.json │ │ └── sac.png │ ├── sas.imageset │ │ ├── Contents.json │ │ └── sas.png │ ├── tor.imageset │ │ ├── Contents.json │ │ └── tor.png │ ├── uta.imageset │ │ ├── Contents.json │ │ └── uta.png │ └── was.imageset │ │ ├── Contents.json │ │ └── was.png ├── allyoop.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── allyoop.xcscheme ├── allyoop │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── RNUserDefaults-bridge.h │ ├── RNUserDefaults.m │ ├── RNUserDefaults.swift │ └── main.m ├── allyoopTests │ ├── Info.plist │ └── allyoopTests.m └── ionicons.ttf └── package.json /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*.web.js 5 | .*/*.android.js 6 | 7 | # Some modules have their own node_modules with overlap 8 | .*/node_modules/node-haste/.* 9 | 10 | # Ugh 11 | .*/node_modules/babel.* 12 | .*/node_modules/babylon.* 13 | .*/node_modules/invariant.* 14 | 15 | # Ignore react and fbjs where there are overlaps, but don't ignore 16 | # anything that react-native relies on 17 | .*/node_modules/fbjs-haste/.*/__tests__/.* 18 | .*/node_modules/fbjs-haste/__forks__/Map.js 19 | .*/node_modules/fbjs-haste/__forks__/Promise.js 20 | .*/node_modules/fbjs-haste/__forks__/fetch.js 21 | .*/node_modules/fbjs-haste/core/ExecutionEnvironment.js 22 | .*/node_modules/fbjs-haste/core/isEmpty.js 23 | .*/node_modules/fbjs-haste/crypto/crc32.js 24 | .*/node_modules/fbjs-haste/stubs/ErrorUtils.js 25 | .*/node_modules/react-haste/React.js 26 | .*/node_modules/react-haste/renderers/dom/ReactDOM.js 27 | .*/node_modules/react-haste/renderers/shared/event/eventPlugins/ResponderEventPlugin.js 28 | 29 | # Ignore commoner tests 30 | .*/node_modules/commoner/test/.* 31 | 32 | # See https://github.com/facebook/flow/issues/442 33 | .*/react-tools/node_modules/commoner/lib/reader.js 34 | 35 | # Ignore jest 36 | .*/node_modules/jest-cli/.* 37 | 38 | # Ignore Website 39 | .*/website/.* 40 | 41 | [include] 42 | 43 | [libs] 44 | node_modules/react-native/Libraries/react-native/react-native-interface.js 45 | 46 | [options] 47 | module.system=haste 48 | 49 | munge_underscores=true 50 | 51 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 52 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.png$' -> 'RelativeImageStub' 53 | 54 | suppress_type=$FlowIssue 55 | suppress_type=$FlowFixMe 56 | suppress_type=$FixMe 57 | 58 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 59 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ 60 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 61 | 62 | [version] 63 | 0.19.0 64 | -------------------------------------------------------------------------------- /.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/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Wang Zixiao 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This is why we play 2 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 3 | [![Circle CI](https://circleci.com/gh/wwayne/react-native-nba-app/tree/master.svg?style=svg)](https://circleci.com/gh/wwayne/react-native-nba-app/tree/master) 4 | 5 | ![group](https://cloud.githubusercontent.com/assets/5305874/12059257/dacf1ad0-af92-11e5-920c-ba4818d8dc1d.png) 6 | 7 | ## Info 8 | * **Platform:** iOS & Android 9 | * **State Management:** Redux 10 | * **Code Style:** Standard 11 | * **Unit Test:** None, take a look at [snowflake](https://github.com/bartonhammond/snowflake) for learning 12 | * **Related Articles:** [Let’s drawing charts in React-Native without any library](https://medium.com/@wwayne_me/let-s-drawing-charts-in-react-native-without-any-library-4c20ba38d8ab#.kyyxnrb9s) 13 | 14 | ## Demonstration 15 | ### Games 16 | ![game](https://cloud.githubusercontent.com/assets/5305874/12422631/e33d57ca-bf02-11e5-8bdf-e10df77fc1fb.gif) 17 | ![game_android](https://cloud.githubusercontent.com/assets/5305874/12530667/eb14e1c2-c220-11e5-864d-971e62646afa.gif) 18 | 19 | ### Players 20 | ![player](https://cloud.githubusercontent.com/assets/5305874/12422675/19ae6696-bf03-11e5-87d5-6abc805b62b8.gif) 21 | ![player_android](https://cloud.githubusercontent.com/assets/5305874/12530668/f42c16b8-c220-11e5-82a5-a886cd0cfd65.gif) 22 | 23 | ### Teams 24 | ![team](https://cloud.githubusercontent.com/assets/5305874/12422777/7dc6d870-bf03-11e5-82eb-634b14d34f16.gif) 25 | ![team_android](https://cloud.githubusercontent.com/assets/5305874/12530753/dc968fd0-c223-11e5-98ed-a55771ce5333.gif) 26 | 27 | 28 | ## Develop 29 | #### iOS 30 | 1. `$ npm install` 31 | 2. `$ npm run clean` react-native unsupport for .babelrc since 0.16, so you have to remove all .babelrc in your project(includes packages) 32 | 3. `$ npm start` 33 | 4. Open your Xcode, select a simulator, click the play button or `cmd + R` 34 | 35 | #### Android 36 | 1. Same to [official doc](http://facebook.github.io/react-native/docs/android-setup.html#content) 37 | 2. `$ react-native run-android` 38 | 3. It's a bit complicated to those who are not familiar with Android development, like me 39 | 40 | ## Design 41 | The design is made by Sketch, you can find the original design file in [Dropbox](https://www.dropbox.com/s/3hn1o5xgk7bzjpa/Allyoop.sketch?dl=0), it may help if someone wants to design new features. 42 | 43 | ## Run in device 44 | #### iOS: 45 | 1. Check the [official doc](http://facebook.github.io/react-native/docs/running-on-device-ios.html#using-offline-bundle) first, the following steps are based on official doc 46 | 47 | 2. Comment `console` in `lib/middleware/logger.js`, because native environment doesn't support for `console.group` 48 | 49 | 3. Generate js bundle: **$ react-native bundle --entry-file index.ios.js --platform ios --bundle-output ./main.jsbundle** 50 | 51 | 4. Add js bundle into your project: open Xcode, select **Files** -> **Add Files to...** -> select the main.jsbundle file that just generated 52 | 53 | 5. In Xcode, select your device, then click the play button or `cmd + R` 54 | 55 | #### Android: 56 | I never tried 57 | 58 | ## To Be Continue 59 | 1. Fix UI in Android 60 | 2. Find a better way to organize css 61 | 3. Unit test 62 | 63 | 64 | ## License 65 | 66 | MIT 67 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | /** 4 | * The react.gradle file registers two tasks: bundleDebugJsAndAssets and bundleReleaseJsAndAssets. 5 | * These basically call `react-native bundle` with the correct arguments during the Android build 6 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 7 | * bundle directly from the development server. Below you can see all the possible configurations 8 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 9 | * `apply from: "react.gradle"` line. 10 | * 11 | * project.ext.react = [ 12 | * // the name of the generated asset file containing your JS bundle 13 | * bundleAssetName: "index.android.bundle", 14 | * 15 | * // the entry file for bundle generation 16 | * entryFile: "index.android.js", 17 | * 18 | * // whether to bundle JS and assets in debug mode 19 | * bundleInDebug: false, 20 | * 21 | * // whether to bundle JS and assets in release mode 22 | * bundleInRelease: true, 23 | * 24 | * // the root of your project, i.e. where "package.json" lives 25 | * root: "../../", 26 | * 27 | * // where to put the JS bundle asset in debug mode 28 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 29 | * 30 | * // where to put the JS bundle asset in release mode 31 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 32 | * 33 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 34 | * // require('./image.png')), in debug mode 35 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 36 | * 37 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 38 | * // require('./image.png')), in release mode 39 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 40 | * 41 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 42 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 43 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 44 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 45 | * // for example, you might want to remove it from here. 46 | * inputExcludes: ["android/**", "ios/**"] 47 | * ] 48 | */ 49 | 50 | apply from: "react.gradle" 51 | 52 | android { 53 | compileSdkVersion 23 54 | buildToolsVersion "23.0.1" 55 | 56 | defaultConfig { 57 | applicationId "com.allyoop" 58 | minSdkVersion 16 59 | targetSdkVersion 22 60 | versionCode 1 61 | versionName "1.0" 62 | ndk { 63 | abiFilters "armeabi-v7a", "x86" 64 | } 65 | } 66 | buildTypes { 67 | release { 68 | minifyEnabled false // Set this to true to enable Proguard 69 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 70 | } 71 | } 72 | } 73 | 74 | dependencies { 75 | compile fileTree(dir: "libs", include: ["*.jar"]) 76 | compile "com.android.support:appcompat-v7:23.0.1" 77 | compile "com.facebook.react:react-native:0.17.+" 78 | compile project(':react-native-icons') 79 | } 80 | -------------------------------------------------------------------------------- /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 | 30 | # Do not strip any method/class that is annotated with @DoNotStrip 31 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 32 | -keepclassmembers class * { 33 | @com.facebook.proguard.annotations.DoNotStrip *; 34 | } 35 | 36 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 37 | void set*(***); 38 | *** get*(); 39 | } 40 | 41 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 42 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 43 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 44 | -keepclassmembers class * { @com.facebook.react.uimanager.ReactProp ; } 45 | -keepclassmembers class * { @com.facebook.react.uimanager.ReactPropGroup ; } 46 | 47 | # okhttp 48 | 49 | -keepattributes Signature 50 | -keepattributes *Annotation* 51 | -keep class com.squareup.okhttp.** { *; } 52 | -keep interface com.squareup.okhttp.** { *; } 53 | -dontwarn com.squareup.okhttp.** 54 | 55 | # okio 56 | 57 | -keep class sun.misc.Unsafe { *; } 58 | -dontwarn java.nio.file.* 59 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 60 | -dontwarn okio.** 61 | -------------------------------------------------------------------------------- /android/app/react.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.tools.ant.taskdefs.condition.Os 2 | 3 | def config = project.hasProperty("react") ? project.react : []; 4 | 5 | def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" 6 | def entryFile = config.entryFile ?: "index.android.js" 7 | 8 | // because elvis operator 9 | def elvisFile(thing) { 10 | return thing ? file(thing) : null; 11 | } 12 | 13 | def reactRoot = elvisFile(config.root) ?: file("../../") 14 | def jsBundleDirDebug = elvisFile(config.jsBundleDirDebug) ?: 15 | file("$buildDir/intermediates/assets/debug") 16 | def jsBundleDirRelease = elvisFile(config.jsBundleDirRelease) ?: 17 | file("$buildDir/intermediates/assets/release") 18 | def resourcesDirDebug = elvisFile(config.resourcesDirDebug) ?: 19 | file("$buildDir/intermediates/res/merged/debug") 20 | def resourcesDirRelease = elvisFile(config.resourcesDirRelease) ?: 21 | file("$buildDir/intermediates/res/merged/release") 22 | def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] 23 | 24 | def jsBundleFileDebug = file("$jsBundleDirDebug/$bundleAssetName") 25 | def jsBundleFileRelease = file("$jsBundleDirRelease/$bundleAssetName") 26 | 27 | task bundleDebugJsAndAssets(type: Exec) { 28 | // create dirs if they are not there (e.g. the "clean" task just ran) 29 | doFirst { 30 | jsBundleDirDebug.mkdirs() 31 | resourcesDirDebug.mkdirs() 32 | } 33 | 34 | // set up inputs and outputs so gradle can cache the result 35 | inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) 36 | outputs.dir jsBundleDirDebug 37 | outputs.dir resourcesDirDebug 38 | 39 | // set up the call to the react-native cli 40 | workingDir reactRoot 41 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 42 | commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "true", "--entry-file", 43 | entryFile, "--bundle-output", jsBundleFileDebug, "--assets-dest", resourcesDirDebug 44 | } else { 45 | commandLine "react-native", "bundle", "--platform", "android", "--dev", "true", "--entry-file", 46 | entryFile, "--bundle-output", jsBundleFileDebug, "--assets-dest", resourcesDirDebug 47 | } 48 | 49 | enabled config.bundleInDebug ?: false 50 | } 51 | 52 | task bundleReleaseJsAndAssets(type: Exec) { 53 | // create dirs if they are not there (e.g. the "clean" task just ran) 54 | doFirst { 55 | jsBundleDirRelease.mkdirs() 56 | resourcesDirRelease.mkdirs() 57 | } 58 | 59 | // set up inputs and outputs so gradle can cache the result 60 | inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) 61 | outputs.dir jsBundleDirRelease 62 | outputs.dir resourcesDirRelease 63 | 64 | // set up the call to the react-native cli 65 | workingDir reactRoot 66 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 67 | commandLine "cmd","/c", "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file", 68 | entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease 69 | } else { 70 | commandLine "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file", 71 | entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease 72 | } 73 | 74 | enabled config.bundleInRelease ?: true 75 | } 76 | 77 | gradle.projectsEvaluated { 78 | // hook bundleDebugJsAndAssets into the android build process 79 | bundleDebugJsAndAssets.dependsOn mergeDebugResources 80 | bundleDebugJsAndAssets.dependsOn mergeDebugAssets 81 | processDebugResources.dependsOn bundleDebugJsAndAssets 82 | 83 | // hook bundleReleaseJsAndAssets into the android build process 84 | bundleReleaseJsAndAssets.dependsOn mergeReleaseResources 85 | bundleReleaseJsAndAssets.dependsOn mergeReleaseAssets 86 | processReleaseResources.dependsOn bundleReleaseJsAndAssets 87 | } 88 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/assets/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/android/app/src/main/assets/ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/java/com/allyoop/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.allyoop; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.KeyEvent; 6 | 7 | import com.facebook.react.LifecycleState; 8 | import com.facebook.react.ReactInstanceManager; 9 | import com.facebook.react.ReactRootView; 10 | import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; 11 | import com.facebook.react.shell.MainReactPackage; 12 | import com.facebook.soloader.SoLoader; 13 | 14 | import com.smixx.reactnativeicons.ReactNativeIcons; // <--- import 15 | import java.util.Arrays; // <--- import this if you want to specify which fonts to load 16 | import com.smixx.reactnativeicons.IconFont; // <--- import this if you want to specify which fonts to load 17 | 18 | public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler { 19 | 20 | private ReactInstanceManager mReactInstanceManager; 21 | private ReactRootView mReactRootView; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | mReactRootView = new ReactRootView(this); 27 | 28 | mReactInstanceManager = ReactInstanceManager.builder() 29 | .setApplication(getApplication()) 30 | .setBundleAssetName("index.android.bundle") 31 | .setJSMainModuleName("index.android") 32 | .addPackage(new MainReactPackage()) 33 | /* react-native icons */ 34 | .addPackage(new ReactNativeIcons()) 35 | .setUseDeveloperSupport(BuildConfig.DEBUG) 36 | .setInitialLifecycleState(LifecycleState.RESUMED) 37 | .build(); 38 | 39 | mReactRootView.startReactApplication(mReactInstanceManager, "allyoop", null); 40 | 41 | setContentView(mReactRootView); 42 | } 43 | 44 | @Override 45 | public boolean onKeyUp(int keyCode, KeyEvent event) { 46 | if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) { 47 | mReactInstanceManager.showDevOptionsDialog(); 48 | return true; 49 | } 50 | return super.onKeyUp(keyCode, event); 51 | } 52 | 53 | @Override 54 | public void onBackPressed() { 55 | if (mReactInstanceManager != null) { 56 | mReactInstanceManager.onBackPressed(); 57 | } else { 58 | super.onBackPressed(); 59 | } 60 | } 61 | 62 | @Override 63 | public void invokeDefaultOnBackPressed() { 64 | super.onBackPressed(); 65 | } 66 | 67 | @Override 68 | protected void onPause() { 69 | super.onPause(); 70 | 71 | if (mReactInstanceManager != null) { 72 | mReactInstanceManager.onPause(); 73 | } 74 | } 75 | 76 | @Override 77 | protected void onResume() { 78 | super.onResume(); 79 | 80 | if (mReactInstanceManager != null) { 81 | mReactInstanceManager.onResume(this, this); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | allyoop 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/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.4-all.zip 6 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'allyoop' 2 | 3 | include ':app' 4 | include ':react-native-icons' 5 | project(':react-native-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-icons/android') 6 | -------------------------------------------------------------------------------- /app/actions/application.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { APP } from '../constant' 4 | 5 | /** 6 | * Switch tab 7 | */ 8 | const changeTab = (tab) => { 9 | return (dispatch) => { 10 | return Promise.resolve(dispatch({ 11 | type: APP.TAB, 12 | data: tab 13 | })) 14 | } 15 | } 16 | 17 | /** 18 | * Navigation, now only used for gamelist to gamedetail 19 | * when enter game detail, game list should stop requesting data continuously 20 | */ 21 | const toNavigation = targetComponent => { 22 | return dispatch => { 23 | return Promise.resolve(dispatch({ 24 | type: APP.NAVIGATION, 25 | data: targetComponent 26 | })) 27 | } 28 | } 29 | 30 | export default { 31 | changeTab, 32 | toNavigation 33 | } 34 | -------------------------------------------------------------------------------- /app/actions/game.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import Channel from '../channel' 4 | import { GAME } from '../constant' 5 | 6 | /** 7 | * Get info of game general 8 | */ 9 | const getGameGeneral = (year, month, date) => { 10 | return (dispatch, getStore) => { 11 | if (getStore().application.navigator === 'gameIndex') { 12 | const channel = new Channel() 13 | return channel.getGameGeneral(year, month, date) 14 | .then(data => { 15 | return dispatch({ 16 | type: GAME.INFO, 17 | data 18 | }) 19 | }) 20 | } else { 21 | return Promise.resolve() 22 | } 23 | } 24 | } 25 | 26 | /** 27 | * Grab detail of each game 28 | * @params id {String} && type {String} 29 | * @note id = game_id & tye = game_type 30 | * @return game {Object} 31 | */ 32 | const getGameDetail = (id, type, year, month, date) => { 33 | return (dispatch, getStore) => { 34 | if (getStore().application.navigator === 'gameDetail') { 35 | /* If the game is finish and have detail data, no need to request again */ 36 | if (type === 'over') { 37 | const game = getStore().over.data.find((g) => { return g.id === id }) 38 | if (game.detail && game.detail.loaded) { 39 | return Promise.resolve(dispatch({ 40 | type: GAME.DETAIL, 41 | data: game.detail.data 42 | })) 43 | } 44 | } 45 | const channel = new Channel() 46 | return channel.getGameDetail(year, month, date, id) 47 | .then(data => { 48 | return dispatch({ 49 | type: GAME.DETAIL, 50 | gameId: id, 51 | gameType: type, 52 | data 53 | }) 54 | }) 55 | } else { 56 | return Promise.resolve() 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * Get every team's standing 63 | */ 64 | const getLeagueStanding = () => { 65 | return (dispatch, getStore) => { 66 | if (getStore().standing.loaded) { 67 | return Promise.resolve(dispatch({ 68 | type: GAME.STANDING, 69 | data: getStore().standing.data 70 | })) 71 | } 72 | 73 | const d = new Date() 74 | const currentMonth = d.getMonth() + 1 75 | let year 76 | if (currentMonth >= 10) { 77 | year = d.getFullYear().toString() 78 | } else { 79 | year = d.getFullYear().toString() - 1 80 | } 81 | 82 | const channel = new Channel() 83 | return channel.getLeagueStanding(year) 84 | .then(data => { 85 | return dispatch({ 86 | type: GAME.STANDING, 87 | data 88 | }) 89 | }) 90 | } 91 | } 92 | 93 | export default { 94 | getGameGeneral, 95 | getGameDetail, 96 | getLeagueStanding 97 | } 98 | 99 | -------------------------------------------------------------------------------- /app/actions/player.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import {PLAYER} from '../constant' 4 | import Channel from '../channel' 5 | import userDefaults from '../lib/userDefaults' 6 | 7 | const getPlayerList = () => { 8 | return (dispatch, getStore) => { 9 | if (getStore().playerList.isLoaded) { 10 | return Promise.resolve(dispatch({ 11 | type: PLAYER.LIST, 12 | data: getStore().playerList.data 13 | })) 14 | } 15 | const channel = new Channel() 16 | return channel.getPlayerList() 17 | .then(data => { 18 | return dispatch({ 19 | type: PLAYER.LIST, 20 | data: data 21 | }) 22 | }) 23 | .catch(err => console.error(err)) 24 | } 25 | } 26 | 27 | const setSearchRecord = player => { 28 | return dispatch => { 29 | return userDefaults.get(PLAYER.RECENT) 30 | .then(recent => { 31 | let originData = [] 32 | if (recent) { 33 | originData = Object.assign([], recent) 34 | } 35 | 36 | /* If recent record has player, return */ 37 | if (originData.find((data, index) => { 38 | return data.id === player.id 39 | })) return Promise.resolve() 40 | 41 | if (originData.length === 10) originData.pop() 42 | originData.unshift(player) 43 | return userDefaults.set(PLAYER.RECENT, originData) 44 | .then(() => { 45 | return dispatch({ 46 | type: PLAYER.RECENT, 47 | data: originData 48 | }) 49 | }) 50 | }) 51 | } 52 | } 53 | 54 | const getSearchRecord = id => { 55 | return dispatch => { 56 | return userDefaults.get(PLAYER.RECENT) 57 | .then(recent => { 58 | let originData = [] 59 | if (recent) { 60 | originData = Object.assign([], recent) 61 | } 62 | return dispatch({ 63 | type: PLAYER.RECENT, 64 | data: originData 65 | }) 66 | }) 67 | .catch(err => { 68 | console.error('err', err) 69 | }) 70 | } 71 | } 72 | 73 | const getPlayerDetail = id => { 74 | return (dispatch, getStore) => { 75 | if (getStore().playerLoaded[id]) { 76 | return Promise.resolve(dispatch({ 77 | type: PLAYER.DETAIL, 78 | data: getStore().playerLoaded[id], 79 | id 80 | })) 81 | } 82 | const channel = new Channel() 83 | return channel.getPlayerInfo(id) 84 | .then(data => { 85 | return dispatch({ 86 | type: PLAYER.DETAIL, 87 | data, 88 | id 89 | }) 90 | }) 91 | .catch(err => console.error(err)) 92 | } 93 | } 94 | 95 | const getPlayerLog = id => { 96 | return (dispatch, getStore) => { 97 | if (getStore().playerLoaded[id] && getStore().playerLoaded[id].log) { 98 | return Promise.resolve(dispatch({ 99 | type: PLAYER.LOG, 100 | data: getStore().playerLoaded[id].log, 101 | id 102 | })) 103 | } 104 | const channel = new Channel() 105 | return channel.getPlayerLog(id) 106 | .then(data => { 107 | return dispatch({ 108 | type: PLAYER.LOG, 109 | data, 110 | id 111 | }) 112 | }) 113 | .catch(err => console.error(err)) 114 | } 115 | } 116 | 117 | export default { 118 | getPlayerList, 119 | setSearchRecord, 120 | getSearchRecord, 121 | getPlayerDetail, 122 | getPlayerLog 123 | } 124 | -------------------------------------------------------------------------------- /app/actions/team.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import {TEAM} from '../constant' 4 | import Channel from '../channel' 5 | 6 | export const getTeamRank = (year, month, date) => { 7 | return (dispatch, getStore) => { 8 | if (getStore().team.loaded) { 9 | return Promise.resolve({ 10 | type: TEAM.RANK, 11 | data: getStore().team.data 12 | }) 13 | } 14 | const channel = new Channel() 15 | return channel.getTeamRank(year, month, date) 16 | .then(data => { 17 | return dispatch({ 18 | type: TEAM.RANK, 19 | data: data 20 | }) 21 | }) 22 | .catch(err => console.error(err)) 23 | } 24 | } 25 | 26 | export const getTeamInfo = (id) => { 27 | return (dispatch, getStore) => { 28 | if (getStore().team.detail[id]) { 29 | return Promise.resolve({ 30 | type: TEAM.INFO, 31 | data: getStore().team.detail[id], 32 | id 33 | }) 34 | } 35 | const channel = new Channel() 36 | return channel.getTeamInfo(id) 37 | .then(data => { 38 | return dispatch({ 39 | type: TEAM.INFO, 40 | data: data, 41 | id 42 | }) 43 | }) 44 | .catch(err => console.error(err)) 45 | } 46 | } 47 | 48 | export const getTeamDetail = (id) => { 49 | return (dispatch, getStore) => { 50 | if (getStore().team.detail[id] && getStore().team.detail[id].players) { 51 | return Promise.resolve({ 52 | type: TEAM.DETAIL, 53 | data: getStore().team.detail[id].players, 54 | id 55 | }) 56 | } 57 | const channel = new Channel() 58 | return channel.getTeamDetail(id) 59 | .then(data => { 60 | return dispatch({ 61 | type: TEAM.DETAIL, 62 | data: data, 63 | id 64 | }) 65 | }) 66 | .catch(err => console.error(err)) 67 | } 68 | } 69 | 70 | export default { 71 | getTeamRank, 72 | getTeamInfo, 73 | getTeamDetail 74 | } 75 | -------------------------------------------------------------------------------- /app/channel/address.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const d = new Date() 4 | const currentMonth = d.getMonth() + 1 5 | let season 6 | if (currentMonth >= 10) { 7 | season = d.getFullYear().toString() + '-' + (d.getFullYear() + 1).toString().substring(2, 4) 8 | } else { 9 | season = (d.getFullYear().toString() - 1) + '-' + d.getFullYear().toString().substring(2, 4) 10 | } 11 | 12 | const address = { 13 | /** 14 | * All game of the date 15 | * @params gameDate: {String} {Format: yearmonthdate} 16 | * @example gameDate: 20151125 17 | */ 18 | gameGeneral: (gameDate) => { 19 | return `http://data.nba.com/data/5s/json/cms/noseason/scoreboard/${gameDate}/games.json` 20 | }, 21 | /** 22 | * Detail of a game in a specific date 23 | * @params gameDate: {String} {Format: yearmonthdate} & gameId: {String} 24 | * @example gameDate: 20151128 & gameId: 0021500239 25 | */ 26 | gameDetail: (gameDate, gameId) => { 27 | return `http://data.nba.com/data/10s/json/cms/noseason/game/${gameDate}/${gameId}/boxscore.json` 28 | }, 29 | /** 30 | * Current league standing 31 | * @params year {String} 32 | * @example year: 2015 33 | */ 34 | leagueStanding: (year) => { 35 | return `http://data.nba.com/data/json/cms/${year}/league/standings.json` 36 | }, 37 | 38 | playerList: () => { 39 | return `http://stats.nba.com/stats/commonallplayers?IsOnlyCurrentSeason=0&LeagueID=00&Season=${season}` 40 | }, 41 | 42 | playerInfo: (id) => { 43 | return `http://stats.nba.com/stats/commonplayerinfo?LeagueID=00&PlayerID=${id}&SeasonType=Regular+Season` 44 | }, 45 | 46 | playerLog: (id) => { 47 | return `http://stats.nba.com/stats/playergamelog?LeagueID=00&PerMode=PerGame&PlayerID=${id}&Season=${season}&SeasonType=Regular+Season` 48 | }, 49 | 50 | /** 51 | * @params gameDate month/date/year 52 | */ 53 | teamRank: (gameDate) => { 54 | return `http://stats.nba.com/stats/scoreboard?DayOffset=0&LeagueID=00&gameDate=${gameDate}` 55 | }, 56 | 57 | teamInfo: (id) => { 58 | return `http://stats.nba.com/stats/teaminfocommon?LeagueID=00&SeasonType=Regular+Season&TeamID=${id}&season=${season}` 59 | }, 60 | 61 | teamDetail: (id) => { 62 | return `http://stats.nba.com/stats/teamplayerdashboard?DateFrom=&DateTo=&GameSegment=&LastNGames=0&LeagueID=00&Location=&MeasureType=Base&Month=0&OpponentTeamID=0&Outcome=&PaceAdjust=N&PerMode=PerGame&Period=0&PlusMinus=N&Rank=N&Season=${season}&SeasonSegment=&SeasonType=Regular+Season&TeamID=${id}&VsConference=&VsDivision=` 63 | }, 64 | 65 | teamDetailBasic: (id) => { 66 | return `http://stats.nba.com/stats/commonteamroster?LeagueID=00&Season=${season}&TeamID=${id}` 67 | } 68 | } 69 | 70 | export default address 71 | -------------------------------------------------------------------------------- /app/channel/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import address from './address' 4 | import producer from './producer' 5 | 6 | export default class Channel { 7 | 8 | constructor (options) { 9 | this.options = options 10 | } 11 | 12 | getGameGeneral (year, month, date) { 13 | const gen_url = address.gameGeneral(`${year}${month}${date}`) 14 | return window.fetch(gen_url) 15 | .then(res => res.json()) 16 | .then(data => { 17 | const allGames = producer.gameGeneral(data) 18 | if (allGames.live.length + allGames.unstart.length + allGames.over.length === 0) { 19 | return this.getGameGeneral(year, month, parseInt(date, 10) + 1) 20 | } 21 | allGames.gameDate = `${year}-${month}-${date}` 22 | return allGames 23 | }) 24 | } 25 | 26 | getGameDetail (year, month, date, gameId) { 27 | const det_url = address.gameDetail(`${year}${month}${date}`, gameId) 28 | return window.fetch(det_url) 29 | .then(res => res.json()) 30 | .then(data => producer.gameDetail(data)) 31 | } 32 | 33 | getLeagueStanding (year) { 34 | const stand_url = address.leagueStanding(year) 35 | return window.fetch(stand_url) 36 | .then(res => res.json()) 37 | .then(data => producer.leagueStanding(data)) 38 | } 39 | 40 | getPlayerList () { 41 | const url = address.playerList() 42 | return window.fetch(url) 43 | .then(res => res.json()) 44 | .then(data => producer.playerList(data)) 45 | } 46 | 47 | getPlayerInfo (id) { 48 | const url = address.playerInfo(id) 49 | return window.fetch(url) 50 | .then(res => res.json()) 51 | .then(data => producer.playerInfo(data)) 52 | } 53 | 54 | getPlayerLog (id) { 55 | const url = address.playerLog(id) 56 | return window.fetch(url) 57 | .then(res => res.json()) 58 | .then(data => producer.playerLog(data)) 59 | } 60 | 61 | getTeamRank (year, month, date) { 62 | const url = address.teamRank(`${month}/${date}/${year}`) 63 | return window.fetch(url) 64 | .then(res => res.json()) 65 | .then(data => producer.teamRank(data)) 66 | } 67 | 68 | getTeamInfo (id) { 69 | const url = address.teamInfo(id) 70 | return window.fetch(url) 71 | .then(res => res.json()) 72 | .then(data => producer.teamInfo(data)) 73 | } 74 | 75 | getTeamDetail (id) { 76 | /* Get players data and basic info */ 77 | const url = address.teamDetail(id) 78 | const urlBasic = address.teamDetailBasic(id) 79 | return Promise.all([ 80 | window.fetch(url) 81 | .then(res => res.json()) 82 | .then(data => producer.teamDetail(data)), 83 | window.fetch(urlBasic) 84 | .then(res => res.json()) 85 | .then(data => producer.teamDetailBasic(data)) 86 | ]) 87 | .then(result => { 88 | const playerData = result[0] 89 | const playerInfo = result[1] 90 | return playerData.map(player => { 91 | return Object.assign({}, player, playerInfo[player.id]) 92 | }) 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/components/game/GameList.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | ListView, 7 | View, 8 | Text, 9 | PropTypes, 10 | TouchableHighlight 11 | } from 'react-native' 12 | 13 | import {Icon} from 'react-native-icons' 14 | import moment from 'moment-timezone' 15 | import GamePanel from './GamePanel' 16 | import Tabbar from '../share/Tabbar' 17 | 18 | export default class GameList extends Component { 19 | constructor (props) { 20 | super(props) 21 | this.state = { 22 | dataSource: new ListView.DataSource({ 23 | rowHasChanged: (row1, row2) => row1 !== row2 24 | }), 25 | date: this.getToday(), 26 | isToday: true 27 | } 28 | this.mount = true // Control the state of mount 29 | this.timeout = null // Control the state of setTimeout 30 | } 31 | 32 | /** 33 | * TODO: check what will be triggered when user re active app(from backend to front-end) 34 | */ 35 | componentDidMount () { 36 | const {actions} = this.props 37 | const {date} = this.state 38 | actions.getGameGeneral(date[0], date[1], date[2]) 39 | } 40 | 41 | componentWillReceiveProps (props) { 42 | const {live, over, unstart, actions, application} = props 43 | const {dataSource, date} = this.state 44 | const rows = live.data.concat(unstart.data).concat(over.data) 45 | 46 | /* Judge if the navigator is on Game List page */ 47 | if (application.navigator === 'gameIndex') { 48 | if (live.data.length > 0) { 49 | clearTimeout(this.timeout) 50 | this.timeout = setTimeout(() => { 51 | actions.getGameGeneral(date[0], date[1], date[2]) 52 | }, 5000) 53 | } else if (unstart.data.length > 0) { 54 | clearTimeout(this.timeout) 55 | this.timeout = setTimeout(() => { 56 | actions.getGameGeneral(date[0], date[1], date[2]) 57 | }, 120000) 58 | } 59 | } 60 | 61 | if (this.mount) { 62 | this.setState({ 63 | dataSource: dataSource.cloneWithRows(rows) 64 | }) 65 | } 66 | } 67 | 68 | componentWillUnmount () { 69 | this.mount = false 70 | } 71 | 72 | renderRow (game, _, index) { 73 | const {date} = this.state 74 | if (this.mount) { 75 | return () 76 | } 77 | } 78 | 79 | /* Get date format */ 80 | getToday () { 81 | const dateString = moment.tz(Date.now(), 'America/Los_Angeles').format() 82 | const dateArray = dateString.replace('T', '-').split('-') 83 | return dateArray.splice(0, 3) 84 | } 85 | 86 | getYesterday () { 87 | let d = new Date() 88 | d.setDate(d.getDate() - 1) 89 | const dateString = moment.tz(d, 'America/Los_Angeles').format() 90 | const dateArray = dateString.replace('T', '-').split('-') 91 | return dateArray.splice(0, 3) 92 | } 93 | 94 | /* Swith between yesterday and today */ 95 | changeDate () { 96 | const {isToday} = this.state 97 | const {actions} = this.props 98 | 99 | if (isToday) { 100 | const date = this.getYesterday() 101 | actions.getGameGeneral(date[0], date[1], date[2]) 102 | this.setState({ 103 | date, 104 | isToday: false 105 | }) 106 | } else { 107 | const date = this.getToday() 108 | actions.getGameGeneral(date[0], date[1], date[2]) 109 | this.setState({ 110 | date, 111 | isToday: true 112 | }) 113 | } 114 | } 115 | 116 | render () { 117 | const {dataSource, date} = this.state 118 | const {live, over, unstart} = this.props 119 | const gameCount = live.data.length + over.data.length + unstart.data.length 120 | 121 | return ( 122 | 123 | 124 | 125 | 130 | 131 | {date[0] + '-' + date[1] + '-' + date[2]} 132 | {gameCount + ' Games'} 133 | 134 | 135 | 140 | 141 | ) 142 | } 143 | } 144 | 145 | GameList.propTypes = { 146 | actions: PropTypes.object, 147 | live: PropTypes.object, 148 | unstart: PropTypes.object, 149 | over: PropTypes.object 150 | } 151 | 152 | const styles = StyleSheet.create({ 153 | container: { 154 | flex: 1 155 | }, 156 | // Header 157 | header: { 158 | backgroundColor: '#4D98E4', 159 | height: 100, 160 | flexDirection: 'column', 161 | position: 'relative', 162 | paddingLeft: 15 163 | }, 164 | calendarIcon: { 165 | alignSelf: 'flex-end', 166 | height: 15, 167 | marginRight: 15, 168 | marginTop: 12, 169 | width: 25 170 | }, 171 | gameDate: { 172 | color: '#fff', 173 | fontWeight: '200', 174 | fontSize: 25 175 | }, 176 | gameCount: { 177 | color: '#fff', 178 | fontWeight: '200', 179 | fontSize: 14 180 | }, 181 | // List View 182 | listView: { 183 | backgroundColor: '#fff', 184 | flex: 6, 185 | flexDirection: 'column', 186 | paddingTop: 12 187 | } 188 | }) 189 | -------------------------------------------------------------------------------- /app/components/game/GamePanel.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | PropTypes, 5 | Component, 6 | StyleSheet, 7 | View, 8 | Text, 9 | TouchableHighlight, 10 | Image, 11 | PixelRatio, 12 | Platform 13 | } from 'react-native' 14 | 15 | import GameDetail from './GameDetail' 16 | import teamMap from '../../utils/team-map' 17 | 18 | export default class GamePanel extends Component { 19 | 20 | onPressRow () { 21 | const {navigator, game, date, actions} = this.props 22 | if (game.type !== 'unstart') { 23 | actions.toNavigation('gameDetail') 24 | .then(() => { 25 | navigator.push(Object.assign({}, { 26 | name: 'GameDetail', 27 | component: GameDetail, 28 | game, 29 | date 30 | })) 31 | }) 32 | .catch(err => console.error(err)) 33 | } 34 | } 35 | 36 | render () { 37 | const {game} = this.props 38 | 39 | const homeAbb = game.home.team.toLowerCase() 40 | const visitorAbb = game.visitor.team.toLowerCase() 41 | 42 | let gameProcess = '' 43 | let cssType = '' 44 | switch (game.type) { 45 | case 'unstart': 46 | gameProcess = game.date.replace(/\s*ET\s*/, '') 47 | cssType = 'Unstart' 48 | break 49 | case 'live': 50 | gameProcess += game.process.quarter + ' ' 51 | gameProcess += game.process.time.replace(/\s+/, '') 52 | cssType = 'Live' 53 | break 54 | case 'over': 55 | gameProcess = 'Final' 56 | cssType = 'Over' 57 | break 58 | default: 59 | return 60 | } 61 | 62 | const homeTeamLogo = teamMap[homeAbb].logo 63 | const visitorTeamLogo = teamMap[visitorAbb].logo 64 | 65 | return ( 66 | 67 | 68 | 69 | 70 | 71 | {teamMap[homeAbb].city} 72 | {teamMap[homeAbb].team} 73 | 74 | 75 | 76 | {gameProcess} 77 | {game.type !== 'unstart' && 78 | 79 | {game.home.score} 80 | 81 | {game.visitor.score} 82 | 83 | } 84 | 85 | 86 | 87 | 88 | {teamMap[visitorAbb].city} 89 | {teamMap[visitorAbb].team} 90 | 91 | 92 | 93 | ) 94 | } 95 | } 96 | 97 | GamePanel.propTypes = { 98 | navigator: PropTypes.object, 99 | game: PropTypes.object, 100 | date: PropTypes.array, 101 | actions: PropTypes.object 102 | } 103 | 104 | const gameFontSize = Platform.OS === 'ios' ? 31 : 25 105 | 106 | const styles = StyleSheet.create({ 107 | container: { 108 | borderRadius: 5, 109 | flex: 1, 110 | flexDirection: 'row', 111 | height: 95, 112 | marginHorizontal: 12, 113 | marginBottom: 10 114 | }, 115 | // Team 116 | team: { 117 | alignItems: 'center', 118 | borderRadius: 5, 119 | flex: 1.5 120 | }, 121 | teamLogo: { 122 | width: 50, 123 | height: 50, 124 | marginTop: 10 125 | }, 126 | teamCity: { 127 | color: '#fff', 128 | fontSize: 11, 129 | marginTop: 2 130 | }, 131 | teamName: { 132 | color: '#fff', 133 | fontWeight: 'bold', 134 | fontSize: 13, 135 | position: 'relative', 136 | top: 0 137 | }, 138 | // Info 139 | gameInfo: { 140 | alignItems: 'center', 141 | flex: 1.5, 142 | flexDirection: 'column' 143 | }, 144 | infoProcess: { 145 | color: '#fff', 146 | fontSize: 10, 147 | marginTop: 22, 148 | marginBottom: 3 149 | }, 150 | processUnstart: { 151 | fontSize: 22, 152 | position: 'relative', 153 | top: 13 154 | }, 155 | infoScorePanel: { 156 | flex: 1, 157 | flexDirection: 'row', 158 | justifyContent: 'center' 159 | }, 160 | infoScore: { 161 | color: '#fff', 162 | fontWeight: '100', 163 | fontSize: gameFontSize, 164 | textAlign: 'center', 165 | width: 50 166 | }, 167 | infoDivider: { 168 | backgroundColor: 'rgba(255, 255, 255, 0.3)', 169 | height: 25, 170 | marginTop: 7, 171 | marginLeft: 10, 172 | marginRight: 10, 173 | width: 2 / PixelRatio.get() 174 | } 175 | }) 176 | -------------------------------------------------------------------------------- /app/components/game/GamePlayers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | PropTypes, 7 | Text, 8 | View, 9 | ScrollView, 10 | ListView 11 | } from 'react-native' 12 | 13 | class PlayerRow extends Component { 14 | render () { 15 | const {player, last} = this.props 16 | 17 | return ( 18 | 19 | {player.first_name.substring(0, 1) + '.' + player.last_name} 20 | {player.starting_position ? player.starting_position : '-'} 21 | {player.points} 22 | {player.assists} 23 | {parseInt(player.rebounds_defensive, 10) + parseInt(player.rebounds_offensive, 10)} 24 | {player.field_goals_made + ' - ' + player.field_goals_attempted} 25 | {player.blocks} 26 | {player.steals} 27 | {player.three_pointers_made + ' - ' + player.three_pointers_attempted} 28 | {player.free_throws_made + ' - ' + player.free_throws_attempted} 29 | {player.turnovers} 30 | {player.fouls} 31 | {player.plus_minus} 32 | {player.minutes} 33 | 34 | ) 35 | } 36 | } 37 | 38 | PlayerRow.propTypes = { 39 | player: PropTypes.object, 40 | last: PropTypes.bool 41 | } 42 | 43 | export default class GamePlayers extends Component { 44 | 45 | constructor (props) { 46 | super(props) 47 | this.state = { 48 | dataSource: new ListView.DataSource({ 49 | rowHasChanged: (row1, row2) => row1 !== row2 50 | }) 51 | } 52 | } 53 | 54 | componentDidMount () { 55 | const {detail} = this.props 56 | this.updateDataSource(detail) 57 | } 58 | 59 | componentWillReceiveProps (props) { 60 | const {detail} = props 61 | this.updateDataSource(detail) 62 | } 63 | 64 | updateDataSource (detail) { 65 | const {dataSource} = this.state 66 | let rows = Object.assign([], detail.player) 67 | rows.unshift({}) // unshift an empty object, use it as title row 68 | 69 | this.setState({ 70 | dataSource: dataSource.cloneWithRows(rows) 71 | }) 72 | } 73 | 74 | renderTitle (index) { 75 | return ( 76 | 77 | 78 | P 79 | PTS 80 | AST 81 | REB 82 | FG 83 | BLK 84 | STL 85 | 3PT 86 | FT 87 | TO 88 | PF 89 | +/- 90 | MIN 91 | 92 | ) 93 | } 94 | 95 | renderRow (player, _, i) { 96 | const index = parseInt(i, 10) 97 | const {detail} = this.props 98 | 99 | if (index === 0) { 100 | return this.renderTitle(index) 101 | } 102 | return () 103 | } 104 | 105 | render () { 106 | const {dataSource} = this.state 107 | const horizontal = true 108 | return ( 109 | /** 110 | * @TODO: I don't know what is the best practice to scroll both horizontal and vertical 111 | * The method I used here may not adaptable 112 | */ 113 | 114 | 118 | 122 | 123 | 124 | ) 125 | } 126 | } 127 | 128 | GamePlayers.propTypes = { 129 | detail: PropTypes.object 130 | } 131 | 132 | const styles = StyleSheet.create({ 133 | // Container 134 | container: { 135 | flex: 13, 136 | position: 'relative' 137 | }, 138 | // Scroll 139 | scrollView: { 140 | flex: 1, 141 | width: 400 142 | }, 143 | // List 144 | listView: { 145 | flex: 1, 146 | flexDirection: 'column', 147 | marginBottom: 48, 148 | marginRight: 30, 149 | width: 800 150 | }, 151 | // Player box (tr) 152 | titleRow: { 153 | borderBottomColor: '#c2c2c2', 154 | borderBottomWidth: 2, 155 | height: 30, 156 | borderStyle: 'solid' 157 | }, 158 | playerBox: { 159 | alignItems: 'stretch', 160 | borderBottomColor: '#c2c2c2', 161 | borderBottomWidth: 1, 162 | flex: 1, 163 | flexDirection: 'row', 164 | height: 30 165 | }, 166 | playerBoxLast: { 167 | borderBottomWidth: 0 168 | }, 169 | // Every box (td) 170 | title: { 171 | alignSelf: 'center', 172 | color: '#7F7F7F', 173 | fontSize: 12 174 | }, 175 | pName: { 176 | color: '#222', 177 | fontSize: 12, 178 | paddingLeft: 5 179 | }, 180 | dataBox: { 181 | alignSelf: 'center', 182 | color: '#222', 183 | fontSize: 11 184 | }, 185 | p1: { 186 | alignItems: 'center', 187 | borderRightColor: '#c2c2c2', 188 | borderRightWidth: 1, 189 | flex: 1, 190 | flexDirection: 'row' 191 | }, 192 | lastP1: { 193 | borderRightWidth: 0 194 | }, 195 | p2: { 196 | borderRightColor: '#c2c2c2', 197 | borderRightWidth: 1, 198 | flex: 2, 199 | flexDirection: 'row' 200 | }, 201 | p2Name: { 202 | alignItems: 'center', 203 | flexDirection: 'row' 204 | } 205 | }) 206 | -------------------------------------------------------------------------------- /app/components/player/PlayerDetail.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | View, 7 | Text, 8 | Image, 9 | ScrollView, 10 | TouchableHighlight, 11 | PropTypes, 12 | Dimensions, 13 | InteractionManager 14 | } from 'react-native' 15 | 16 | import {Icon} from 'react-native-icons' 17 | import teamInfo from '../../utils/team-map' 18 | import PlayerLog from './PlayerLog' 19 | import PlayerTrend from './PlayerTrend' 20 | 21 | const navigationHeight = 30 22 | const headerHeight = 120 23 | 24 | export default class PlayerDetail extends Component { 25 | 26 | constructor (props) { 27 | super(props) 28 | this.state = { 29 | player: null 30 | } 31 | } 32 | 33 | componentDidMount () { 34 | const {actions} = this.props 35 | const {player} = this.props.route 36 | 37 | InteractionManager.runAfterInteractions(() => { 38 | actions.getPlayerDetail(player.id) 39 | .then(() => { 40 | actions.getPlayerLog(player.id) 41 | }) 42 | }) 43 | } 44 | 45 | componentWillReceiveProps (props) { 46 | const {playerLoaded} = props 47 | const {player} = this.props.route 48 | 49 | this.setState({ 50 | player: playerLoaded[player.id] 51 | }) 52 | } 53 | 54 | onPressBack () { 55 | const {navigator} = this.props 56 | navigator.pop() 57 | } 58 | 59 | render () { 60 | const {route, playerLoaded} = this.props 61 | const player = playerLoaded[route.player.id] || this.state.player 62 | 63 | const team = player && player.team.toLowerCase() 64 | /* ScrollView need a specifical height */ 65 | const scrollHeight = Dimensions.get('window').height - navigationHeight - headerHeight 66 | return ( 67 | 68 | {player && 69 | 70 | 71 | 74 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | {player.firstName + ' ' + player.lastName} 87 | {player.jersey} 88 | 89 | 90 | 91 | 92 | 93 | {player.pts} 94 | Points 95 | 96 | 97 | {player.ast} 98 | Assists 99 | 100 | 101 | {player.reb} 102 | Rebounds 103 | 104 | 105 | {player.log && 106 | 107 | 108 | 109 | 110 | 111 | } 112 | 113 | 114 | } 115 | 116 | ) 117 | } 118 | } 119 | 120 | const styles = StyleSheet.create({ 121 | container: { 122 | flexDirection: 'column' 123 | }, 124 | // Navigation 125 | navigation: { 126 | flexDirection: 'row', 127 | height: navigationHeight 128 | }, 129 | backIcon: { 130 | height: 30, 131 | marginLeft: 6, 132 | marginTop: 6, 133 | width: 30 134 | }, 135 | // Header part 136 | header: { 137 | height: headerHeight 138 | }, 139 | portraitView: { 140 | alignSelf: 'center', 141 | backgroundColor: '#fff', 142 | borderRadius: 60, 143 | marginTop: 5, 144 | height: 60, 145 | width: 60 146 | }, 147 | portrait: { 148 | height: 60, 149 | width: 60, 150 | borderRadius: 30 151 | }, 152 | name: { 153 | alignSelf: 'center', 154 | color: '#fff', 155 | fontSize: 16, 156 | marginTop: 5 157 | }, 158 | jersey: { 159 | alignSelf: 'center', 160 | color: '#fff', 161 | fontSize: 14 162 | }, 163 | // Basic data 164 | basicData: { 165 | flexDirection: 'row', 166 | height: 28, 167 | justifyContent: 'center' 168 | }, 169 | basicDataBlock: { 170 | alignItems: 'flex-end', 171 | flexDirection: 'row', 172 | justifyContent: 'center', 173 | width: 100 174 | }, 175 | basicDataNumber: { 176 | color: '#909CAF', 177 | fontSize: 19, 178 | fontWeight: '500', 179 | marginRight: 3 180 | }, 181 | basicDataMark: { 182 | color: '#909CAF', 183 | fontSize: 12, 184 | position: 'relative', 185 | bottom: 1 186 | }, 187 | // Divider 188 | logDivider: { 189 | backgroundColor: '#eee', 190 | height: 3, 191 | marginHorizontal: 10, 192 | marginVertical: 15 193 | } 194 | }) 195 | 196 | PlayerDetail.propTypes = { 197 | player: PropTypes.object, 198 | order: PropTypes.number, 199 | actions: PropTypes.object, 200 | navigator: PropTypes.object, 201 | route: PropTypes.object, 202 | playerLoaded: PropTypes.object 203 | } 204 | -------------------------------------------------------------------------------- /app/components/player/PlayerIndex.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | View, 7 | Text, 8 | ListView, 9 | TextInput, 10 | PropTypes, 11 | TouchableHighlight 12 | } from 'react-native' 13 | 14 | import {Icon} from 'react-native-icons' 15 | import Tabbar from '../share/Tabbar' 16 | import PlayerDetail from './PlayerDetail' 17 | 18 | export default class PlayerIndex extends Component { 19 | 20 | constructor (props) { 21 | super(props) 22 | this.state = { 23 | text: '', 24 | dataSource: new ListView.DataSource({ 25 | rowHasChanged: (row1, row2) => row1 !== row2 26 | }) 27 | } 28 | this.playerList = null 29 | this.searchRecent = [] 30 | this.inputDelay = null 31 | this.mount = true 32 | } 33 | 34 | componentDidMount () { 35 | const {actions} = this.props 36 | actions.getPlayerList() 37 | .then(() => { 38 | actions.getSearchRecord() 39 | }) 40 | } 41 | 42 | componentWillReceiveProps (props) { 43 | const {playerList} = props 44 | this.playerList = playerList.data 45 | this.searchRecent = playerList.recent 46 | } 47 | 48 | componentWillUnmount () { 49 | this.mount = false 50 | } 51 | 52 | onInput (text) { 53 | this.setState({ 54 | text 55 | }) 56 | /* Search after user stop typing */ 57 | clearTimeout(this.inputDelay) 58 | this.inputDelay = setTimeout(() => { 59 | this.getResult(text) 60 | }, 1500) 61 | } 62 | 63 | getResult (text) { 64 | if (text.length > 0) { 65 | const {playerList} = this 66 | const {dataSource} = this.state 67 | 68 | const reg = new RegExp(text, 'i') 69 | const players = playerList.filter(player => { 70 | return reg.test(player.name) 71 | }) 72 | this.setState({ 73 | dataSource: dataSource.cloneWithRows(players) 74 | }) 75 | } 76 | } 77 | 78 | selectPlayer (player) { 79 | const {actions, navigator} = this.props 80 | actions.setSearchRecord(player) 81 | navigator.push({ 82 | name: 'PlayerDetail', 83 | component: PlayerDetail, 84 | player 85 | }) 86 | } 87 | 88 | renderRow (player, _, index) { 89 | return ( 90 | 91 | 92 | 93 | {player.name} 94 | {player.teamCity + ' ' + player.teamName} 95 | 96 | 97 | 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | render () { 105 | const {text, dataSource} = this.state 106 | 107 | let myDataSource = dataSource 108 | if (!text.length) { 109 | myDataSource = dataSource.cloneWithRows(this.searchRecent) 110 | } 111 | 112 | return ( 113 | 114 | 115 | 116 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 136 | 137 | ) 138 | } 139 | } 140 | 141 | const styles = StyleSheet.create({ 142 | container: { 143 | flex: 1 144 | }, 145 | // Header 146 | header: { 147 | alignItems: 'center', 148 | backgroundColor: '#E66840', 149 | flexDirection: 'row', 150 | height: 100 151 | }, 152 | headerInner: { 153 | flex: 1, 154 | flexDirection: 'row', 155 | justifyContent: 'center' 156 | }, 157 | textInput: { 158 | backgroundColor: '#BD4C29', 159 | borderRadius: 5, 160 | color: '#fff', 161 | fontSize: 14, 162 | height: 40, 163 | paddingHorizontal: 5, 164 | width: 260 165 | }, 166 | searchIconView: { 167 | backgroundColor: '#BD4C29', 168 | borderTopRightRadius: 5, 169 | borderBottomRightRadius: 5, 170 | height: 40, 171 | left: -5, 172 | position: 'relative', 173 | width: 40 174 | }, 175 | searchIcon: { 176 | width: 16, 177 | height: 16, 178 | left: 20, 179 | marginLeft: -8, 180 | marginTop: -8, 181 | position: 'absolute', 182 | top: 20 183 | }, 184 | // List 185 | list: { 186 | flex: 1 187 | }, 188 | panel: { 189 | borderColor: '#979797', 190 | borderBottomWidth: 1, 191 | height: 65, 192 | flexDirection: 'row' 193 | }, 194 | panelLeft: { 195 | flex: 1, 196 | paddingLeft: 10, 197 | justifyContent: 'center' 198 | }, 199 | panelName: { 200 | color: '#6B7C96', 201 | fontSize: 17 202 | }, 203 | panelTeam: { 204 | color: '#909CAF', 205 | fontSize: 13 206 | }, 207 | panelRight: { 208 | height: 65, 209 | position: 'relative', 210 | width: 30 211 | }, 212 | enterIcon: { 213 | height: 30, 214 | left: 15, 215 | marginLeft: -15, 216 | marginTop: -15, 217 | position: 'absolute', 218 | top: 32.5, 219 | width: 30 220 | } 221 | }) 222 | 223 | PlayerIndex.propTypes = { 224 | actions: PropTypes.object, 225 | navigator: PropTypes.object 226 | } 227 | -------------------------------------------------------------------------------- /app/components/player/PlayerLog.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | PropTypes, 6 | View, 7 | Text, 8 | Animated, 9 | StyleSheet, 10 | TouchableHighlight, 11 | Dimensions 12 | } from 'react-native' 13 | 14 | import {Icon} from 'react-native-icons' 15 | 16 | export default class PlayerLog extends Component { 17 | 18 | constructor (props) { 19 | super(props) 20 | const data = this.props.data[0] 21 | const width = this.getWidth(data) 22 | this.state = { 23 | pts: new Animated.Value(width.pts), 24 | ast: new Animated.Value(width.ast), 25 | reb: new Animated.Value(width.reb), 26 | stl: new Animated.Value(width.stl), 27 | blk: new Animated.Value(width.blk), 28 | tov: new Animated.Value(width.tov), 29 | min: new Animated.Value(width.min), 30 | currentIndex: 0 31 | } 32 | } 33 | 34 | /** 35 | * Calculate width of each bar 36 | * @params {pts: {Number}, ast, reb, stl, blk, tov, min} 37 | * @return {pts: {Number}, ast, reb, stl, blk, tov, min} 38 | */ 39 | getWidth (data) { 40 | const deviceWidth = Dimensions.get('window').width 41 | const maxWidth = 350 42 | const indicators = ['pts', 'ast', 'reb', 'stl', 'blk', 'tov', 'min'] 43 | const unit = { 44 | ptsUnit: Math.floor(maxWidth / 45), 45 | astUnit: Math.floor(maxWidth / 15), 46 | rebUnit: Math.floor(maxWidth / 18), 47 | stlUnit: Math.floor(maxWidth / 6), 48 | blkUnit: Math.floor(maxWidth / 7), 49 | tovUnit: Math.floor(maxWidth / 10), 50 | minUnit: Math.floor(maxWidth / 60) 51 | } 52 | let width = {} 53 | let widthCap // Give with a max cap 54 | indicators.forEach(item => { 55 | /* React-Native bug: if width=0 at first time, the borderRadius can't be implemented in the View */ 56 | widthCap = data[item] * unit[`${item}Unit`] || 5 57 | width[item] = widthCap <= (deviceWidth - 50) ? widthCap : (deviceWidth - 50) 58 | }) 59 | 60 | return width 61 | } 62 | 63 | onPressLeft () { 64 | const {currentIndex} = this.state 65 | const {data} = this.props 66 | if (currentIndex < data.length - 1) this.handleAnimation(currentIndex + 1) 67 | } 68 | 69 | onPressRight () { 70 | const {currentIndex} = this.state 71 | if (currentIndex > 0) this.handleAnimation(currentIndex - 1) 72 | } 73 | 74 | handleAnimation (index) { 75 | const {data} = this.props 76 | const width = this.getWidth(data[index]) 77 | const timing = Animated.timing 78 | 79 | const indicators = ['pts', 'ast', 'reb', 'stl', 'blk', 'tov', 'min'] 80 | Animated.parallel(indicators.map(item => { 81 | return timing(this.state[item], {toValue: width[item]}) 82 | })).start() 83 | /** 84 | * Animated won't trigger react life cycle 85 | * I'm not sure if using animated and setState in a same time would affect performance, not bad for now 86 | */ 87 | this.setState({ 88 | currentIndex: index 89 | }) 90 | } 91 | 92 | render () { 93 | const {pts, ast, reb, stl, blk, tov, min} = this.state 94 | const {currentIndex} = this.state 95 | const data = this.props.data[currentIndex] 96 | 97 | const d = new Date(data.gameDate) 98 | const date = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() 99 | 100 | /* set opacity=0 if no prev or no next, or the size will be changed unexpected */ 101 | const canPrev = currentIndex < this.props.data.length - 1 ? 1 : 0 102 | const canNext = currentIndex > 0 ? 1 : 0 103 | return ( 104 | 105 | 106 | 107 | Points 108 | 109 | {pts && 110 | 111 | } 112 | {data.pts} 113 | 114 | 115 | 116 | Assists 117 | 118 | {ast && 119 | 120 | } 121 | {data.ast} 122 | 123 | 124 | 125 | Rebounds 126 | 127 | {reb && 128 | 129 | } 130 | {data.reb} 131 | 132 | 133 | 134 | Steals 135 | 136 | {stl && 137 | 138 | } 139 | {data.stl} 140 | 141 | 142 | 143 | Blocks 144 | 145 | {blk && 146 | 147 | } 148 | {data.blk} 149 | 150 | 151 | 152 | Turnovers 153 | 154 | {tov && 155 | 156 | } 157 | {data.tov} 158 | 159 | 160 | 161 | Minutes 162 | 163 | {min && 164 | 165 | } 166 | {data.min} 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | {date} 175 | 176 | 177 | 178 | 179 | 180 | ) 181 | } 182 | } 183 | 184 | const styles = StyleSheet.create({ 185 | container: { 186 | flexDirection: 'column', 187 | marginTop: 6 188 | }, 189 | // Item 190 | item: { 191 | flexDirection: 'column', 192 | marginBottom: 5, 193 | paddingHorizontal: 10 194 | }, 195 | label: { 196 | color: '#CBCBCB', 197 | flex: 1, 198 | fontSize: 12, 199 | position: 'relative', 200 | top: 2 201 | }, 202 | data: { 203 | flex: 2, 204 | flexDirection: 'row' 205 | }, 206 | dataNumber: { 207 | color: '#CBCBCB', 208 | fontSize: 11 209 | }, 210 | // Bar 211 | bar: { 212 | alignSelf: 'center', 213 | borderRadius: 5, 214 | height: 8, 215 | marginRight: 5 216 | }, 217 | points: { 218 | backgroundColor: '#F55443' 219 | }, 220 | assists: { 221 | backgroundColor: '#FCBD24' 222 | }, 223 | rebounds: { 224 | backgroundColor: '#59838B' 225 | }, 226 | steals: { 227 | backgroundColor: '#4D98E4' 228 | }, 229 | blocks: { 230 | backgroundColor: '#418E50' 231 | }, 232 | turnovers: { 233 | backgroundColor: '#7B7FEC' 234 | }, 235 | minutes: { 236 | backgroundColor: '#3ABAA4' 237 | }, 238 | // controller 239 | controller: { 240 | flex: 1, 241 | flexDirection: 'row', 242 | justifyContent: 'center', 243 | marginTop: 15 244 | }, 245 | button: { 246 | flex: 1, 247 | position: 'relative', 248 | top: -1 249 | }, 250 | chevronLeft: { 251 | alignSelf: 'flex-end', 252 | height: 28, 253 | marginRight: 10, 254 | width: 28 255 | }, 256 | chevronRight: { 257 | alignSelf: 'flex-start', 258 | height: 28, 259 | marginLeft: 10, 260 | width: 28 261 | }, 262 | date: { 263 | color: '#6B7C96', 264 | flex: 1, 265 | fontSize: 22, 266 | fontWeight: '300', 267 | height: 28, 268 | textAlign: 'center' 269 | } 270 | 271 | }) 272 | 273 | PlayerLog.propTypes = { 274 | data: PropTypes.array 275 | } 276 | -------------------------------------------------------------------------------- /app/components/player/PlayerTrend.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | PropTypes, 6 | StyleSheet, 7 | View, 8 | Text, 9 | ScrollView 10 | } from 'react-native' 11 | 12 | import PlayerTrendBarItem from './PlayerTrendBarItem' 13 | const barInterval = 2 14 | const barItemTop = 16 15 | 16 | export default class PlayerTrend extends Component { 17 | 18 | constructor (props) { 19 | super(props) 20 | this.unitHeight = 2 21 | } 22 | /** 23 | * calculate lowest, highest, average, total of a froup of data 24 | * @params data{Array} indicatior{String} 25 | * @return {low, high, lowDate, highDate, avg, sum, count} 26 | */ 27 | calculateLog (data, indicator) { 28 | const count = data.length 29 | let high = data[0][indicator] 30 | let low = data[0][indicator] 31 | let highDate = data[0]['gameDate'] 32 | let lowDate = data[0]['gameDate'] 33 | let sum = 0 34 | 35 | let value 36 | data.forEach((d, index) => { 37 | value = d[indicator] 38 | sum += value 39 | if (value < low) { 40 | low = value 41 | lowDate = data[index]['gameDate'] 42 | } else if (value > high) { 43 | high = value 44 | highDate = data[index]['gameDate'] 45 | } 46 | }) 47 | 48 | return { 49 | low, 50 | high, 51 | count, 52 | sum, 53 | avg: sum / count, 54 | highDate, 55 | lowDate 56 | } 57 | } 58 | 59 | renderBars (data, high, low, color) { 60 | const {unitHeight} = this 61 | 62 | return data.map((value, index) => { 63 | return ( 64 | 74 | ) 75 | }) 76 | } 77 | 78 | render () { 79 | const {data, color} = this.props 80 | const {unitHeight} = this 81 | const footData = this.calculateLog(data, 'plusMinus') 82 | 83 | const scrollHeight = footData.high * unitHeight + Math.abs(footData.low) * unitHeight + barItemTop 84 | return ( 85 | 86 | + / - 87 | 94 | {this.renderBars(data.map(d => d.plusMinus), footData.high, footData.low, color)} 95 | 96 | 97 | 98 | {(footData.avg).toFixed(2)} 99 | avg 100 | 101 | 102 | 103 | Highest: 104 | {footData.high} 105 | {'in ' + footData.highDate} 106 | 107 | 108 | Lowest: 109 | {footData.low} 110 | {'in ' + footData.lowDate} 111 | 112 | 113 | 114 | 115 | ) 116 | } 117 | } 118 | 119 | const styles = StyleSheet.create({ 120 | container: { 121 | paddingHorizontal: 10, 122 | paddingBottom: 30 123 | }, 124 | title: { 125 | color: '#6B7C96', 126 | fontSize: 13, 127 | fontWeight: '600' 128 | }, 129 | // Bar chart 130 | scrollView: { 131 | position: 'relative' 132 | }, 133 | // Summary 134 | summary: { 135 | flex: 1, 136 | flexDirection: 'row', 137 | height: 40, 138 | marginTop: 20 139 | }, 140 | sumLeft: { 141 | alignItems: 'flex-end', 142 | bottom: -4, // Why flex-end can't align them... 143 | flex: 1, 144 | flexDirection: 'row', 145 | position: 'relative' 146 | }, 147 | sumAvg: { 148 | color: '#909CAF', 149 | fontSize: 25, 150 | fontWeight: '200' 151 | }, 152 | sumAvgLabel: { 153 | color: '909CAF', 154 | marginLeft: 2, 155 | position: 'relative', 156 | top: -3 157 | }, 158 | sumRight: { 159 | flex: 1, 160 | justifyContent: 'flex-end' 161 | }, 162 | sumPolarItem: { 163 | alignItems: 'flex-end', 164 | flexDirection: 'row', 165 | justifyContent: 'flex-end', 166 | marginTop: 2 167 | }, 168 | sumPolarLabel: { 169 | color: '#909CAF', 170 | fontSize: 11, 171 | marginLeft: 3 172 | }, 173 | sumPolarNumber: { 174 | color: '#6B7C96', 175 | fontSize: 15, 176 | marginLeft: 3, 177 | position: 'relative', 178 | top: 1.5 179 | } 180 | }) 181 | 182 | PlayerTrend.propTypes = { 183 | data: PropTypes.array, 184 | color: PropTypes.string 185 | } 186 | -------------------------------------------------------------------------------- /app/components/player/PlayerTrendBarItem.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | PropTypes, 6 | View, 7 | StyleSheet, 8 | TouchableHighlight, 9 | Text, 10 | Dimensions 11 | } from 'react-native' 12 | 13 | const tooltipWidth = 100 14 | const barWidth = 15 15 | 16 | export default class PlayerTrendBarItem extends Component { 17 | 18 | constructor (props) { 19 | super(props) 20 | this.state = { 21 | isHover: false, 22 | isHoverCovered: false, 23 | isHoverCoveredRight: false 24 | } 25 | } 26 | 27 | onPressIn (e) { 28 | const screenWidth = Dimensions.get('window').width 29 | 30 | this.setState({ 31 | isHover: true, 32 | isHoverCoveredLeft: e.nativeEvent.pageX < (tooltipWidth / 2 + 10), 33 | isHoverCoveredRight: e.nativeEvent.pageX + tooltipWidth / 2 + 20 > screenWidth 34 | }) 35 | } 36 | 37 | onPressOut (e) { 38 | this.setState({ 39 | isHover: false, 40 | isHoverCovered: false, 41 | isHoverCoveredRight: false 42 | }) 43 | } 44 | 45 | render () { 46 | const {color, low, high, value, date, unitHeight, barInterval, barItemTop} = this.props 47 | const {isHover, isHoverCoveredLeft, isHoverCoveredRight} = this.state 48 | 49 | let entity 50 | let empty 51 | let wrapperStyle = {} 52 | if (value >= 0) { 53 | entity = value 54 | empty = high - value 55 | } else { 56 | entity = Math.abs(value) 57 | empty = Math.abs(low) - entity 58 | wrapperStyle = { 59 | top: high * unitHeight, 60 | right: barInterval, 61 | transform: [{ 62 | rotate: '180deg' 63 | }] 64 | } 65 | } 66 | 67 | /* Prevent tooltip covered by the edge */ 68 | let tooltipPosition = { 69 | left: -(tooltipWidth / 2), 70 | marginLeft: barWidth / 2 71 | } 72 | let tooltipMark = { 73 | left: tooltipWidth / 2, 74 | marginLeft: -6, 75 | borderLeftWidth: 6, 76 | borderRightWidth: 6 77 | } 78 | if (isHoverCoveredLeft) { 79 | tooltipPosition.left = 0 80 | tooltipPosition.marginLeft = 0 81 | tooltipMark.left = 5 82 | tooltipMark.marginLeft = 0 83 | 84 | delete tooltipMark.borderLeftWidth 85 | } else if (isHoverCoveredRight) { 86 | delete tooltipPosition.left 87 | delete tooltipPosition.marginLeft 88 | delete tooltipMark.left 89 | delete tooltipMark.marginLeft 90 | 91 | tooltipPosition.right = 3 92 | delete tooltipMark.borderRightWidth 93 | tooltipMark.right = 5 94 | } 95 | 96 | const baseStyle = { 97 | backgroundColor: color, 98 | marginRight: barInterval 99 | } 100 | return ( 101 | 105 | 106 | 107 | 108 | 109 | 110 | {isHover && 111 | 112 | {value + ' in ' + date} 113 | 114 | 115 | } 116 | 117 | 118 | ) 119 | } 120 | } 121 | 122 | const styles = StyleSheet.create({ 123 | container: { 124 | position: 'relative' 125 | }, 126 | // Bar 127 | barWrapper: { 128 | position: 'relative' 129 | }, 130 | bar: { 131 | width: barWidth 132 | }, 133 | empty: { 134 | opacity: 0.2 135 | }, 136 | // Tooltip 137 | tooltip: { 138 | backgroundColor: 'rgba(0, 0, 0, 0.8)', 139 | borderRadius: 5, 140 | paddingHorizontal: 3, 141 | paddingVertical: 2, 142 | position: 'absolute', 143 | top: -17, 144 | width: tooltipWidth 145 | }, 146 | tooltipContent: { 147 | color: '#fff', 148 | fontSize: 9, 149 | textAlign: 'center' 150 | }, 151 | tooltipMark: { 152 | borderTopColor: 'rgba(0, 0, 0, 0.8)', 153 | borderTopWidth: 5, 154 | borderLeftColor: 'transparent', 155 | borderRightColor: 'transparent', 156 | bottom: -5, 157 | position: 'absolute', 158 | height: 0, 159 | width: 0 160 | } 161 | }) 162 | 163 | PlayerTrendBarItem.propTypes = { 164 | value: PropTypes.number, 165 | high: PropTypes.number, 166 | low: PropTypes.number, 167 | color: PropTypes.string, 168 | unitHeight: PropTypes.number, 169 | date: PropTypes.string, 170 | barInterval: PropTypes.number, 171 | barItemTop: PropTypes.number 172 | } 173 | -------------------------------------------------------------------------------- /app/components/share/NavigatorBar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @note I didn't customer navigator bar because could be flick when switching 3 | */ 4 | 'use strict' 5 | 6 | import React, { 7 | Component, 8 | View, 9 | PropTypes 10 | } from 'react-native' 11 | 12 | export default class NavigatorBar extends Component { 13 | constructor (props) { 14 | super(props) 15 | this.state = { 16 | presentedIndex: 0 17 | } 18 | } 19 | 20 | componentDidMount () { 21 | const {navState} = this.props 22 | this.setState({ 23 | presentedIndex: navState.presentedIndex 24 | }) 25 | } 26 | 27 | componentWillReceiveProps (props) { 28 | this.setState({ 29 | presentedIndex: props.navState.routeStack.length - 1 30 | }) 31 | } 32 | 33 | render () { 34 | return ( 35 | 36 | ) 37 | } 38 | } 39 | 40 | NavigatorBar.propTypes = { 41 | navState: PropTypes.object 42 | } 43 | 44 | -------------------------------------------------------------------------------- /app/components/share/Tabbar.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | View, 7 | Text, 8 | TouchableHighlight, 9 | PropTypes 10 | } from 'react-native' 11 | 12 | export default class Tabbar extends Component { 13 | 14 | onPress (tab) { 15 | const {actions} = this.props 16 | actions.changeTab(tab) 17 | } 18 | 19 | render () { 20 | const {tab} = this.props 21 | 22 | return ( 23 | 24 | 25 | 26 | 27 | Game 28 | 29 | {tab === 'game' && 30 | 31 | } 32 | 33 | 34 | 35 | 36 | 37 | Players 38 | 39 | {tab === 'players' && 40 | 41 | } 42 | 43 | 44 | 45 | 46 | 47 | Teams 48 | 49 | {tab === 'teams' && 50 | 51 | } 52 | 53 | 54 | 55 | ) 56 | } 57 | } 58 | 59 | const styles = StyleSheet.create({ 60 | container: { 61 | flexDirection: 'row', 62 | height: 45 63 | }, 64 | tab: { 65 | alignItems: 'center', 66 | flex: 1 67 | }, 68 | tabview: { 69 | flex: 1, 70 | flexDirection: 'column' 71 | }, 72 | tabviewInner: { 73 | flexDirection: 'row', 74 | flex: 1, 75 | position: 'relative' 76 | }, 77 | tabtext: { 78 | alignSelf: 'center', 79 | flex: 1, 80 | fontSize: 13, 81 | color: '#fff' 82 | }, 83 | // Active 84 | active: { 85 | backgroundColor: '#fff', 86 | bottom: 0, 87 | height: 2, 88 | left: 0, 89 | position: 'absolute', 90 | right: 0 91 | }, 92 | // Background 93 | game: { 94 | backgroundColor: '#3471AE' 95 | }, 96 | players: { 97 | backgroundColor: '#BD4C29' 98 | }, 99 | teams: { 100 | backgroundColor: '#1C8929' 101 | }, 102 | roster: {} 103 | }) 104 | 105 | Tabbar.propTypes = { 106 | actions: PropTypes.object, 107 | tab: PropTypes.string 108 | } 109 | 110 | -------------------------------------------------------------------------------- /app/components/team/TeamConference.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * @note put ListView into scrollView cause warning 5 | * @see https://github.com/facebook/react-native/issues/1501 6 | * To remove the warning temporily, 7 | * comment the code in /node_modules/react-native/Libraries/Components/ScrollReponder.js line 204 8 | */ 9 | import React, { 10 | Component, 11 | PropTypes, 12 | View, 13 | Text, 14 | Image, 15 | StyleSheet, 16 | TouchableHighlight, 17 | ListView, 18 | Dimensions 19 | } from 'react-native' 20 | 21 | import teamMap from '../../utils/team-map' 22 | import TeamDetail from './TeamDetail' 23 | 24 | /** 25 | * Give listView a specific height, or it will affected by parental ScrollView which has set pagingEnable=true 26 | * @see https://github.com/facebook/react-native/issues/3673 27 | */ 28 | const listHeight = Dimensions.get('window').height - 100 - 45 29 | 30 | export default class TeamConference extends Component { 31 | 32 | constructor (props) { 33 | super(props) 34 | /* Switch team list to id based */ 35 | let teamMapById = {} 36 | for (let key in teamMap) { 37 | teamMapById[teamMap[key].id] = Object.assign({}, teamMap[key], {abbr: key}) 38 | } 39 | this.teamMapById = teamMapById 40 | } 41 | 42 | selectTeam (id) { 43 | const {navigator} = this.props 44 | navigator.push(Object.assign({}, { 45 | name: 'TeamDetail', 46 | component: TeamDetail, 47 | id 48 | })) 49 | } 50 | 51 | renderRow (item, _, index) { 52 | const team = Object.assign({}, this.teamMapById[item.id], item) 53 | const itemStyle = index % 2 === 0 ? styles.item : [styles.item, styles.itemEven] 54 | 55 | const teamLogo = team.abbr === 'hou' ? teamMap[team.abbr].logo2 : teamMap[team.abbr].logo 56 | 57 | return ( 58 | 59 | 60 | 61 | {parseInt(index, 10) + 1} 62 | 63 | 64 | {team.city} 65 | {team.team} 66 | 67 | 68 | {team.loss + ' - ' + team.win} 69 | 70 | 71 | 72 | 73 | 74 | 75 | ) 76 | } 77 | 78 | render () { 79 | const {data} = this.props 80 | const dataSource = new ListView.DataSource({ 81 | rowHasChanged: (row1, row2) => row1 !== row2 82 | }).cloneWithRows(data) 83 | 84 | return ( 85 | 90 | ) 91 | } 92 | } 93 | 94 | const styles = StyleSheet.create({ 95 | // List 96 | listView: { 97 | height: listHeight 98 | }, 99 | // item 100 | item: { 101 | height: 70, 102 | flex: 1, 103 | flexDirection: 'row' 104 | }, 105 | itemEven: { 106 | backgroundColor: '#F4F4F4' 107 | }, 108 | // order 109 | order: { 110 | alignSelf: 'center', 111 | width: 50 112 | }, 113 | orderLabel: { 114 | color: '#6B7C96', 115 | fontSize: 11, 116 | fontWeight: 'bold', 117 | textAlign: 'center' 118 | }, 119 | // team 120 | team: { 121 | flex: 5, 122 | justifyContent: 'center' 123 | }, 124 | teamCity: { 125 | color: '#6B7C96', 126 | fontSize: 18, 127 | fontWeight: '200' 128 | }, 129 | teamName: { 130 | color: '#909CAF', 131 | fontSize: 13 132 | }, 133 | // Standing 134 | standing: { 135 | alignSelf: 'center', 136 | flex: 3 137 | }, 138 | standingLabel: { 139 | color: '#6B7C96', 140 | textAlign: 'right' 141 | }, 142 | // Logo 143 | logo: { 144 | alignSelf: 'center', 145 | flex: 3 146 | }, 147 | logoImage: { 148 | alignSelf: 'center', 149 | height: 35, 150 | width: 35 151 | } 152 | }) 153 | 154 | TeamConference.propTypes = { 155 | data: PropTypes.array, 156 | navigator: PropTypes.object 157 | } 158 | -------------------------------------------------------------------------------- /app/components/team/TeamDetail.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | View, 7 | Image, 8 | Text, 9 | ListView, 10 | TouchableHighlight, 11 | PropTypes, 12 | Dimensions, 13 | InteractionManager 14 | } from 'react-native' 15 | 16 | import {Icon} from 'react-native-icons' 17 | import teamInfo from '../../utils/team-map' 18 | import TeamDetailPlayer from './TeamDetailPlayer' 19 | 20 | const listHeight = Dimensions.get('window').height - 30 - 90 - 35 21 | 22 | export default class TeamDetail extends Component { 23 | 24 | constructor (props) { 25 | super(props) 26 | const {id} = this.props.route 27 | const {team} = this.props 28 | let dataSource = new ListView.DataSource({ 29 | rowHasChanged: (row1, row2) => row1 !== row2 30 | }) 31 | if (team.detail[id] && team.detail[id].players) { 32 | dataSource = dataSource.cloneWithRows(team.detail[id].players) 33 | } 34 | 35 | this.state = { 36 | team: team.detail[id], 37 | dataSource 38 | } 39 | } 40 | 41 | componentDidMount () { 42 | const {actions} = this.props 43 | const {id} = this.props.route 44 | 45 | InteractionManager.runAfterInteractions(() => { 46 | actions.getTeamInfo(id) 47 | .then(() => { 48 | actions.getTeamDetail(id) 49 | }) 50 | }) 51 | } 52 | 53 | componentWillReceiveProps (props) { 54 | const {team} = props 55 | const {id} = this.props.route 56 | const {dataSource} = this.state 57 | 58 | let newState = {} 59 | if (team.detail[id].players) { 60 | newState = { 61 | team: team.detail[id], 62 | dataSource: dataSource.cloneWithRows(team.detail[id].players) 63 | } 64 | } else { 65 | newState = { 66 | team: team.detail[id] 67 | } 68 | } 69 | 70 | this.setState(newState) 71 | } 72 | 73 | onPressBack () { 74 | const {navigator} = this.props 75 | navigator.pop() 76 | } 77 | 78 | renderRow (player, _, index) { 79 | const {team, navigator} = this.props 80 | const {id} = this.props.route 81 | const isLast = index === team.detail[id].players.length - 1 82 | return 83 | } 84 | 85 | render () { 86 | const {team, dataSource} = this.state 87 | return ( 88 | 89 | {team && 90 | 91 | 92 | 95 | 100 | 101 | 102 | 103 | 104 | 105 | {team.teamCity} 106 | {team.teamName} 107 | 108 | 109 | 110 | 111 | 112 | {team.win + 'W - ' + team.loss + 'L' } 113 | {'#' + team.confRank + ' in the ' + team.teamConf + ' Conference'} 114 | {'#' + team.diviRank + ' in the ' + team.teamDivi + ' Division'} 115 | 116 | 117 | 118 | 119 | 120 | PPG 121 | {team.ptsRank + 'th'} 122 | 123 | 124 | RPG 125 | {team.rebRank + 'th'} 126 | 127 | 128 | APG 129 | {team.astRank + 'th'} 130 | 131 | 132 | OPPG 133 | {team.oppRank + 'th'} 134 | 135 | 136 | 137 | {team.players && 138 | 142 | } 143 | 144 | } 145 | 146 | ) 147 | } 148 | } 149 | 150 | const styles = StyleSheet.create({ 151 | container: { 152 | flexDirection: 'column' 153 | }, 154 | // Navigation 155 | navigation: { 156 | flexDirection: 'row', 157 | height: 30 158 | }, 159 | backIcon: { 160 | height: 30, 161 | marginLeft: 2, 162 | marginTop: 6, 163 | width: 30 164 | }, 165 | // Header part 166 | header: { 167 | height: 90, 168 | flexDirection: 'row', 169 | paddingHorizontal: 10, 170 | paddingBottom: 15 171 | }, 172 | // Heade team 173 | headerTeam: { 174 | flex: 1.5, 175 | flexDirection: 'column', 176 | justifyContent: 'center' 177 | }, 178 | headerTeamCity: { 179 | color: '#fff', 180 | fontSize: 17 181 | }, 182 | headerTeamName: { 183 | color: '#fff', 184 | fontSize: 13 185 | }, 186 | // Header logo 187 | headerLogo: { 188 | flex: 1, 189 | justifyContent: 'center' 190 | }, 191 | headerLogoImage: { 192 | alignSelf: 'flex-start', 193 | height: 70, 194 | width: 70 195 | }, 196 | // Header rank 197 | headerRank: { 198 | flex: 1.5, 199 | justifyContent: 'center' 200 | }, 201 | headerRankResult: { 202 | color: '#fff', 203 | fontSize: 10 204 | }, 205 | headerRankConf: { 206 | color: '#fff', 207 | fontSize: 10 208 | }, 209 | headerRankDivi: { 210 | color: '#fff', 211 | fontSize: 10 212 | }, 213 | // Data info 214 | dataInfo: { 215 | alignItems: 'center', 216 | flexDirection: 'row', 217 | height: 35 218 | }, 219 | dataInfoItem: { 220 | alignItems: 'flex-end', 221 | flex: 1, 222 | flexDirection: 'row', 223 | justifyContent: 'center' 224 | }, 225 | itemLabel: { 226 | color: '#6B7C96', 227 | fontSize: 10 228 | }, 229 | itemData: { 230 | color: '#6B7C96', 231 | fontSize: 15, 232 | fontWeight: '600', 233 | marginLeft: 2, 234 | position: 'relative', 235 | top: 2 236 | }, 237 | // list view, 238 | listView: { 239 | backgroundColor: '#F4F4F4', 240 | height: listHeight 241 | } 242 | }) 243 | 244 | TeamDetail.propTypes = { 245 | route: PropTypes.object, 246 | team: PropTypes.object, 247 | actions: PropTypes.object, 248 | navigator: PropTypes.object 249 | } 250 | -------------------------------------------------------------------------------- /app/components/team/TeamDetailPlayer.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | View, 7 | Image, 8 | Text, 9 | TouchableHighlight, 10 | PropTypes 11 | } from 'react-native' 12 | 13 | import teamInfo from '../../utils/team-map' 14 | import PlayerDetail from '../player/PlayerDetail' 15 | 16 | export default class TeamDetailPlayer extends Component { 17 | 18 | onPress () { 19 | const {navigator, player} = this.props 20 | navigator.push({ 21 | name: 'PlayerDetail', 22 | component: PlayerDetail, 23 | player 24 | }) 25 | } 26 | 27 | render () { 28 | const {player, team} = this.props 29 | const teamMap = teamInfo[team.teamAbbr.toLowerCase()] 30 | 31 | return ( 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {player.name} 45 | {player.pos + ' ' + player.num} 46 | 47 | 48 | 49 | 50 | {'Age: ' + player.age} 51 | {'Height: ' + player.height} 52 | {'Weight: ' + player.weight} 53 | 54 | 55 | 56 | {player.pts} 57 | PTS 58 | 59 | 60 | {player.reb} 61 | REB 62 | 63 | 64 | {player.ast} 65 | AST 66 | 67 | 68 | {player.min} 69 | MIN 70 | 71 | 72 | {player.gp} 73 | GP 74 | 75 | 76 | 77 | 78 | 79 | 80 | ) 81 | } 82 | } 83 | 84 | const styles = StyleSheet.create({ 85 | container: { 86 | borderColor: '#E1E1E1', 87 | borderBottomWidth: 1, 88 | height: 60, 89 | flexDirection: 'row' 90 | }, 91 | // Portrait 92 | portrait: { 93 | flex: 1, 94 | justifyContent: 'center' 95 | }, 96 | portraitBackground: { 97 | alignSelf: 'center', 98 | backgroundColor: '#fff', 99 | borderRadius: 18, 100 | height: 36, 101 | width: 36 102 | }, 103 | portraitImage: { 104 | height: 36, 105 | width: 36, 106 | borderRadius: 18, 107 | borderWidth: 1 108 | }, 109 | // Info 110 | info: { 111 | flex: 2, 112 | justifyContent: 'center' 113 | }, 114 | infoName: { 115 | color: '#7C7C7C', 116 | fontSize: 12 117 | }, 118 | infoPosition: { 119 | color: '#7C7C7C', 120 | fontSize: 12 121 | }, 122 | // Data 123 | data: { 124 | flex: 5, 125 | justifyContent: 'center' 126 | }, 127 | // Data persion 128 | dataPerson: { 129 | flexDirection: 'row' 130 | }, 131 | dataPersonItem: { 132 | color: '#909CAF', 133 | fontSize: 10, 134 | marginRight: 10 135 | }, 136 | // Data game 137 | dataGame: { 138 | flexDirection: 'row' 139 | }, 140 | dataGameItem: { 141 | alignItems: 'flex-end', 142 | flexDirection: 'row', 143 | marginRight: 10 144 | }, 145 | dataGameItemData: { 146 | color: '#6B7C96', 147 | fontSize: 12, 148 | position: 'relative', 149 | top: 1 150 | }, 151 | dataGameItemLabel: { 152 | color: '#6B7C96', 153 | fontSize: 9 154 | } 155 | }) 156 | 157 | TeamDetailPlayer.propTypes = { 158 | player: PropTypes.object, 159 | team: PropTypes.object, 160 | navigator: PropTypes.object 161 | } 162 | -------------------------------------------------------------------------------- /app/components/team/TeamList.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | PropTypes, 6 | View, 7 | Text, 8 | StyleSheet 9 | } from 'react-native' 10 | 11 | import Tabbar from '../share/Tabbar' 12 | import moment from 'moment-timezone' 13 | import TeamConference from './TeamConference' 14 | import CollectionView from '../../lib/collection' 15 | 16 | export default class TeamList extends Component { 17 | 18 | constructor (props) { 19 | super(props) 20 | this.state = { 21 | conference: 'western' 22 | } 23 | } 24 | 25 | componentDidMount () { 26 | const {actions} = this.props 27 | const dateString = moment.tz(Date.now(), 'America/Los_Angeles').format() 28 | const dateArray = dateString.replace('T', '-').split('-') 29 | actions.getTeamRank(dateArray[0], dateArray[1], dateArray[2]) 30 | } 31 | 32 | scrollEnd (x, y) { 33 | if (x === 0) { 34 | this.setState({ 35 | conference: 'western' 36 | }) 37 | } else { 38 | this.setState({ 39 | conference: 'eastern' 40 | }) 41 | } 42 | } 43 | 44 | render () { 45 | const {conference} = this.state 46 | const {team} = this.props 47 | 48 | return ( 49 | 50 | 51 | {conference.toUpperCase()} 52 | conference 53 | 54 | 55 | {team.loaded && 56 | 57 | {[, ]} 58 | 59 | } 60 | 61 | ) 62 | } 63 | } 64 | 65 | const styles = StyleSheet.create({ 66 | container: { 67 | flex: 1 68 | }, 69 | // Header 70 | header: { 71 | backgroundColor: '#2DB43D', 72 | height: 100, 73 | justifyContent: 'center', 74 | paddingHorizontal: 20 75 | }, 76 | conference: { 77 | color: '#fff', 78 | fontSize: 25, 79 | fontWeight: '300' 80 | }, 81 | confLabel: { 82 | color: '#fff', 83 | fontSize: 14, 84 | fontWeight: '200' 85 | } 86 | }) 87 | 88 | TeamList.propTypes = { 89 | actions: PropTypes.object, 90 | team: PropTypes.object 91 | } 92 | -------------------------------------------------------------------------------- /app/constant.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* Android env dosen't support Symbol, no idea about the reason */ 4 | // import Enum from 'es6-enum' 5 | 6 | export const APP = { 7 | TAB: 'APP.TAB', 8 | 'NAVIGATION': 'APP.NAVIGATION' 9 | } 10 | // Enum('TAB', 'NAVIGATION') 11 | 12 | export const GAME = { 13 | INFO: 'GAME.INFO', 14 | DETAIL: 'GAME.DETAIL', 15 | STANDING: 'GAME.STANDING' 16 | } 17 | // Enum('INFO', 'DETAIL', 'STANDING') 18 | 19 | export const PLAYER = { 20 | LIST: 'PLAYER.LIST', 21 | DETAIL: 'PLAYER.DETAIL', 22 | LOG: 'PLAYER.LOG', 23 | RECENT: 'PLAYER.RECENT' 24 | } 25 | // Enum('LIST', 'DETAIL', 'LOG', 'RECENT') 26 | 27 | export const TEAM = { 28 | RANK: 'TEAM.RANK', 29 | INFO: 'TEAM.INFO', 30 | DETAIL: 'TEAM.DETAIL' 31 | } 32 | // Enum('RANK', 'INFO', 'DETAIL') 33 | -------------------------------------------------------------------------------- /app/containers/App.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | View, 6 | StyleSheet, 7 | PropTypes 8 | } from 'react-native' 9 | import {connect} from 'react-redux/native' 10 | import {bindActionCreators} from 'redux' 11 | 12 | import applicationActions from '../actions/application' 13 | import gameActions from '../actions/game' 14 | import playerActions from '../actions/player' 15 | import teamActions from '../actions/team' 16 | 17 | import Game from './Game' 18 | import Player from './Player' 19 | import Team from './Team' 20 | 21 | export default class App extends Component { 22 | 23 | constructor (props) { 24 | super(props) 25 | this.state = { 26 | tab: null 27 | } 28 | } 29 | 30 | componentWillReceiveProps (props) { 31 | const {application} = props 32 | this.setState({ 33 | tab: application.tab 34 | }) 35 | } 36 | 37 | render () { 38 | const {tab} = this.state 39 | const {game, player, team, gameActions, playerActions, teamActions} = this.props 40 | 41 | return ( 42 | 43 | {tab === 'game' && 44 | 45 | } 46 | {tab === 'players' && 47 | 48 | } 49 | {tab === 'teams' && 50 | 51 | } 52 | 53 | ) 54 | } 55 | } 56 | 57 | const styles = StyleSheet.create({ 58 | container: { 59 | flex: 1 60 | } 61 | }) 62 | 63 | App.propTypes = { 64 | game: PropTypes.object, 65 | player: PropTypes.object, 66 | team: PropTypes.object, 67 | gameActions: PropTypes.object, 68 | playerActions: PropTypes.object, 69 | teamActions: PropTypes.object 70 | } 71 | 72 | export default connect(state => { 73 | return { 74 | application: state.application, 75 | game: { 76 | live: state.live, 77 | over: state.over, 78 | unstart: state.unstart, 79 | standing: state.standing, 80 | application: state.application 81 | }, 82 | player: { 83 | playerList: state.playerList, 84 | playerLoaded: state.playerLoaded 85 | }, 86 | team: { 87 | team: state.team, 88 | playerLoaded: state.playerLoaded 89 | } 90 | } 91 | }, dispatch => { 92 | return { 93 | gameActions: bindActionCreators(Object.assign({}, applicationActions, gameActions), dispatch), 94 | playerActions: bindActionCreators(Object.assign({}, applicationActions, playerActions), dispatch), 95 | teamActions: bindActionCreators(Object.assign({}, applicationActions, playerActions, teamActions), dispatch) 96 | } 97 | })(App) 98 | -------------------------------------------------------------------------------- /app/containers/Game.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | PropTypes, 5 | Component, 6 | Navigator 7 | } from 'react-native' 8 | 9 | import GameList from '../components/game/GameList' 10 | import NavigatorBar from '../components/share/NavigatorBar' 11 | 12 | export default class Game extends Component { 13 | 14 | componentDidMount () { 15 | const {actions} = this.props 16 | actions.getLeagueStanding() 17 | } 18 | 19 | renderScene (route, navigator) { 20 | if (route.component) { 21 | const Component = route.component 22 | return 23 | } 24 | } 25 | 26 | render () { 27 | return ( 28 | } 34 | configureScene={() => ({ 35 | ...Navigator.SceneConfigs.FloatFromRight 36 | })} 37 | renderScene={this.renderScene.bind(this)} 38 | /> 39 | ) 40 | } 41 | } 42 | 43 | Game.propTypes = { 44 | game: PropTypes.object, 45 | actions: PropTypes.object 46 | } 47 | -------------------------------------------------------------------------------- /app/containers/Player.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | Navigator 6 | } from 'react-native' 7 | 8 | import PlayerIndex from '../components/player/PlayerIndex' 9 | import NavigatorBar from '../components/share/NavigatorBar' 10 | 11 | export default class Player extends Component { 12 | 13 | renderScene (route, navigator) { 14 | if (route.component) { 15 | const Component = route.component 16 | return 17 | } 18 | } 19 | 20 | render () { 21 | return ( 22 | ({ 28 | ...Navigator.SceneConfigs.FloatFromRight 29 | })} 30 | navigationBar={} 31 | renderScene={this.renderScene.bind(this)} 32 | /> 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/containers/Team.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | Navigator 6 | } from 'react-native' 7 | 8 | import TeamList from '../components/team/TeamList' 9 | import NavigatorBar from '../components/share/NavigatorBar' 10 | 11 | export default class Team extends Component { 12 | 13 | renderScene (route, navigator) { 14 | if (route.component) { 15 | const Component = route.component 16 | return 17 | } 18 | } 19 | 20 | render () { 21 | return ( 22 | ({ 28 | ...Navigator.SceneConfigs.FloatFromRight 29 | })} 30 | navigationBar={} 31 | renderScene={this.renderScene.bind(this)} 32 | /> 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/lib/collection/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | ScrollView, 6 | StyleSheet, 7 | Dimensions, 8 | PropTypes, 9 | View 10 | } from 'react-native' 11 | 12 | export default class Collection extends Component { 13 | 14 | constructor (props) { 15 | super(props) 16 | this.screenWidth = Dimensions.get('window').width 17 | this.screenHeight = Dimensions.get('window').height - 40 18 | } 19 | 20 | onMomentumScrollEnd (e) { 21 | this.props.scrollEnd && this.props.scrollEnd(e.nativeEvent.contentOffset.x, e.nativeEvent.contentOffset.y) 22 | } 23 | 24 | render () { 25 | const {screenWidth} = this 26 | 27 | return ( 28 | 40 | {this.props.children.map((child, index) => { 41 | return ( 44 | {child} 45 | 46 | ) 47 | })} 48 | 49 | ) 50 | } 51 | } 52 | 53 | const styles = StyleSheet.create({ 54 | container: { 55 | flexDirection: 'row' 56 | } 57 | }) 58 | 59 | Collection.propTypes = { 60 | scrollEnd: PropTypes.func, 61 | children: PropTypes.array 62 | } 63 | 64 | -------------------------------------------------------------------------------- /app/lib/userDefaults/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ABORT: Customer component to use ios UserDeafults 3 | * @see https://facebook.github.io/react-native/docs/native-modules-ios.html#exporting-swift 4 | * 5 | * In order to support both platforms, a better choice is to use AsyncStore 6 | */ 7 | 8 | 'use strict' 9 | 10 | import {AsyncStorage} from 'react-native' 11 | 12 | /* Only store string to store, easy for management */ 13 | const userDefaults = { 14 | set: (key, value) => { 15 | const jsonValue = JSON.stringify(value) 16 | return AsyncStorage.setItem(key, jsonValue) 17 | }, 18 | 19 | get: (key) => { 20 | return AsyncStorage.getItem(key) 21 | .then(data => { 22 | if (data) return JSON.parse(data) 23 | return null 24 | }) 25 | } 26 | } 27 | 28 | export default userDefaults 29 | -------------------------------------------------------------------------------- /app/middleware/logger.js: -------------------------------------------------------------------------------- 1 | export default function logger (store) { 2 | return next => action => { 3 | /* You have to comment these or you will get error when running in real device */ 4 | // console.group() 5 | // console.log('will dispath', action) 6 | const result = next(action) 7 | // console.log('state after dispatch', store.getState()) 8 | // console.groupEnd() 9 | return result 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/reducers/application.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import createReducer from '../utils/create-reducer' 4 | 5 | import {APP} from '../constant' 6 | 7 | /* First navigatore of each tab named [tab]Index */ 8 | const initialState = { 9 | tab: 'game', 10 | navigator: 'gameIndex' 11 | } 12 | 13 | const actionHandler = { 14 | [APP.TAB]: (state, action) => { 15 | return Object.assign({}, state, { 16 | tab: action.data, 17 | navigator: action.data + 'Index' 18 | }) 19 | }, 20 | 21 | [APP.NAVIGATION]: (state, action) => { 22 | return Object.assign({}, state, { 23 | navigator: action.data 24 | }) 25 | } 26 | } 27 | 28 | export default createReducer(initialState, actionHandler) 29 | -------------------------------------------------------------------------------- /app/reducers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { combineReducers } from 'redux' 4 | 5 | import live from './live' 6 | import over from './over' 7 | import unstart from './unstart' 8 | import standing from './standing' 9 | import application from './application' 10 | import playerList from './playerList' 11 | import playerLoaded from './playerLoaded' 12 | import team from './team' 13 | 14 | const reducers = combineReducers({ 15 | unstart, 16 | live, 17 | over, 18 | standing, 19 | application, 20 | playerList, 21 | playerLoaded, 22 | team 23 | }) 24 | 25 | export default reducers 26 | -------------------------------------------------------------------------------- /app/reducers/live.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import createReducer from '../utils/create-reducer' 4 | 5 | import {GAME} from '../constant' 6 | 7 | const initialState = { 8 | data: [ 9 | /* 10 | { 11 | id: {Number} 12 | type: {String} 13 | home: { 14 | team: {String} 15 | score: {String} 16 | }, 17 | visitor: { 18 | team: {String} 19 | score: {String} 20 | }, 21 | detail: { 22 | url: {String} 23 | data: {Object} 24 | }, 25 | process: { 26 | time: {String} 27 | quarter: {String} 28 | } 29 | } 30 | */ 31 | ] 32 | } 33 | 34 | const actionHandler = { 35 | [GAME.INFO]: (state, action) => { 36 | return Object.assign({}, state, { 37 | gameDate: action.data.gameDate, 38 | data: action.data.live 39 | }) 40 | }, 41 | 42 | [GAME.DETAIL]: (state, action) => { 43 | if (action.gameType !== 'live') return state 44 | let newState = Object.assign([], state) 45 | state.data.some(game => { 46 | if (game.id === action.gameId) { 47 | game.detail = game.detail || {} 48 | game.detail.data = action.data 49 | game.detail.loaded = true 50 | game.process = action.data.process 51 | game.home.score = action.data.home.score 52 | game.visitor.score = action.data.visitor.score 53 | return true 54 | } 55 | return false 56 | }) 57 | return newState 58 | } 59 | } 60 | 61 | export default createReducer(initialState, actionHandler) 62 | -------------------------------------------------------------------------------- /app/reducers/over.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import createReducer from '../utils/create-reducer' 4 | 5 | import {GAME} from '../constant' 6 | 7 | const initialState = { 8 | data: [ 9 | /* 10 | { 11 | id: {Number} 12 | type: {String} 13 | home: { 14 | team: {String} 15 | score: {String} 16 | }, 17 | visitor: { 18 | team: {String} 19 | score: {String} 20 | }, 21 | detail: { 22 | url: {String} 23 | loaded: {Bool} 24 | data: {Object} 25 | } 26 | } 27 | */ 28 | ] 29 | } 30 | 31 | const actionHandler = { 32 | [GAME.INFO]: (state, action) => { 33 | return Object.assign({}, state, { 34 | gameDate: action.data.gameDate, 35 | data: action.data.over 36 | }) 37 | }, 38 | 39 | [GAME.DETAIL]: (state, action) => { 40 | if (action.gameType !== 'over') return state 41 | let newState = Object.assign([], state) 42 | state.data.some(game => { 43 | if (game.id === action.gameId) { 44 | game.detail = game.detail || {} 45 | game.detail.loaded = true 46 | game.detail.data = action.data 47 | return true 48 | } 49 | return false 50 | }) 51 | 52 | return newState 53 | } 54 | } 55 | 56 | export default createReducer(initialState, actionHandler) 57 | -------------------------------------------------------------------------------- /app/reducers/playerList.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import createReducer from '../utils/create-reducer' 4 | 5 | import {PLAYER} from '../constant' 6 | 7 | const initialState = { 8 | isLoaded: false, 9 | recent: [], 10 | data: [] 11 | } 12 | 13 | const actionHandler = { 14 | [PLAYER.LIST]: (state, action) => { 15 | return { 16 | isLoaded: true, 17 | data: action.data 18 | } 19 | }, 20 | 21 | [PLAYER.RECENT]: (state, action) => { 22 | return { 23 | recent: action.data 24 | } 25 | } 26 | } 27 | 28 | export default createReducer(initialState, actionHandler) 29 | -------------------------------------------------------------------------------- /app/reducers/playerLoaded.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import createReducer from '../utils/create-reducer' 4 | 5 | import {PLAYER} from '../constant' 6 | 7 | const initialState = { 8 | // id: {isLogLoaded, log, ...} 9 | } 10 | 11 | const actionHandler = { 12 | [PLAYER.DETAIL]: (state, action) => { 13 | const data = state[action.id] ? Object.assign(state[action.id], action.data) : action.data 14 | return { 15 | [action.id]: data 16 | } 17 | }, 18 | 19 | [PLAYER.LOG]: (state, action) => { 20 | const actionData = { 21 | log: action.data 22 | } 23 | const data = state[action.id] ? Object.assign(state[action.id], actionData) : actionData 24 | return { 25 | [action.id]: data 26 | } 27 | } 28 | } 29 | 30 | export default createReducer(initialState, actionHandler) 31 | -------------------------------------------------------------------------------- /app/reducers/standing.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Record each team's standing 3 | */ 4 | 5 | 'use strict' 6 | 7 | import createReducer from '../utils/create-reducer' 8 | 9 | import {GAME} from '../constant' 10 | 11 | const initialState = { 12 | loaded: false, 13 | data: { 14 | // teamId: { 15 | // abbr: {String} 16 | // state: {Object} 17 | // } 18 | } 19 | } 20 | 21 | const actionHandler = { 22 | [GAME.STANDING]: (state, action) => { 23 | return { 24 | loaded: true, 25 | data: action.data 26 | } 27 | } 28 | } 29 | 30 | export default createReducer(initialState, actionHandler) 31 | -------------------------------------------------------------------------------- /app/reducers/team.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Team rank 3 | */ 4 | 5 | 'use strict' 6 | 7 | import createReducer from '../utils/create-reducer' 8 | import {TEAM} from '../constant' 9 | 10 | const initialState = { 11 | loaded: false, 12 | data: { 13 | eastern: [ 14 | // {id, abbre, standing} 15 | ], 16 | western: [] 17 | }, 18 | detail: {} 19 | } 20 | 21 | const actionHandler = { 22 | [TEAM.RANK]: (state, action) => { 23 | return { 24 | loaded: true, 25 | data: action.data 26 | } 27 | }, 28 | 29 | [TEAM.INFO]: (state, action) => { 30 | let detail = state.detail 31 | detail[action.id] = action.data 32 | return { 33 | detail 34 | } 35 | }, 36 | 37 | [TEAM.DETAIL]: (state, action) => { 38 | let detail = state.detail 39 | detail[action.id] = detail[action.id] || {} 40 | detail[action.id].players = action.data 41 | return { 42 | detail 43 | } 44 | } 45 | } 46 | 47 | export default createReducer(initialState, actionHandler) 48 | -------------------------------------------------------------------------------- /app/reducers/unstart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import createReducer from '../utils/create-reducer' 4 | 5 | import {GAME} from '../constant' 6 | 7 | const initialState = { 8 | data: [ 9 | /* 10 | { 11 | id: {Number} 12 | type: {String} 13 | home: { 14 | team: {String} 15 | }, 16 | visitor: { 17 | team: {String} 18 | }, 19 | time: 'String' 20 | } 21 | */ 22 | ] 23 | } 24 | 25 | const actionHandler = { 26 | [GAME.INFO]: (state, action) => { 27 | return Object.assign({}, state, { 28 | gameDate: action.data.gameDate, 29 | data: action.data.unstart 30 | }) 31 | } 32 | } 33 | 34 | export default createReducer(initialState, actionHandler) 35 | -------------------------------------------------------------------------------- /app/root.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import React, { 4 | Component, 5 | StatusBarIOS, 6 | Platform 7 | } from 'react-native' 8 | import { createStore, applyMiddleware } from 'redux' 9 | import { Provider } from 'react-redux/native' 10 | import reducers from './reducers' 11 | 12 | import logger from './middleware/logger' 13 | import thunk from 'redux-thunk' 14 | 15 | import App from './containers/App' 16 | 17 | const createStoreWithMW = applyMiddleware(logger, thunk)(createStore) 18 | const store = createStoreWithMW(reducers) 19 | 20 | export default class Root extends Component { 21 | 22 | componentDidMount () { 23 | if (Platform.OS === 'ios') { 24 | StatusBarIOS.setHidden(true) 25 | } 26 | } 27 | 28 | render () { 29 | return ( 30 | 31 | {() => } 32 | 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/utils/create-reducer.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default function createReducer (initialState, actionHandlers) { 4 | return (state = initialState, action) => { 5 | const reduceFn = actionHandlers[action.type] 6 | if (!reduceFn) return state 7 | // Looks it works like Object.assign 8 | return { ...state, ...reduceFn(state, action) } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/utils/team-map.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | export default { 4 | atl: { 5 | id: 1610612737, 6 | city: 'Atlanta', 7 | team: 'Hawks', 8 | color: '#E03A3E', 9 | conf: 'eastern', 10 | logo: require('../../assets/img/atl.png') 11 | }, 12 | bkn: { 13 | id: 1610612751, 14 | city: 'Brooklyn', 15 | team: 'Nets', 16 | color: '#000', 17 | conf: 'eastern', 18 | logo: require('../../assets/img/bkn.png') 19 | }, 20 | bos: { 21 | id: 1610612738, 22 | city: 'Boston', 23 | team: 'Celtics', 24 | color: '#008348', 25 | conf: 'eastern', 26 | logo: require('../../assets/img/bos.png') 27 | }, 28 | cha: { 29 | id: 1610612766, 30 | city: 'Charlotte', 31 | team: 'Hornets', 32 | color: '#008CA8', 33 | conf: 'eastern', 34 | logo: require('../../assets/img/cha.png') 35 | }, 36 | chi: { 37 | id: 1610612741, 38 | city: 'Chicago', 39 | team: 'Bulls', 40 | color: '#CE1141', 41 | conf: 'eastern', 42 | logo: require('../../assets/img/chi.png') 43 | }, 44 | cle: { 45 | id: 1610612739, 46 | city: 'Cleveland', 47 | team: 'Cavaliers', 48 | color: '#860038', 49 | conf: 'eastern', 50 | logo: require('../../assets/img/cle.png') 51 | }, 52 | dal: { 53 | id: 1610612742, 54 | city: 'Dallas', 55 | team: 'Mavericks', 56 | color: '#007DC5', 57 | conf: 'western', 58 | logo: require('../../assets/img/dal.png') 59 | }, 60 | den: { 61 | id: 1610612743, 62 | city: 'Denver', 63 | team: 'Nuggets', 64 | color: '#FFB20F', 65 | conf: 'western', 66 | logo: require('../../assets/img/den.png') 67 | }, 68 | det: { 69 | id: 1610612765, 70 | city: 'Detroit', 71 | team: 'Pistons', 72 | color: '#006BB6', 73 | conf: 'eastern', 74 | logo: require('../../assets/img/det.png') 75 | }, 76 | gsw: { 77 | id: 1610612744, 78 | city: 'Golden State', 79 | team: 'Warriors', 80 | color: '#FDB927', 81 | conf: 'western', 82 | logo: require('../../assets/img/gsw.png') 83 | }, 84 | hou: { 85 | id: 1610612745, 86 | city: 'Houston', 87 | team: 'Rockets', 88 | color: '#CE1141', 89 | conf: 'western', 90 | logo: require('../../assets/img/hou.png'), 91 | logo2: require('../../assets/img/hou2.png') 92 | }, 93 | ind: { 94 | id: 1610612754, 95 | city: 'Indiana', 96 | team: 'Pacers', 97 | color: '#FFC633', 98 | conf: 'eastern', 99 | logo: require('../../assets/img/ind.png') 100 | }, 101 | lac: { 102 | id: 1610612746, 103 | city: 'Los Angeles', 104 | team: 'Clippers', 105 | color: '#222', 106 | conf: 'western', 107 | logo: require('../../assets/img/lac.png') 108 | }, 109 | lal: { 110 | id: 1610612747, 111 | city: 'Los Angeles', 112 | team: 'Lakers', 113 | color: '#552582', 114 | conf: 'western', 115 | logo: require('../../assets/img/lal.png') 116 | }, 117 | mem: { 118 | id: 1610612763, 119 | city: 'Memphis', 120 | team: 'Grizzlies', 121 | color: '#6189B9', 122 | conf: 'western', 123 | logo: require('../../assets/img/mem.png') 124 | }, 125 | mia: { 126 | id: 1610612748, 127 | city: 'Miami', 128 | team: 'Heat', 129 | color: '#98002E', 130 | conf: 'eastern', 131 | logo: require('../../assets/img/mia.png') 132 | }, 133 | mil: { 134 | id: 1610612749, 135 | city: 'Milwaukee', 136 | team: 'Bucks', 137 | color: '#00471B', 138 | conf: 'eastern', 139 | logo: require('../../assets/img/mil.png') 140 | }, 141 | min: { 142 | id: 1610612750, 143 | city: 'Minnesota', 144 | team: 'Timberwolves', 145 | color: '#005083', 146 | conf: 'western', 147 | logo: require('../../assets/img/min.png') 148 | }, 149 | nop: { 150 | id: 1610612740, 151 | city: 'New Orleans', 152 | team: 'Pelicans', 153 | color: '#002B5C', 154 | conf: 'western', 155 | logo: require('../../assets/img/nop.png') 156 | }, 157 | nyk: { 158 | id: 1610612752, 159 | city: 'New York', 160 | team: 'Knicks', 161 | color: '#F58426', 162 | conf: 'eastern', 163 | logo: require('../../assets/img/nyk.png') 164 | }, 165 | okc: { 166 | id: 1610612760, 167 | city: 'Oklahoma City', 168 | team: 'Thunder', 169 | color: '#F05133', 170 | conf: 'western', 171 | logo: require('../../assets/img/okc.png') 172 | }, 173 | orl: { 174 | id: 1610612753, 175 | city: 'Orlando', 176 | team: 'Magic', 177 | color: '#007DC5', 178 | conf: 'eastern', 179 | logo: require('../../assets/img/orl.png') 180 | }, 181 | phi: { 182 | id: 1610612755, 183 | city: 'Philadelphia', 184 | team: '76ers', 185 | color: '#006BB6', 186 | conf: 'eastern', 187 | logo: require('../../assets/img/phi.png') 188 | }, 189 | phx: { 190 | id: 1610612756, 191 | city: 'Phoenix', 192 | team: 'Suns', 193 | color: '#E56020', 194 | conf: 'western', 195 | logo: require('../../assets/img/phx.png') 196 | }, 197 | por: { 198 | id: 1610612757, 199 | city: 'Portland', 200 | team: 'Trail Blazers', 201 | color: '#000', 202 | conf: 'western', 203 | logo: require('../../assets/img/por.png') 204 | }, 205 | sac: { 206 | id: 1610612758, 207 | city: 'Sacramento', 208 | team: 'Kings', 209 | color: '#724C9F', 210 | conf: 'western', 211 | logo: require('../../assets/img/sac.png') 212 | }, 213 | sas: { 214 | id: 1610612759, 215 | city: 'San Antonio', 216 | team: 'Spurs', 217 | color: '#B6BFBF', 218 | conf: 'western', 219 | logo: require('../../assets/img/sas.png') 220 | }, 221 | tor: { 222 | id: 1610612761, 223 | city: 'Toronto', 224 | team: 'Raptors', 225 | color: '#CE1141', 226 | conf: 'eastern', 227 | logo: require('../../assets/img/tor.png') 228 | }, 229 | uta: { 230 | id: 1610612762, 231 | city: 'Utah', 232 | team: 'Jazz', 233 | color: '#00471B', 234 | conf: 'western', 235 | logo: require('../../assets/img/uta.png') 236 | }, 237 | was: { 238 | id: 1610612764, 239 | city: 'Washington', 240 | team: 'Wizards', 241 | color: '#F5002F', 242 | conf: 'eastern', 243 | logo: require('../../assets/img/was.png') 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /assets/img/atl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/atl.png -------------------------------------------------------------------------------- /assets/img/bkn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/bkn.png -------------------------------------------------------------------------------- /assets/img/bos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/bos.png -------------------------------------------------------------------------------- /assets/img/cha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/cha.png -------------------------------------------------------------------------------- /assets/img/chi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/chi.png -------------------------------------------------------------------------------- /assets/img/cle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/cle.png -------------------------------------------------------------------------------- /assets/img/dal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/dal.png -------------------------------------------------------------------------------- /assets/img/den.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/den.png -------------------------------------------------------------------------------- /assets/img/det.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/det.png -------------------------------------------------------------------------------- /assets/img/gsw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/gsw.png -------------------------------------------------------------------------------- /assets/img/hou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/hou.png -------------------------------------------------------------------------------- /assets/img/hou2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/hou2.png -------------------------------------------------------------------------------- /assets/img/ind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/ind.png -------------------------------------------------------------------------------- /assets/img/lac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/lac.png -------------------------------------------------------------------------------- /assets/img/lal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/lal.png -------------------------------------------------------------------------------- /assets/img/mem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/mem.png -------------------------------------------------------------------------------- /assets/img/mia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/mia.png -------------------------------------------------------------------------------- /assets/img/mil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/mil.png -------------------------------------------------------------------------------- /assets/img/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/min.png -------------------------------------------------------------------------------- /assets/img/nop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/nop.png -------------------------------------------------------------------------------- /assets/img/nyk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/nyk.png -------------------------------------------------------------------------------- /assets/img/okc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/okc.png -------------------------------------------------------------------------------- /assets/img/orl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/orl.png -------------------------------------------------------------------------------- /assets/img/phi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/phi.png -------------------------------------------------------------------------------- /assets/img/phx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/phx.png -------------------------------------------------------------------------------- /assets/img/por.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/por.png -------------------------------------------------------------------------------- /assets/img/sac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/sac.png -------------------------------------------------------------------------------- /assets/img/sas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/sas.png -------------------------------------------------------------------------------- /assets/img/tor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/tor.png -------------------------------------------------------------------------------- /assets/img/uta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/uta.png -------------------------------------------------------------------------------- /assets/img/was.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/assets/img/was.png -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 5.0.0 4 | 5 | dependencies: 6 | override: 7 | - npm install 8 | -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import {AppRegistry} from 'react-native' 4 | import Root from './app/root' 5 | 6 | AppRegistry.registerComponent('allyoop', () => Root) 7 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import {AppRegistry} from 'react-native' 4 | import Root from './app/root' 5 | 6 | AppRegistry.registerComponent('allyoop', () => Root) 7 | -------------------------------------------------------------------------------- /ios/FAKIcon.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | /** 5 | * Abstract superclass for icons, should not be used directly. You can subclass this class to provide new icon font support. 6 | */ 7 | @interface FAKIcon : NSObject 8 | 9 | /** 10 | * The drawing offset of the icon in the image. If you do not specify this property, the icon is centered horizontally and vertically inside the image. 11 | */ 12 | @property (nonatomic) UIOffset drawingPositionAdjustment; 13 | 14 | /** 15 | * The background color of the image while drawing. If you do not specify this property, no background color is drawn. 16 | */ 17 | @property (strong, nonatomic) UIColor *drawingBackgroundColor; 18 | 19 | /** 20 | * The icon font size for the icon. 21 | */ 22 | @property (nonatomic) CGFloat iconFontSize; 23 | 24 | /** 25 | * Register a icon font with it's file url. 26 | * 27 | * @param url The file url for the font, file must exists. 28 | */ 29 | + (void)registerIconFontWithURL:(NSURL *)url; 30 | 31 | /** 32 | * Returns an dictionary of icons available for this icon font. 33 | * 34 | * @return A dictionary of icons. The keys are character codes of icons, the corresponding value for a key is the name for that icon. 35 | */ 36 | + (NSDictionary *)allIcons; 37 | 38 | /** 39 | * Creates and returns an icon font object for the specified size. This is an abstract method and subclasses must provide an implementation. 40 | * 41 | * @param size The size (in points) to which the font is scaled. This value must be greater than 0.0. 42 | * 43 | * @return A font object of the specified name and size. 44 | */ 45 | + (UIFont *)iconFontWithSize:(CGFloat)size; 46 | 47 | /** 48 | * Creates and returns a FAKIcon object for the specified character code and size. 49 | * 50 | * @param code A string represents a character code. Like @"\uf000" 51 | * @param size The desired size (in points) of the icon font that will be used for the icon. This value must be greater than 0.0. 52 | * 53 | * @return Returns a FAKIcon object. 54 | */ 55 | + (instancetype)iconWithCode:(NSString *)code size:(CGFloat)size; 56 | 57 | /** 58 | * Adds an attribute with the given name and value to the icon. 59 | * 60 | * @param attrs A dictionary containing the text attributes to set. These attributes will be used to create attributedString when you call -attributedString on the receiver. For information about system-supplied attribute keys, See NSAttributedString UIKit Additions Reference (https://developer.apple.com/library/ios/documentation/UIKit/Reference/NSAttributedString_UIKit_Additions/Reference/Reference.html#//apple_ref/doc/uid/TP40011688-CH1-SW16) 61 | * @warning You should not set the NSFontAttributeName attribute to another font. 62 | */ 63 | - (void)setAttributes:(NSDictionary *)attrs; 64 | 65 | /** 66 | * Adds an attribute with the given name and value to the icon. 67 | * 68 | * @param name A string specifying the attribute name. 69 | * @param value Adds an attribute with the given name and value to the icon. 70 | */ 71 | - (void)addAttribute:(NSString *)name value:(id)value; 72 | 73 | /** 74 | * Adds the given collection of attributes to the icon. 75 | * 76 | * @param attrs A dictionary containing the attributes to add. 77 | */ 78 | - (void)addAttributes:(NSDictionary *)attrs; 79 | 80 | /** 81 | * Removes the named attribute from the icon. 82 | * 83 | * @param name A string specifying the attribute name to remove. 84 | */ 85 | - (void)removeAttribute:(NSString *)name; 86 | 87 | /** 88 | * Returns the attributes for the icon. 89 | * 90 | * @return The attributes for the icon. 91 | */ 92 | - (NSDictionary *)attributes; 93 | 94 | /** 95 | * Returns the value for an attribute with a given name of the icon. 96 | * 97 | * @param attrName The name of an attribute. 98 | * 99 | * @return The value for an attribute with a given name of the icon, or nil if there is no such attribute. 100 | */ 101 | - (id)attribute:(NSString *)attrName; 102 | 103 | /** 104 | * Creates and returns a NSAttributedString with specified attributes for the receiver. 105 | * 106 | * @return A NSAttributedString with specifed attributes. 107 | */ 108 | - (NSAttributedString *)attributedString; 109 | 110 | /** 111 | * Returns the character code of the icon. 112 | * 113 | * @return The character code of the icon. 114 | */ 115 | - (NSString *)characterCode; 116 | 117 | /** 118 | * Returns the name of the icon. 119 | * 120 | * @return The name of the icon. 121 | */ 122 | - (NSString *)iconName; 123 | 124 | /** 125 | * Returns the icon font of the icon. 126 | * 127 | * @return The icon font of the icon. 128 | */ 129 | - (UIFont *)iconFont; 130 | 131 | /** 132 | * Draws the icon on an image. The icon will be centered horizontally and vertically by default. You can set the drawingPostionAdjustment property to adjust drawing offset. 133 | * 134 | * @param imageSize Height and width for the image. 135 | * 136 | * @return An image with the icon. 137 | */ 138 | - (UIImage *)imageWithSize:(CGSize)imageSize; 139 | 140 | @end 141 | 142 | @interface UIImage (FAKAddon) 143 | 144 | /** 145 | * Draws the FAKIcons in an array on an image. These icons will be centered horizontally and vertically by default. You can set the drawingPostionAdjustment property to adjust drawing offset for each icon. 146 | * 147 | * @param icons The icons to be drawn. The first icon will be drawn on the bottom and the last icon will be drawn on the top. 148 | * @param imageSize Height and width for the generated image. 149 | * 150 | * @return An image with the icons. 151 | */ 152 | + (UIImage *)imageWithStackedIcons:(NSArray *)icons imageSize:(CGSize)imageSize; 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /ios/FAKIcon.m: -------------------------------------------------------------------------------- 1 | #import "FAKIcon.h" 2 | #import 3 | 4 | @interface FAKIcon () 5 | 6 | @property (strong, nonatomic) NSMutableAttributedString *mutableAttributedString; 7 | 8 | @end 9 | 10 | @implementation FAKIcon 11 | 12 | + (void)registerIconFontWithURL:(NSURL *)url 13 | { 14 | NSAssert([[NSFileManager defaultManager] fileExistsAtPath:[url path]], @"Font file doesn't exist"); 15 | CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)url); 16 | CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider); 17 | CGDataProviderRelease(fontDataProvider); 18 | CFErrorRef error = NULL; 19 | CTFontManagerRegisterGraphicsFont(newFont, &error); 20 | CGFontRelease(newFont); 21 | 22 | if (error) { 23 | CFRelease(error); 24 | } 25 | } 26 | 27 | + (NSDictionary *)allIcons 28 | { 29 | @throw @"You need to implement this method in subclass."; 30 | } 31 | 32 | + (UIFont *)iconFontWithSize:(CGFloat)size 33 | { 34 | @throw @"You need to implement this method in subclass."; 35 | } 36 | 37 | + (instancetype)iconWithCode:(NSString *)code size:(CGFloat)size 38 | { 39 | FAKIcon *icon = [[self alloc] init]; 40 | icon.mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:code attributes:@{NSFontAttributeName: [self iconFontWithSize:size]}]; 41 | return icon; 42 | } 43 | 44 | - (NSAttributedString *)attributedString 45 | { 46 | return [self.mutableAttributedString copy]; 47 | } 48 | 49 | - (NSString *)characterCode 50 | { 51 | return [self.mutableAttributedString string]; 52 | } 53 | 54 | - (NSString *)iconName 55 | { 56 | return [[self class] allIcons][[self characterCode]]; 57 | } 58 | 59 | - (CGFloat)iconFontSize 60 | { 61 | return [self iconFont].pointSize; 62 | } 63 | 64 | - (void)setIconFontSize:(CGFloat)iconSize 65 | { 66 | [self addAttribute:NSFontAttributeName value:[[self iconFont] fontWithSize:iconSize]]; 67 | } 68 | 69 | #pragma mark - Setting and Getting Attributes 70 | 71 | - (void)setAttributes:(NSDictionary *)attrs; 72 | { 73 | if (!attrs[NSFontAttributeName]) { 74 | NSMutableDictionary *mutableAttrs = [attrs mutableCopy]; 75 | mutableAttrs[NSFontAttributeName] = self.iconFont; 76 | attrs = [mutableAttrs copy]; 77 | } 78 | [self.mutableAttributedString setAttributes:attrs range:[self rangeForMutableAttributedText]] ; 79 | } 80 | 81 | - (void)addAttribute:(NSString *)name value:(id)value 82 | { 83 | [self.mutableAttributedString addAttribute:name value:value range:[self rangeForMutableAttributedText]]; 84 | } 85 | 86 | - (void)addAttributes:(NSDictionary *)attrs 87 | { 88 | [self.mutableAttributedString addAttributes:attrs range:[self rangeForMutableAttributedText]]; 89 | } 90 | 91 | - (void)removeAttribute:(NSString *)name 92 | { 93 | [self.mutableAttributedString removeAttribute:name range:[self rangeForMutableAttributedText]]; 94 | } 95 | 96 | - (NSDictionary *)attributes 97 | { 98 | return [self.mutableAttributedString attributesAtIndex:0 effectiveRange:NULL]; 99 | } 100 | 101 | - (id)attribute:(NSString *)attrName 102 | { 103 | return [self.mutableAttributedString attribute:attrName atIndex:0 effectiveRange:NULL]; 104 | } 105 | 106 | - (NSRange)rangeForMutableAttributedText 107 | { 108 | return NSMakeRange(0, [self.mutableAttributedString length]); 109 | } 110 | 111 | - (UIFont *)iconFont 112 | { 113 | return [self attribute:NSFontAttributeName]; 114 | } 115 | 116 | #pragma mark - Image Drawing 117 | 118 | - (UIImage *)imageWithSize:(CGSize)imageSize 119 | { 120 | UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0); 121 | 122 | // ---------- begin context ---------- 123 | CGContextRef context = UIGraphicsGetCurrentContext(); 124 | 125 | [self fillBackgroundForContext:context backgroundSize:imageSize]; 126 | 127 | [self.mutableAttributedString drawInRect:[self drawingRectWithImageSize:imageSize]]; 128 | UIImage *iconImage = UIGraphicsGetImageFromCurrentImageContext(); 129 | 130 | // ---------- end context ---------- 131 | UIGraphicsEndImageContext(); 132 | 133 | return iconImage; 134 | } 135 | 136 | - (void)fillBackgroundForContext:(CGContextRef)context backgroundSize:(CGSize)size 137 | { 138 | if (self.drawingBackgroundColor) { 139 | [self.drawingBackgroundColor setFill]; 140 | CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height)); 141 | } 142 | } 143 | 144 | // Calculate the correct drawing position 145 | - (CGRect)drawingRectWithImageSize:(CGSize)imageSize 146 | { 147 | CGSize iconSize = [self.mutableAttributedString size]; 148 | CGFloat xOffset = (imageSize.width - iconSize.width) / 2.0; 149 | xOffset += self.drawingPositionAdjustment.horizontal; 150 | CGFloat yOffset = (imageSize.height - iconSize.height) / 2.0; 151 | yOffset += self.drawingPositionAdjustment.vertical; 152 | return CGRectMake(xOffset, yOffset, iconSize.width, iconSize.height); 153 | } 154 | 155 | @end 156 | 157 | #pragma mark - Stacked Icons 158 | 159 | @implementation UIImage (FAKAddon) 160 | 161 | + (UIImage *)imageWithStackedIcons:(NSArray *)icons imageSize:(CGSize)imageSize 162 | { 163 | UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0); 164 | 165 | // ---------- begin context ---------- 166 | CGContextRef context = UIGraphicsGetCurrentContext(); 167 | 168 | for (FAKIcon *icon in icons) { 169 | NSAssert([icon isKindOfClass:[FAKIcon class]], @"You can only stack FAKIcon derived objects."); 170 | [icon fillBackgroundForContext:context backgroundSize:imageSize]; 171 | [icon.mutableAttributedString drawInRect:[icon drawingRectWithImageSize:imageSize]]; 172 | } 173 | 174 | UIImage *iconImage = UIGraphicsGetImageFromCurrentImageContext(); 175 | 176 | // ---------- end context ---------- 177 | UIGraphicsEndImageContext(); 178 | 179 | return iconImage; 180 | } 181 | 182 | @end 183 | 184 | -------------------------------------------------------------------------------- /ios/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "3x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "3x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "57x57", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "57x57", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "iphone", 45 | "size" : "60x60", 46 | "scale" : "3x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/atl.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "atl.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/atl.imageset/atl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/atl.imageset/atl.png -------------------------------------------------------------------------------- /ios/Images.xcassets/bkn.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bkn.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/bkn.imageset/bkn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/bkn.imageset/bkn.png -------------------------------------------------------------------------------- /ios/Images.xcassets/bos.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bos.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/bos.imageset/bos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/bos.imageset/bos.png -------------------------------------------------------------------------------- /ios/Images.xcassets/cha.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cha.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/cha.imageset/cha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/cha.imageset/cha.png -------------------------------------------------------------------------------- /ios/Images.xcassets/chi.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "chi.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/chi.imageset/chi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/chi.imageset/chi.png -------------------------------------------------------------------------------- /ios/Images.xcassets/cle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cle.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/cle.imageset/cle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/cle.imageset/cle.png -------------------------------------------------------------------------------- /ios/Images.xcassets/dal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "dal.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/dal.imageset/dal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/dal.imageset/dal.png -------------------------------------------------------------------------------- /ios/Images.xcassets/den.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "den.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/den.imageset/den.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/den.imageset/den.png -------------------------------------------------------------------------------- /ios/Images.xcassets/det.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "det.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/det.imageset/det.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/det.imageset/det.png -------------------------------------------------------------------------------- /ios/Images.xcassets/gsw.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "gsw.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/gsw.imageset/gsw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/gsw.imageset/gsw.png -------------------------------------------------------------------------------- /ios/Images.xcassets/hou.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hou2.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/hou.imageset/hou2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/hou.imageset/hou2.png -------------------------------------------------------------------------------- /ios/Images.xcassets/hou2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hou.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/hou2.imageset/hou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/hou2.imageset/hou.png -------------------------------------------------------------------------------- /ios/Images.xcassets/ind.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ind.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/ind.imageset/ind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/ind.imageset/ind.png -------------------------------------------------------------------------------- /ios/Images.xcassets/lac.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "lac.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/lac.imageset/lac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/lac.imageset/lac.png -------------------------------------------------------------------------------- /ios/Images.xcassets/lal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "lal.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/lal.imageset/lal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/lal.imageset/lal.png -------------------------------------------------------------------------------- /ios/Images.xcassets/mem.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "mem.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/mem.imageset/mem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/mem.imageset/mem.png -------------------------------------------------------------------------------- /ios/Images.xcassets/mia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "mia.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/mia.imageset/mia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/mia.imageset/mia.png -------------------------------------------------------------------------------- /ios/Images.xcassets/mil.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "mil.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/mil.imageset/mil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/mil.imageset/mil.png -------------------------------------------------------------------------------- /ios/Images.xcassets/min.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "min.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/min.imageset/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/min.imageset/min.png -------------------------------------------------------------------------------- /ios/Images.xcassets/nop.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "nop.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/nop.imageset/nop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/nop.imageset/nop.png -------------------------------------------------------------------------------- /ios/Images.xcassets/nyk.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "nyk.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/nyk.imageset/nyk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/nyk.imageset/nyk.png -------------------------------------------------------------------------------- /ios/Images.xcassets/okc.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "okc.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/okc.imageset/okc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/okc.imageset/okc.png -------------------------------------------------------------------------------- /ios/Images.xcassets/orl.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "orl.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/orl.imageset/orl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/orl.imageset/orl.png -------------------------------------------------------------------------------- /ios/Images.xcassets/phi.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "phi.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/phi.imageset/phi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/phi.imageset/phi.png -------------------------------------------------------------------------------- /ios/Images.xcassets/phx.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "phx.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/phx.imageset/phx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/phx.imageset/phx.png -------------------------------------------------------------------------------- /ios/Images.xcassets/por.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "por.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/por.imageset/por.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/por.imageset/por.png -------------------------------------------------------------------------------- /ios/Images.xcassets/sac.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sac.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/sac.imageset/sac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/sac.imageset/sac.png -------------------------------------------------------------------------------- /ios/Images.xcassets/sas.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sas.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/sas.imageset/sas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/sas.imageset/sas.png -------------------------------------------------------------------------------- /ios/Images.xcassets/tor.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tor.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/tor.imageset/tor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/tor.imageset/tor.png -------------------------------------------------------------------------------- /ios/Images.xcassets/uta.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "uta.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/uta.imageset/uta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/uta.imageset/uta.png -------------------------------------------------------------------------------- /ios/Images.xcassets/was.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "was.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ios/Images.xcassets/was.imageset/was.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/Images.xcassets/was.imageset/was.png -------------------------------------------------------------------------------- /ios/allyoop.xcodeproj/xcshareddata/xcschemes/allyoop.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /ios/allyoop/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/allyoop/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTRootView.h" 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | NSURL *jsCodeLocation; 19 | 20 | /** 21 | * Loading JavaScript code - uncomment the one you want. 22 | * 23 | * OPTION 1 24 | * Load from development server. Start the server from the repository root: 25 | * 26 | * $ npm start 27 | * 28 | * To run on device, change `localhost` to the IP address of your computer 29 | * (you can get this by typing `ifconfig` into the terminal and selecting the 30 | * `inet` value under `en0:`) and make sure your computer and iOS device are 31 | * on the same Wi-Fi network. 32 | */ 33 | 34 | jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; 35 | 36 | /** 37 | * OPTION 2 38 | * Load from pre-bundled file on disk. The static bundle is automatically 39 | * generated by "Bundle React Native code and images" build step. 40 | */ 41 | 42 | // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 43 | 44 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 45 | moduleName:@"allyoop" 46 | initialProperties:nil 47 | launchOptions:launchOptions]; 48 | 49 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 50 | UIViewController *rootViewController = [UIViewController new]; 51 | rootViewController.view = rootView; 52 | self.window.rootViewController = rootViewController; 53 | [self.window makeKeyAndVisible]; 54 | return YES; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ios/allyoop/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/allyoop/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ios/allyoop/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 | NSAllowsArbitraryLoads 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /ios/allyoop/RNUserDefaults-bridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNUserDefaults-bridge.h 3 | // allyoop 4 | // 5 | // Created by wayne on 15/12/12. 6 | // Copyright © 2015年 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTBridgeModule.h" 10 | -------------------------------------------------------------------------------- /ios/allyoop/RNUserDefaults.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNUserDefaults.m 3 | // allyoop 4 | // 5 | // Created by wayne on 15/12/11. 6 | // Copyright © 2015年 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RCTBridgeModule.h" 10 | 11 | @interface RCT_EXTERN_MODULE(RNUserDefaults, NSObject) 12 | 13 | RCT_EXTERN_METHOD(setObject:(NSString *)key forValue:(NSString *)value callback:(RCTResponseSenderBlock *)cb) 14 | 15 | RCT_EXTERN_METHOD(getString:(NSString *)key callback:(RCTResponseSenderBlock *)cb) 16 | 17 | RCT_EXTERN_METHOD(removeObject:(NSString *)key callback:(RCTResponseSenderBlock *)cb) 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ios/allyoop/RNUserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RNUserDefaults.swift 3 | // allyoop 4 | // 5 | // Created by wayne on 15/12/11. 6 | // Copyright © 2015年 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @objc(RNUserDefaults) 11 | 12 | class RNUserDefaults: NSObject { 13 | 14 | let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults() 15 | 16 | @objc func setObject(key: String, forValue value: String, callback cb: RCTResponseSenderBlock) -> Void { 17 | self.defaults.setObject(value, forKey: key) 18 | cb(["Success"]) 19 | } 20 | 21 | @objc func getString(key:String, callback cb: RCTResponseSenderBlock) -> Void { 22 | if let value = self.defaults.stringForKey(key) { 23 | cb([value]) 24 | } else { 25 | cb([false]) 26 | } 27 | } 28 | 29 | @objc func removeObject(key: String, callback cb: RCTResponseSenderBlock) -> Void { 30 | self.defaults.removeObjectForKey(key) 31 | cb(["Remove success"]) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /ios/allyoop/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/allyoopTests/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/allyoopTests/allyoopTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import "RCTLog.h" 14 | #import "RCTRootView.h" 15 | 16 | #define TIMEOUT_SECONDS 240 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface allyoopTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation allyoopTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /ios/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwayne/react-native-nba-app/f0cdfcadca396b9b2e306fc8023fce4b9677d5ea/ios/ionicons.ttf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "allyoop", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "clean": "find node_modules -type f -name .babelrc | grep -v /react-native/ | xargs rm", 7 | "test": "standard --verbose | snazzy ./lib/**/*.js", 8 | "prestart": "npm run clean", 9 | "start": "node_modules/react-native/packager/packager.sh" 10 | }, 11 | "dependencies": { 12 | "bluebird": "^3.0.5", 13 | "es6-enum": "^1.0.0", 14 | "moment-timezone": "^0.4.1", 15 | "react-native": "^0.17.0", 16 | "react-native-icons": "^0.7.0", 17 | "react-redux": "^3.1.0", 18 | "redux": "^3.0.4", 19 | "redux-thunk": "^1.0.0", 20 | "whatwg-fetch": "^0.10.1" 21 | }, 22 | "devDependencies": { 23 | "snazzy": "^2.0.1", 24 | "standard": "^5.4.1" 25 | } 26 | } 27 | --------------------------------------------------------------------------------