├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── ScreenShots
├── Chi Tiết Sản Phẩm.png
├── Giỏ Hàng.png
├── Liên hệ.png
├── Trang Chủ.png
└── Tìm kiếm.png
├── __tests__
├── index.android.js
└── index.ios.js
├── android
├── app
│ ├── BUCK
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ └── fonts
│ │ │ ├── Entypo.ttf
│ │ │ ├── EvilIcons.ttf
│ │ │ ├── FontAwesome.ttf
│ │ │ ├── Foundation.ttf
│ │ │ ├── Ionicons.ttf
│ │ │ ├── MaterialCommunityIcons.ttf
│ │ │ ├── MaterialIcons.ttf
│ │ │ ├── Octicons.ttf
│ │ │ ├── SimpleLineIcons.ttf
│ │ │ └── Zocial.ttf
│ │ ├── java
│ │ └── com
│ │ │ └── foodapp
│ │ │ ├── MainActivity.java
│ │ │ └── MainApplication.java
│ │ └── res
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ └── values
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── keystores
│ ├── BUCK
│ └── debug.keystore.properties
└── settings.gradle
├── app.json
├── index.android.js
├── index.ios.js
├── ios
├── foodapp-tvOS
│ └── Info.plist
├── foodapp-tvOSTests
│ └── Info.plist
├── foodapp.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── foodapp-tvOS.xcscheme
│ │ └── foodapp.xcscheme
├── foodapp
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── ImagePickerManager.h
│ ├── ImagePickerManager.m
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ └── main.m
└── foodappTests
│ ├── Info.plist
│ └── foodappTests.m
├── package.json
├── server.zip
├── src
├── app.android.js
├── app.ios.js
├── constants
│ ├── actionTypes.js
│ ├── api.js
│ └── constants.js
├── img
│ ├── arrow-down@2x.png
│ ├── ios-arrow-down@1.png
│ ├── ios-arrow-down@2x.png
│ ├── ios-arrow-down@3x.png
│ ├── ios-search@1x.png
│ └── ios-search@2x.png
├── lib
│ └── WooCommerce
│ │ ├── Api.js
│ │ └── Config.js
├── modules
│ ├── _global
│ │ ├── Drawer.js
│ │ ├── ProgressBar.js
│ │ ├── scrollableTabView
│ │ │ ├── Button.android.js
│ │ │ ├── Button.ios.js
│ │ │ └── DefaultTabBar.js
│ │ └── styles
│ │ │ └── Drawer.js
│ └── foods
│ │ ├── Cart.js
│ │ ├── Contact.js
│ │ ├── CreatePost.js
│ │ ├── DetailFood.js
│ │ ├── Foods.js
│ │ ├── FoodsList.js
│ │ ├── Search.js
│ │ ├── Temp.js
│ │ ├── components
│ │ ├── CardCategory.js
│ │ ├── CardContact.js
│ │ ├── CardOne.js
│ │ ├── CardThree.js
│ │ ├── CardTwo.js
│ │ ├── ItemCart.js
│ │ └── styles
│ │ │ ├── CardOne.js
│ │ │ ├── CardThree.js
│ │ │ └── CardTwo.js
│ │ ├── foods.actions.js
│ │ ├── foods.reducer.js
│ │ └── styles
│ │ ├── Cart.js
│ │ ├── DetailFood.js
│ │ ├── Food.js
│ │ ├── Foods.js
│ │ ├── FoodsList.js
│ │ └── Search.js
├── reducers
│ ├── initialState.js
│ └── rootReducer.js
├── screens.js
├── store
│ └── configureStore.js
└── utils
│ └── AppIcons.js
├── temp.txt
├── utils
└── reactotronConfig.js
└── yarn.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
50 |
51 | fastlane/report.xml
52 | fastlane/Preview.html
53 | fastlane/screenshots
54 |
55 |
56 | # DEV
57 |
58 | .env
59 | yarn-error.log
60 | .vscode
61 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 June Domingo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # foodapp
2 |
3 | An app developed on React Native which connect to WordPress WooCommerce API to display products and order.
4 |
5 | # Video Demo
6 | - Link demo: https://www.youtube.com/watch?v=7BkctT_0z8M
7 |
8 | ### What's included
9 |
10 | - [React Native](https://facebook.github.io/react-native/) - Build Native Mobile Apps using JavaScript and React
11 | - [Redux](https://nodejs.org/) - Predictable state container for JavaScript apps
12 | - [Wix/react-native-navigation](https://github.com/wix/react-native-navigation) - A complete native navigation solution for React Native with optional redux support - nav bars, tabs, drawer, modals
13 | - [Redux Thunk](https://github.com/gaearon/redux-thunk) - Thunk middleware for Redux
14 | - [ESLint](http://eslint.org/) - The pluggable linting utility for JavaScript and JSX
15 | - [Wordpress](https://wordpress.com) - The easiest way to create a website or blog
16 | - [WooCommerce API](https://docs.woocommerce.com/document/woocommerce-rest-api/) WooCommerce (WC) 2.6+ is fully integrated with the WordPress REST API
17 |
18 | ### Requirements
19 | - [Node](https://nodejs.org) `4.x` or newer
20 | - [React Native](http://facebook.github.io/react-native/docs/getting-started.html) for development
21 | - [Android Studio](https://developer.android.com/studio/index.html) for Android development
22 | - [Xcode](https://developer.apple.com/xcode/) for iOS development
23 | - [Android SDK](https://developer.android.com/sdk/) `23.0.1` or newer for Android development
24 | - [Genymotion](https://www.genymotion.com/) for Android emulation
25 | - [YARN](https://yarnpkg.com/) - for dependency management
26 |
27 |
28 | ### Installation
29 |
30 | Clone this repo
31 |
32 | ```sh
33 | $ git clone https://github.com/ntphuc/react-native-woocommerce-food.git
34 | $ cd foodapp
35 | $ yarn install or npm install
36 | ```
37 | ### Installation SERVER NODEJS for WooCommerce Rest API
38 | - Extract file zip server.zip
39 | - cd to folder server
40 | - Open terminal to start server by command : $ node server.js
41 |
42 | ### CHANGE WEB_URL in project React Native app
43 |
44 | Change value constant WEB_URL in folder src/constants/api.js
45 | - export const WEB_URL = "http://YOUR_IP:YOUR_PORT";
46 |
47 | ### How to start
48 | ```sh
49 | $ react-native run-android
50 | $ react-native run-ios
51 | ```
52 |
--------------------------------------------------------------------------------
/ScreenShots/Chi Tiết Sản Phẩm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/ScreenShots/Chi Tiết Sản Phẩm.png
--------------------------------------------------------------------------------
/ScreenShots/Giỏ Hàng.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/ScreenShots/Giỏ Hàng.png
--------------------------------------------------------------------------------
/ScreenShots/Liên hệ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/ScreenShots/Liên hệ.png
--------------------------------------------------------------------------------
/ScreenShots/Trang Chủ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/ScreenShots/Trang Chủ.png
--------------------------------------------------------------------------------
/ScreenShots/Tìm kiếm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/ScreenShots/Tìm kiếm.png
--------------------------------------------------------------------------------
/__tests__/index.android.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import Index from '../index.android.js';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | const tree = renderer.create(
10 |
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/__tests__/index.ios.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import Index from '../index.ios.js';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | const tree = renderer.create(
10 |
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/android/app/BUCK:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | # To learn about Buck see [Docs](https://buckbuild.com/).
4 | # To run your application with Buck:
5 | # - install Buck
6 | # - `npm start` - to start the packager
7 | # - `cd android`
8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
10 | # - `buck install -r android/app` - compile, install and run application
11 | #
12 |
13 | lib_deps = []
14 | for jarfile in glob(['libs/*.jar']):
15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
16 | lib_deps.append(':' + name)
17 | prebuilt_jar(
18 | name = name,
19 | binary_jar = jarfile,
20 | )
21 |
22 | for aarfile in glob(['libs/*.aar']):
23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
24 | lib_deps.append(':' + name)
25 | android_prebuilt_aar(
26 | name = name,
27 | aar = aarfile,
28 | )
29 |
30 | android_library(
31 | name = 'all-libs',
32 | exported_deps = lib_deps
33 | )
34 |
35 | android_library(
36 | name = 'app-code',
37 | srcs = glob([
38 | 'src/main/java/**/*.java',
39 | ]),
40 | deps = [
41 | ':all-libs',
42 | ':build_config',
43 | ':res',
44 | ],
45 | )
46 |
47 | android_build_config(
48 | name = 'build_config',
49 | package = 'com.foodapp',
50 | )
51 |
52 | android_resource(
53 | name = 'res',
54 | res = 'src/main/res',
55 | package = 'com.foodapp',
56 | )
57 |
58 | android_binary(
59 | name = 'app',
60 | package_type = 'debug',
61 | manifest = 'src/main/AndroidManifest.xml',
62 | keystore = '//android/keystores:debug',
63 | deps = [
64 | ':app-code',
65 | ],
66 | )
67 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
3 |
4 | import com.android.build.OutputFile
5 |
6 | /**
7 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
8 | * and bundleReleaseJsAndAssets).
9 | * These basically call `react-native bundle` with the correct arguments during the Android build
10 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
11 | * bundle directly from the development server. Below you can see all the possible configurations
12 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
13 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
14 | *
15 | * project.ext.react = [
16 | * // the name of the generated asset file containing your JS bundle
17 | * bundleAssetName: "index.android.bundle",
18 | *
19 | * // the entry file for bundle generation
20 | * entryFile: "index.android.js",
21 | *
22 | * // whether to bundle JS and assets in debug mode
23 | * bundleInDebug: false,
24 | *
25 | * // whether to bundle JS and assets in release mode
26 | * bundleInRelease: true,
27 | *
28 | * // whether to bundle JS and assets in another build variant (if configured).
29 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
30 | * // The configuration property can be in the following formats
31 | * // 'bundleIn${productFlavor}${buildType}'
32 | * // 'bundleIn${buildType}'
33 | * // bundleInFreeDebug: true,
34 | * // bundleInPaidRelease: true,
35 | * // bundleInBeta: true,
36 | *
37 | * // the root of your project, i.e. where "package.json" lives
38 | * root: "../../",
39 | *
40 | * // where to put the JS bundle asset in debug mode
41 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
42 | *
43 | * // where to put the JS bundle asset in release mode
44 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
45 | *
46 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
47 | * // require('./image.png')), in debug mode
48 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
49 | *
50 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
51 | * // require('./image.png')), in release mode
52 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
53 | *
54 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
55 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
56 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
57 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
58 | * // for example, you might want to remove it from here.
59 | * inputExcludes: ["android/**", "ios/**"],
60 | *
61 | * // override which node gets called and with what additional arguments
62 | * nodeExecutableAndArgs: ["node"]
63 | *
64 | * // supply additional arguments to the packager
65 | * extraPackagerArgs: []
66 | * ]
67 | */
68 |
69 | apply from: "../../node_modules/react-native/react.gradle"
70 |
71 | /**
72 | * Set this to true to create two separate APKs instead of one:
73 | * - An APK that only works on ARM devices
74 | * - An APK that only works on x86 devices
75 | * The advantage is the size of the APK is reduced by about 4MB.
76 | * Upload all the APKs to the Play Store and people will download
77 | * the correct one based on the CPU architecture of their device.
78 | */
79 | def enableSeparateBuildPerCPUArchitecture = false
80 |
81 | /**
82 | * Run Proguard to shrink the Java bytecode in release builds.
83 | */
84 | def enableProguardInReleaseBuilds = false
85 |
86 | android {
87 | compileSdkVersion 23
88 | buildToolsVersion "23.0.1"
89 |
90 | defaultConfig {
91 | applicationId "com.foodapp"
92 | minSdkVersion 16
93 | targetSdkVersion 22
94 | versionCode 1
95 | versionName "1.0"
96 | ndk {
97 | abiFilters "armeabi-v7a", "x86"
98 | }
99 | }
100 | /*signingConfigs {
101 | release {
102 | storeFile file(MYAPP_RELEASE_STORE_FILE)
103 | storePassword MYAPP_RELEASE_STORE_PASSWORD
104 | keyAlias MYAPP_RELEASE_KEY_ALIAS
105 | keyPassword MYAPP_RELEASE_KEY_PASSWORD
106 | }
107 | }*/
108 | splits {
109 | abi {
110 | reset()
111 | enable enableSeparateBuildPerCPUArchitecture
112 | universalApk false // If true, also generate a universal APK
113 | include "armeabi-v7a", "x86"
114 | }
115 | }
116 | buildTypes {
117 | release {
118 | minifyEnabled enableProguardInReleaseBuilds
119 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
120 | // signingConfig signingConfigs.release
121 | }
122 | }
123 | // applicationVariants are e.g. debug, release
124 | applicationVariants.all { variant ->
125 | variant.outputs.each { output ->
126 | // For each separate APK per architecture, set a unique version code as described here:
127 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
128 | def versionCodes = ["armeabi-v7a":1, "x86":2]
129 | def abi = output.getFilter(OutputFile.ABI)
130 | if (abi != null) { // null for the universal-debug, universal-release variants
131 | output.versionCodeOverride =
132 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
133 | }
134 | }
135 | }
136 | }
137 |
138 | dependencies {
139 |
140 | compile project(':react-native-randombytes')
141 | compile project(':react-native-navigation')
142 | compile project(':react-native-image-picker')
143 | compile project(':react-native-config')
144 | compile project(':react-native-linear-gradient')
145 | compile project(':react-native-vector-icons')
146 | compile fileTree(dir: "libs", include: ["*.jar"])
147 | compile "com.android.support:appcompat-v7:23.0.1"
148 | compile "com.facebook.react:react-native:+" // From node_modules
149 |
150 |
151 | }
152 |
153 | // Run this once to be able to run the application with BUCK
154 | // puts all compile dependencies into folder libs for BUCK to use
155 | task copyDownloadableDepsToLibs(type: Copy) {
156 | from configurations.compile
157 | into 'libs'
158 | }
159 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Disabling obfuscation is useful if you collect stack traces from production crashes
20 | # (unless you are using a system that supports de-obfuscate the stack traces).
21 | -dontobfuscate
22 |
23 | # React Native
24 |
25 | # Keep our interfaces so they can be used by other ProGuard rules.
26 | # See http://sourceforge.net/p/proguard/bugs/466/
27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
30 |
31 | # Do not strip any method/class that is annotated with @DoNotStrip
32 | -keep @com.facebook.proguard.annotations.DoNotStrip class *
33 | -keep @com.facebook.common.internal.DoNotStrip class *
34 | -keepclassmembers class * {
35 | @com.facebook.proguard.annotations.DoNotStrip *;
36 | @com.facebook.common.internal.DoNotStrip *;
37 | }
38 |
39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
40 | void set*(***);
41 | *** get*();
42 | }
43 |
44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; }
46 | -keepclassmembers,includedescriptorclasses class * { native ; }
47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
50 |
51 | -dontwarn com.facebook.react.**
52 |
53 | # okhttp
54 |
55 | -keepattributes Signature
56 | -keepattributes *Annotation*
57 | -keep class okhttp3.** { *; }
58 | -keep interface okhttp3.** { *; }
59 | -dontwarn okhttp3.**
60 |
61 | # okio
62 |
63 | -keep class sun.misc.Unsafe { *; }
64 | -dontwarn java.nio.file.*
65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
66 | -dontwarn okio.**
67 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/android/app/src/main/java/com/foodapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.foodapp;
2 |
3 | // import com.facebook.react.ReactActivity;
4 | import com.reactnativenavigation.controllers.SplashActivity;
5 |
6 | public class MainActivity extends SplashActivity {
7 |
8 | /**
9 | * Returns the name of the main component registered from JavaScript.
10 | * This is used to schedule rendering of the component.
11 | */
12 | // @Override
13 | // protected String getMainComponentName() {
14 | // return "foodapp";
15 | // }
16 | }
17 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/foodapp/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.foodapp;
2 |
3 | import android.app.Application;
4 | import android.util.Log;
5 | import android.support.annotation.NonNull;
6 |
7 | import com.facebook.react.ReactApplication;
8 | import com.bitgo.randombytes.RandomBytesPackage;
9 | import com.imagepicker.ImagePickerPackage;
10 | import com.facebook.react.ReactInstanceManager;
11 | import com.facebook.react.ReactNativeHost;
12 | import com.facebook.react.ReactPackage;
13 | import com.facebook.react.shell.MainReactPackage;
14 | import com.reactnativenavigation.NavigationApplication;
15 | import com.oblador.vectoricons.VectorIconsPackage;
16 | import com.BV.LinearGradient.LinearGradientPackage;
17 | import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
18 | import com.facebook.soloader.SoLoader;
19 |
20 | import java.util.Arrays;
21 | import java.util.List;
22 |
23 | // public class MainApplication extends Application implements ReactApplication {
24 | public class MainApplication extends NavigationApplication {
25 |
26 | // private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
27 | // @Override
28 | // protected boolean getUseDeveloperSupport() {
29 | // return BuildConfig.DEBUG;
30 | // }
31 | //
32 | // @Override
33 | // protected List getPackages() {
34 | // return Arrays.asList(
35 | // new MainReactPackage(),
36 | // new RNWordpressEditorPackage(),
37 | // new RandomBytesPackage(),
38 | // new NavigationReactPackage(),
39 | // new ImagePickerPackage()
40 | // );
41 | // }
42 | // };
43 | //
44 | // @Override
45 | // public ReactNativeHost getReactNativeHost() {
46 | // return mReactNativeHost;
47 | // }
48 |
49 | @Override
50 | public boolean isDebug() {
51 | return BuildConfig.DEBUG;
52 | }
53 |
54 | @NonNull
55 | @Override
56 | public List createAdditionalReactPackages() {
57 | // Add the packages you require here.
58 | // No need to add RnnPackage and MainReactPackage
59 | return Arrays.asList(
60 | new VectorIconsPackage(),
61 | new LinearGradientPackage(),
62 | new ReactNativeConfigPackage(),
63 | new RandomBytesPackage(),
64 | new ImagePickerPackage()
65 | );
66 | }
67 |
68 | @Override
69 | public void onCreate() {
70 | super.onCreate();
71 | SoLoader.init(this, /* native exopackage */ false);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | foodapp
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | mavenLocal()
18 | jcenter()
19 | maven {
20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
21 | url "$rootDir/../node_modules/react-native/android"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | android.useDeprecatedNdk=true
21 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
6 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/keystores/BUCK:
--------------------------------------------------------------------------------
1 | keystore(
2 | name = 'debug',
3 | store = 'debug.keystore',
4 | properties = 'debug.keystore.properties',
5 | visibility = [
6 | 'PUBLIC',
7 | ],
8 | )
9 |
--------------------------------------------------------------------------------
/android/keystores/debug.keystore.properties:
--------------------------------------------------------------------------------
1 | key.store=debug.keystore
2 | key.alias=androiddebugkey
3 | key.store.password=android
4 | key.alias.password=android
5 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'foodapp'
2 |
3 |
4 | include ':app'
5 | include ':react-native-vector-icons'
6 | project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
7 | include ':react-native-navigation'
8 | project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app')
9 | include ':react-native-linear-gradient'
10 | project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
11 | include ':react-native-config'
12 | project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
13 | include ':react-native-image-picker'
14 | project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
15 | include ':react-native-randombytes'
16 | project(':react-native-randombytes').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-randombytes/android')
17 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "foodapp",
3 | "displayName": "foodapp"
4 | }
5 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import App from './src/app';
3 |
4 | const app = new App();
5 |
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import App from './src/app';
3 |
4 | const app = new App();
5 |
--------------------------------------------------------------------------------
/ios/foodapp-tvOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UIViewControllerBasedStatusBarAppearance
38 |
39 | NSLocationWhenInUseUsageDescription
40 |
41 | NSAppTransportSecurity
42 |
43 |
44 | NSExceptionDomains
45 |
46 | localhost
47 |
48 | NSExceptionAllowsInsecureHTTPLoads
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/ios/foodapp-tvOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/foodapp.xcodeproj/xcshareddata/xcschemes/foodapp-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/ios/foodapp.xcodeproj/xcshareddata/xcschemes/foodapp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/ios/foodapp/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/foodapp/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AppDelegate.h"
11 | #import "RCCManager.h"
12 | #import
13 | #import
14 | #import "ReactNativeConfig.h"
15 |
16 | @implementation AppDelegate
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
19 | {
20 | NSURL *jsCodeLocation;
21 |
22 | // rn-config
23 | NSDictionary *config = [ReactNativeConfig env];
24 |
25 | #ifdef DEBUG
26 | jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
27 | #else
28 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
29 | #endif
30 |
31 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
32 | self.window.backgroundColor = [UIColor whiteColor];
33 | [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation];
34 |
35 | // RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
36 | // moduleName:@"foodapp"
37 | // initialProperties:nil
38 | // launchOptions:launchOptions];
39 | // rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
40 | //
41 | // self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
42 | // UIViewController *rootViewController = [UIViewController new];
43 | // rootViewController.view = rootView;
44 | // self.window.rootViewController = rootViewController;
45 | // [self.window makeKeyAndVisible];
46 | return YES;
47 | }
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/ios/foodapp/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/foodapp/ImagePickerManager.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | typedef NS_ENUM(NSInteger, RNImagePickerTarget) {
5 | RNImagePickerTargetCamera = 1,
6 | RNImagePickerTargetLibrarySingleImage,
7 | };
8 |
9 | @interface ImagePickerManager : NSObject
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/ios/foodapp/ImagePickerManager.m:
--------------------------------------------------------------------------------
1 | #import "ImagePickerManager.h"
2 | #import
3 | #import
4 | #import
5 | #import
6 |
7 | @import MobileCoreServices;
8 |
9 | @interface ImagePickerManager ()
10 |
11 | @property (nonatomic, strong) UIAlertController *alertController;
12 | @property (nonatomic, strong) UIImagePickerController *picker;
13 | @property (nonatomic, strong) RCTResponseSenderBlock callback;
14 | @property (nonatomic, strong) NSDictionary *defaultOptions;
15 | @property (nonatomic, retain) NSMutableDictionary *options, *response;
16 | @property (nonatomic, strong) NSArray *customButtons;
17 |
18 | @end
19 |
20 | @implementation ImagePickerManager
21 |
22 | RCT_EXPORT_MODULE();
23 |
24 | RCT_EXPORT_METHOD(launchCamera:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
25 | {
26 | self.callback = callback;
27 | [self launchImagePicker:RNImagePickerTargetCamera options:options];
28 | }
29 |
30 | RCT_EXPORT_METHOD(launchImageLibrary:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
31 | {
32 | self.callback = callback;
33 | [self launchImagePicker:RNImagePickerTargetLibrarySingleImage options:options];
34 | }
35 |
36 | RCT_EXPORT_METHOD(showImagePicker:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
37 | {
38 | self.callback = callback; // Save the callback so we can use it from the delegate methods
39 | self.options = options;
40 |
41 | NSString *title = [self.options valueForKey:@"title"];
42 | if ([title isEqual:[NSNull null]] || title.length == 0) {
43 | title = nil; // A more visually appealing UIAlertControl is displayed with a nil title rather than title = @""
44 | }
45 | NSString *cancelTitle = [self.options valueForKey:@"cancelButtonTitle"];
46 | NSString *takePhotoButtonTitle = [self.options valueForKey:@"takePhotoButtonTitle"];
47 | NSString *chooseFromLibraryButtonTitle = [self.options valueForKey:@"chooseFromLibraryButtonTitle"];
48 |
49 |
50 | self.alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
51 |
52 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
53 | self.callback(@[@{@"didCancel": @YES}]); // Return callback for 'cancel' action (if is required)
54 | }];
55 | [self.alertController addAction:cancelAction];
56 |
57 | if (![takePhotoButtonTitle isEqual:[NSNull null]] && takePhotoButtonTitle.length > 0) {
58 | UIAlertAction *takePhotoAction = [UIAlertAction actionWithTitle:takePhotoButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
59 | [self actionHandler:action];
60 | }];
61 | [self.alertController addAction:takePhotoAction];
62 | }
63 | if (![chooseFromLibraryButtonTitle isEqual:[NSNull null]] && chooseFromLibraryButtonTitle.length > 0) {
64 | UIAlertAction *chooseFromLibraryAction = [UIAlertAction actionWithTitle:chooseFromLibraryButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
65 | [self actionHandler:action];
66 | }];
67 | [self.alertController addAction:chooseFromLibraryAction];
68 | }
69 |
70 | // Add custom buttons to action sheet
71 | if ([self.options objectForKey:@"customButtons"] && [[self.options objectForKey:@"customButtons"] isKindOfClass:[NSArray class]]) {
72 | self.customButtons = [self.options objectForKey:@"customButtons"];
73 | for (NSString *button in self.customButtons) {
74 | NSString *title = [button valueForKey:@"title"];
75 | UIAlertAction *customAction = [UIAlertAction actionWithTitle:title style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
76 | [self actionHandler:action];
77 | }];
78 | [self.alertController addAction:customAction];
79 | }
80 | }
81 |
82 | dispatch_async(dispatch_get_main_queue(), ^{
83 | UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
84 | while (root.presentedViewController != nil) {
85 | root = root.presentedViewController;
86 | }
87 |
88 | /* On iPad, UIAlertController presents a popover view rather than an action sheet like on iPhone. We must provide the location
89 | of the location to show the popover in this case. For simplicity, we'll just display it on the bottom center of the screen
90 | to mimic an action sheet */
91 | self.alertController.popoverPresentationController.sourceView = root.view;
92 | self.alertController.popoverPresentationController.sourceRect = CGRectMake(root.view.bounds.size.width / 2.0, root.view.bounds.size.height, 1.0, 1.0);
93 |
94 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
95 | self.alertController.popoverPresentationController.permittedArrowDirections = 0;
96 | for (id subview in self.alertController.view.subviews) {
97 | if ([subview isMemberOfClass:[UIView class]]) {
98 | ((UIView *)subview).backgroundColor = [UIColor whiteColor];
99 | }
100 | }
101 | }
102 |
103 | [root presentViewController:self.alertController animated:YES completion:nil];
104 | });
105 | }
106 |
107 | - (void)actionHandler:(UIAlertAction *)action
108 | {
109 | // If button title is one of the keys in the customButtons dictionary return the value as a callback
110 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"title==%@", action.title];
111 | NSArray *results = [self.customButtons filteredArrayUsingPredicate:predicate];
112 | if (results.count > 0) {
113 | NSString *customButtonStr = [[results objectAtIndex:0] objectForKey:@"name"];
114 | if (customButtonStr) {
115 | self.callback(@[@{@"customButton": customButtonStr}]);
116 | return;
117 | }
118 | }
119 |
120 | if ([action.title isEqualToString:[self.options valueForKey:@"takePhotoButtonTitle"]]) { // Take photo
121 | [self launchImagePicker:RNImagePickerTargetCamera];
122 | }
123 | else if ([action.title isEqualToString:[self.options valueForKey:@"chooseFromLibraryButtonTitle"]]) { // Choose from library
124 | [self launchImagePicker:RNImagePickerTargetLibrarySingleImage];
125 | }
126 | }
127 |
128 | - (void)launchImagePicker:(RNImagePickerTarget)target options:(NSDictionary *)options
129 | {
130 | self.options = options;
131 | [self launchImagePicker:target];
132 | }
133 |
134 | - (void)launchImagePicker:(RNImagePickerTarget)target
135 | {
136 | self.picker = [[UIImagePickerController alloc] init];
137 |
138 | if (target == RNImagePickerTargetCamera) {
139 | #if TARGET_IPHONE_SIMULATOR
140 | self.callback(@[@{@"error": @"Camera not available on simulator"}]);
141 | return;
142 | #else
143 | self.picker.sourceType = UIImagePickerControllerSourceTypeCamera;
144 | if ([[self.options objectForKey:@"cameraType"] isEqualToString:@"front"]) {
145 | self.picker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
146 | }
147 | else { // "back"
148 | self.picker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
149 | }
150 | #endif
151 | }
152 | else { // RNImagePickerTargetLibrarySingleImage
153 | self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
154 | }
155 |
156 | if ([[self.options objectForKey:@"mediaType"] isEqualToString:@"video"]
157 | || [[self.options objectForKey:@"mediaType"] isEqualToString:@"mixed"]) {
158 |
159 | if ([[self.options objectForKey:@"videoQuality"] isEqualToString:@"high"]) {
160 | self.picker.videoQuality = UIImagePickerControllerQualityTypeHigh;
161 | }
162 | else if ([[self.options objectForKey:@"videoQuality"] isEqualToString:@"low"]) {
163 | self.picker.videoQuality = UIImagePickerControllerQualityTypeLow;
164 | }
165 | else {
166 | self.picker.videoQuality = UIImagePickerControllerQualityTypeMedium;
167 | }
168 |
169 | id durationLimit = [self.options objectForKey:@"durationLimit"];
170 | if (durationLimit) {
171 | self.picker.videoMaximumDuration = [durationLimit doubleValue];
172 | self.picker.allowsEditing = NO;
173 | }
174 | }
175 | if ([[self.options objectForKey:@"mediaType"] isEqualToString:@"video"]) {
176 | self.picker.mediaTypes = @[(NSString *)kUTTypeFood];
177 | } else if ([[self.options objectForKey:@"mediaType"] isEqualToString:@"mixed"]) {
178 | self.picker.mediaTypes = @[(NSString *)kUTTypeFood, (NSString *)kUTTypeImage];
179 | } else {
180 | self.picker.mediaTypes = @[(NSString *)kUTTypeImage];
181 | }
182 |
183 | if ([[self.options objectForKey:@"allowsEditing"] boolValue]) {
184 | self.picker.allowsEditing = true;
185 | }
186 | self.picker.modalPresentationStyle = UIModalPresentationCurrentContext;
187 | self.picker.delegate = self;
188 |
189 | // Check permissions
190 | void (^showPickerViewController)() = ^void() {
191 | dispatch_async(dispatch_get_main_queue(), ^{
192 | UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
193 | while (root.presentedViewController != nil) {
194 | root = root.presentedViewController;
195 | }
196 | [root presentViewController:self.picker animated:YES completion:nil];
197 | });
198 | };
199 |
200 | if (target == RNImagePickerTargetCamera) {
201 | [self checkCameraPermissions:^(BOOL granted) {
202 | if (!granted) {
203 | self.callback(@[@{@"error": @"Camera permissions not granted"}]);
204 | return;
205 | }
206 |
207 | showPickerViewController();
208 | }];
209 | }
210 | else { // RNImagePickerTargetLibrarySingleImage
211 | [self checkPhotosPermissions:^(BOOL granted) {
212 | if (!granted) {
213 | self.callback(@[@{@"error": @"Photo library permissions not granted"}]);
214 | return;
215 | }
216 |
217 | showPickerViewController();
218 | }];
219 | }
220 | }
221 |
222 | - (NSString * _Nullable)originalFilenameForAsset:(PHAsset * _Nullable)asset assetType:(PHAssetResourceType)type {
223 | if (!asset) { return nil; }
224 |
225 | PHAssetResource *originalResource;
226 | // Get the underlying resources for the PHAsset (PhotoKit)
227 | NSArray *pickedAssetResources = [PHAssetResource assetResourcesForAsset:asset];
228 |
229 | // Find the original resource (underlying image) for the asset, which has the desired filename
230 | for (PHAssetResource *resource in pickedAssetResources) {
231 | if (resource.type == type) {
232 | originalResource = resource;
233 | }
234 | }
235 |
236 | return originalResource.originalFilename;
237 | }
238 |
239 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
240 | {
241 | dispatch_block_t dismissCompletionBlock = ^{
242 |
243 | NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenceURL];
244 | NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
245 |
246 | NSString *fileName;
247 | if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
248 | NSString *tempFileName = [[NSUUID UUID] UUIDString];
249 | if (imageURL && [[imageURL absoluteString] rangeOfString:@"ext=GIF"].location != NSNotFound) {
250 | fileName = [tempFileName stringByAppendingString:@".gif"];
251 | }
252 | else if ([[[self.options objectForKey:@"imageFileType"] stringValue] isEqualToString:@"png"]) {
253 | fileName = [tempFileName stringByAppendingString:@".png"];
254 | }
255 | else {
256 | fileName = [tempFileName stringByAppendingString:@".jpg"];
257 | }
258 | }
259 | else {
260 | NSURL *videoURL = info[UIImagePickerControllerMediaURL];
261 | fileName = videoURL.lastPathComponent;
262 | }
263 |
264 | // We default to path to the temporary directory
265 | NSString *path = [[NSTemporaryDirectory()stringByStandardizingPath] stringByAppendingPathComponent:fileName];
266 |
267 | // If storage options are provided, we use the documents directory which is persisted
268 | if ([self.options objectForKey:@"storageOptions"] && [[self.options objectForKey:@"storageOptions"] isKindOfClass:[NSDictionary class]]) {
269 | NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
270 |
271 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
272 | NSString *documentsDirectory = [paths objectAtIndex:0];
273 | path = [documentsDirectory stringByAppendingPathComponent:fileName];
274 |
275 | // Creates documents subdirectory, if provided
276 | if ([storageOptions objectForKey:@"path"]) {
277 | NSString *newPath = [documentsDirectory stringByAppendingPathComponent:[storageOptions objectForKey:@"path"]];
278 | NSError *error;
279 | [[NSFileManager defaultManager] createDirectoryAtPath:newPath withIntermediateDirectories:YES attributes:nil error:&error];
280 | if (error) {
281 | NSLog(@"Error creating documents subdirectory: %@", error);
282 | self.callback(@[@{@"error": error.localizedFailureReason}]);
283 | return;
284 | }
285 | else {
286 | path = [newPath stringByAppendingPathComponent:fileName];
287 | }
288 | }
289 | }
290 |
291 | // Create the response object
292 | self.response = [[NSMutableDictionary alloc] init];
293 |
294 | if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) { // PHOTOS
295 | UIImage *image;
296 | if ([[self.options objectForKey:@"allowsEditing"] boolValue]) {
297 | image = [info objectForKey:UIImagePickerControllerEditedImage];
298 | }
299 | else {
300 | image = [info objectForKey:UIImagePickerControllerOriginalImage];
301 | }
302 |
303 | if (imageURL) {
304 | PHAsset *pickedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[imageURL] options:nil].lastObject;
305 | NSString *originalFilename = [self originalFilenameForAsset:pickedAsset assetType:PHAssetResourceTypePhoto];
306 | self.response[@"fileName"] = originalFilename ?: [NSNull null];
307 | if (pickedAsset.location) {
308 | self.response[@"latitude"] = @(pickedAsset.location.coordinate.latitude);
309 | self.response[@"longitude"] = @(pickedAsset.location.coordinate.longitude);
310 | }
311 | if (pickedAsset.creationDate) {
312 | self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:pickedAsset.creationDate];
313 | }
314 | }
315 |
316 | // GIFs break when resized, so we handle them differently
317 | if (imageURL && [[imageURL absoluteString] rangeOfString:@"ext=GIF"].location != NSNotFound) {
318 | ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
319 | [assetsLibrary assetForURL:imageURL resultBlock:^(ALAsset *asset) {
320 | ALAssetRepresentation *rep = [asset defaultRepresentation];
321 | Byte *buffer = (Byte*)malloc(rep.size);
322 | NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
323 | NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
324 | [data writeToFile:path atomically:YES];
325 |
326 | NSMutableDictionary *gifResponse = [[NSMutableDictionary alloc] init];
327 | [gifResponse setObject:@(image.size.width) forKey:@"width"];
328 | [gifResponse setObject:@(image.size.height) forKey:@"height"];
329 |
330 | BOOL vertical = (image.size.width < image.size.height) ? YES : NO;
331 | [gifResponse setObject:@(vertical) forKey:@"isVertical"];
332 |
333 | if (![[self.options objectForKey:@"noData"] boolValue]) {
334 | NSString *dataString = [data base64EncodedStringWithOptions:0];
335 | [gifResponse setObject:dataString forKey:@"data"];
336 | }
337 |
338 | NSURL *fileURL = [NSURL fileURLWithPath:path];
339 | [gifResponse setObject:[fileURL absoluteString] forKey:@"uri"];
340 |
341 | NSNumber *fileSizeValue = nil;
342 | NSError *fileSizeError = nil;
343 | [fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:&fileSizeError];
344 | if (fileSizeValue){
345 | [gifResponse setObject:fileSizeValue forKey:@"fileSize"];
346 | }
347 |
348 | self.callback(@[gifResponse]);
349 | } failureBlock:^(NSError *error) {
350 | self.callback(@[@{@"error": error.localizedFailureReason}]);
351 | }];
352 | return;
353 | }
354 |
355 | image = [self fixOrientation:image]; // Rotate the image for upload to web
356 |
357 | // If needed, downscale image
358 | float maxWidth = image.size.width;
359 | float maxHeight = image.size.height;
360 | if ([self.options valueForKey:@"maxWidth"]) {
361 | maxWidth = [[self.options valueForKey:@"maxWidth"] floatValue];
362 | }
363 | if ([self.options valueForKey:@"maxHeight"]) {
364 | maxHeight = [[self.options valueForKey:@"maxHeight"] floatValue];
365 | }
366 | image = [self downscaleImageIfNecessary:image maxWidth:maxWidth maxHeight:maxHeight];
367 |
368 | NSData *data;
369 | if ([[[self.options objectForKey:@"imageFileType"] stringValue] isEqualToString:@"png"]) {
370 | data = UIImagePNGRepresentation(image);
371 | }
372 | else {
373 | data = UIImageJPEGRepresentation(image, [[self.options valueForKey:@"quality"] floatValue]);
374 | }
375 | [data writeToFile:path atomically:YES];
376 |
377 | if (![[self.options objectForKey:@"noData"] boolValue]) {
378 | NSString *dataString = [data base64EncodedStringWithOptions:0]; // base64 encoded image string
379 | [self.response setObject:dataString forKey:@"data"];
380 | }
381 |
382 | BOOL vertical = (image.size.width < image.size.height) ? YES : NO;
383 | [self.response setObject:@(vertical) forKey:@"isVertical"];
384 | NSURL *fileURL = [NSURL fileURLWithPath:path];
385 | NSString *filePath = [fileURL absoluteString];
386 | [self.response setObject:filePath forKey:@"uri"];
387 |
388 | // add ref to the original image
389 | NSString *origURL = [imageURL absoluteString];
390 | if (origURL) {
391 | [self.response setObject:origURL forKey:@"origURL"];
392 | }
393 |
394 | NSNumber *fileSizeValue = nil;
395 | NSError *fileSizeError = nil;
396 | [fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:&fileSizeError];
397 | if (fileSizeValue){
398 | [self.response setObject:fileSizeValue forKey:@"fileSize"];
399 | }
400 |
401 | [self.response setObject:@(image.size.width) forKey:@"width"];
402 | [self.response setObject:@(image.size.height) forKey:@"height"];
403 |
404 | NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
405 | if (storageOptions && [[storageOptions objectForKey:@"cameraRoll"] boolValue] == YES && self.picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
406 | ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
407 | if ([[storageOptions objectForKey:@"waitUntilSaved"] boolValue]) {
408 | [library writeImageToSavedPhotosAlbum:image.CGImage metadata:[info valueForKey:UIImagePickerControllerMediaMetadata] completionBlock:^(NSURL *assetURL, NSError *error) {
409 | if (error) {
410 | NSLog(@"Error while saving picture into photo album");
411 | } else {
412 | // when the image has been saved in the photo album
413 | if (assetURL) {
414 | PHAsset *capturedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL] options:nil].lastObject;
415 | NSString *originalFilename = [self originalFilenameForAsset:capturedAsset assetType:PHAssetResourceTypePhoto];
416 | self.response[@"fileName"] = originalFilename ?: [NSNull null];
417 | // This implementation will never have a location for the captured image, it needs to be added manually with CoreLocation code here.
418 | if (capturedAsset.creationDate) {
419 | self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:capturedAsset.creationDate];
420 | }
421 | }
422 | self.callback(@[self.response]);
423 | }
424 | }];
425 | } else {
426 | [library writeImageToSavedPhotosAlbum:image.CGImage metadata:[info valueForKey:UIImagePickerControllerMediaMetadata] completionBlock:nil];
427 | }
428 | }
429 | }
430 | else { // VIDEO
431 | NSURL *videoRefURL = info[UIImagePickerControllerReferenceURL];
432 | NSURL *videoURL = info[UIImagePickerControllerMediaURL];
433 | NSURL *videoDestinationURL = [NSURL fileURLWithPath:path];
434 |
435 | if (videoRefURL) {
436 | PHAsset *pickedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[videoRefURL] options:nil].lastObject;
437 | NSString *originalFilename = [self originalFilenameForAsset:pickedAsset assetType:PHAssetResourceTypeVideo];
438 | self.response[@"fileName"] = originalFilename ?: [NSNull null];
439 | if (pickedAsset.location) {
440 | self.response[@"latitude"] = @(pickedAsset.location.coordinate.latitude);
441 | self.response[@"longitude"] = @(pickedAsset.location.coordinate.longitude);
442 | }
443 | if (pickedAsset.creationDate) {
444 | self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:pickedAsset.creationDate];
445 | }
446 | }
447 |
448 | if ([videoURL.URLByResolvingSymlinksInPath.path isEqualToString:videoDestinationURL.URLByResolvingSymlinksInPath.path] == NO) {
449 | NSFileManager *fileManager = [NSFileManager defaultManager];
450 |
451 | // Delete file if it already exists
452 | if ([fileManager fileExistsAtPath:videoDestinationURL.path]) {
453 | [fileManager removeItemAtURL:videoDestinationURL error:nil];
454 | }
455 |
456 | if (videoURL) { // Protect against reported crash
457 | NSError *error = nil;
458 | [fileManager moveItemAtURL:videoURL toURL:videoDestinationURL error:&error];
459 | if (error) {
460 | self.callback(@[@{@"error": error.localizedFailureReason}]);
461 | return;
462 | }
463 | }
464 | }
465 |
466 | [self.response setObject:videoDestinationURL.absoluteString forKey:@"uri"];
467 | if (videoRefURL.absoluteString) {
468 | [self.response setObject:videoRefURL.absoluteString forKey:@"origURL"];
469 | }
470 |
471 | NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
472 | if (storageOptions && [[storageOptions objectForKey:@"cameraRoll"] boolValue] == YES && self.picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
473 | ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
474 | [library writeVideoAtPathToSavedPhotosAlbum:videoDestinationURL completionBlock:^(NSURL *assetURL, NSError *error) {
475 | if (error) {
476 | self.callback(@[@{@"error": error.localizedFailureReason}]);
477 | return;
478 | } else {
479 | NSLog(@"Save video succeed.");
480 | if ([[storageOptions objectForKey:@"waitUntilSaved"] boolValue]) {
481 | if (assetURL) {
482 | PHAsset *capturedAsset = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL] options:nil].lastObject;
483 | NSString *originalFilename = [self originalFilenameForAsset:capturedAsset assetType:PHAssetResourceTypeVideo];
484 | self.response[@"fileName"] = originalFilename ?: [NSNull null];
485 | // This implementation will never have a location for the captured image, it needs to be added manually with CoreLocation code here.
486 | if (capturedAsset.creationDate) {
487 | self.response[@"timestamp"] = [[ImagePickerManager ISO8601DateFormatter] stringFromDate:capturedAsset.creationDate];
488 | }
489 | }
490 |
491 | self.callback(@[self.response]);
492 | }
493 | }
494 | }];
495 | }
496 | }
497 |
498 | // If storage options are provided, check the skipBackup flag
499 | if ([self.options objectForKey:@"storageOptions"] && [[self.options objectForKey:@"storageOptions"] isKindOfClass:[NSDictionary class]]) {
500 | NSDictionary *storageOptions = [self.options objectForKey:@"storageOptions"];
501 |
502 | if ([[storageOptions objectForKey:@"skipBackup"] boolValue]) {
503 | [self addSkipBackupAttributeToItemAtPath:path]; // Don't back up the file to iCloud
504 | }
505 |
506 | if ([[storageOptions objectForKey:@"waitUntilSaved"] boolValue] == NO ||
507 | [[storageOptions objectForKey:@"cameraRoll"] boolValue] == NO ||
508 | self.picker.sourceType != UIImagePickerControllerSourceTypeCamera)
509 | {
510 | self.callback(@[self.response]);
511 | }
512 | }
513 | else {
514 | self.callback(@[self.response]);
515 | }
516 | };
517 |
518 | dispatch_async(dispatch_get_main_queue(), ^{
519 | [picker dismissViewControllerAnimated:YES completion:dismissCompletionBlock];
520 | });
521 | }
522 |
523 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
524 | {
525 | dispatch_async(dispatch_get_main_queue(), ^{
526 | [picker dismissViewControllerAnimated:YES completion:^{
527 | self.callback(@[@{@"didCancel": @YES}]);
528 | }];
529 | });
530 | }
531 |
532 | #pragma mark - Helpers
533 |
534 | - (void)checkCameraPermissions:(void(^)(BOOL granted))callback
535 | {
536 | AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
537 | if (status == AVAuthorizationStatusAuthorized) {
538 | callback(YES);
539 | return;
540 | } else if (status == AVAuthorizationStatusNotDetermined){
541 | [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
542 | callback(granted);
543 | return;
544 | }];
545 | } else {
546 | callback(NO);
547 | }
548 | }
549 |
550 | - (void)checkPhotosPermissions:(void(^)(BOOL granted))callback
551 | {
552 | PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
553 | if (status == PHAuthorizationStatusAuthorized) {
554 | callback(YES);
555 | return;
556 | } else if (status == PHAuthorizationStatusNotDetermined) {
557 | [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
558 | if (status == PHAuthorizationStatusAuthorized) {
559 | callback(YES);
560 | return;
561 | }
562 | else {
563 | callback(NO);
564 | return;
565 | }
566 | }];
567 | }
568 | else {
569 | callback(NO);
570 | }
571 | }
572 |
573 | - (UIImage*)downscaleImageIfNecessary:(UIImage*)image maxWidth:(float)maxWidth maxHeight:(float)maxHeight
574 | {
575 | UIImage* newImage = image;
576 |
577 | // Nothing to do here
578 | if (image.size.width <= maxWidth && image.size.height <= maxHeight) {
579 | return newImage;
580 | }
581 |
582 | CGSize scaledSize = CGSizeMake(image.size.width, image.size.height);
583 | if (maxWidth < scaledSize.width) {
584 | scaledSize = CGSizeMake(maxWidth, (maxWidth / scaledSize.width) * scaledSize.height);
585 | }
586 | if (maxHeight < scaledSize.height) {
587 | scaledSize = CGSizeMake((maxHeight / scaledSize.height) * scaledSize.width, maxHeight);
588 | }
589 |
590 | // If the pixels are floats, it causes a white line in iOS8 and probably other versions too
591 | scaledSize.width = (int)scaledSize.width;
592 | scaledSize.height = (int)scaledSize.height;
593 |
594 | UIGraphicsBeginImageContext(scaledSize); // this will resize
595 | [image drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
596 | newImage = UIGraphicsGetImageFromCurrentImageContext();
597 | if (newImage == nil) {
598 | NSLog(@"could not scale image");
599 | }
600 | UIGraphicsEndImageContext();
601 |
602 | return newImage;
603 | }
604 |
605 | - (UIImage *)fixOrientation:(UIImage *)srcImg {
606 | if (srcImg.imageOrientation == UIImageOrientationUp) {
607 | return srcImg;
608 | }
609 |
610 | CGAffineTransform transform = CGAffineTransformIdentity;
611 | switch (srcImg.imageOrientation) {
612 | case UIImageOrientationDown:
613 | case UIImageOrientationDownMirrored:
614 | transform = CGAffineTransformTranslate(transform, srcImg.size.width, srcImg.size.height);
615 | transform = CGAffineTransformRotate(transform, M_PI);
616 | break;
617 |
618 | case UIImageOrientationLeft:
619 | case UIImageOrientationLeftMirrored:
620 | transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
621 | transform = CGAffineTransformRotate(transform, M_PI_2);
622 | break;
623 |
624 | case UIImageOrientationRight:
625 | case UIImageOrientationRightMirrored:
626 | transform = CGAffineTransformTranslate(transform, 0, srcImg.size.height);
627 | transform = CGAffineTransformRotate(transform, -M_PI_2);
628 | break;
629 | case UIImageOrientationUp:
630 | case UIImageOrientationUpMirrored:
631 | break;
632 | }
633 |
634 | switch (srcImg.imageOrientation) {
635 | case UIImageOrientationUpMirrored:
636 | case UIImageOrientationDownMirrored:
637 | transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
638 | transform = CGAffineTransformScale(transform, -1, 1);
639 | break;
640 |
641 | case UIImageOrientationLeftMirrored:
642 | case UIImageOrientationRightMirrored:
643 | transform = CGAffineTransformTranslate(transform, srcImg.size.height, 0);
644 | transform = CGAffineTransformScale(transform, -1, 1);
645 | break;
646 | case UIImageOrientationUp:
647 | case UIImageOrientationDown:
648 | case UIImageOrientationLeft:
649 | case UIImageOrientationRight:
650 | break;
651 | }
652 |
653 | CGContextRef ctx = CGBitmapContextCreate(NULL, srcImg.size.width, srcImg.size.height, CGImageGetBitsPerComponent(srcImg.CGImage), 0, CGImageGetColorSpace(srcImg.CGImage), CGImageGetBitmapInfo(srcImg.CGImage));
654 | CGContextConcatCTM(ctx, transform);
655 | switch (srcImg.imageOrientation) {
656 | case UIImageOrientationLeft:
657 | case UIImageOrientationLeftMirrored:
658 | case UIImageOrientationRight:
659 | case UIImageOrientationRightMirrored:
660 | CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.height,srcImg.size.width), srcImg.CGImage);
661 | break;
662 |
663 | default:
664 | CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.width,srcImg.size.height), srcImg.CGImage);
665 | break;
666 | }
667 |
668 | CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
669 | UIImage *img = [UIImage imageWithCGImage:cgimg];
670 | CGContextRelease(ctx);
671 | CGImageRelease(cgimg);
672 | return img;
673 | }
674 |
675 | - (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString
676 | {
677 | NSURL* URL= [NSURL fileURLWithPath: filePathString];
678 | if ([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]) {
679 | NSError *error = nil;
680 | BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
681 | forKey: NSURLIsExcludedFromBackupKey error: &error];
682 |
683 | if(!success){
684 | NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
685 | }
686 | return success;
687 | }
688 | else {
689 | NSLog(@"Error setting skip backup attribute: file not found");
690 | return @NO;
691 | }
692 | }
693 |
694 | #pragma mark - Class Methods
695 |
696 | + (NSDateFormatter * _Nonnull)ISO8601DateFormatter {
697 | static NSDateFormatter *ISO8601DateFormatter;
698 | static dispatch_once_t onceToken;
699 | dispatch_once(&onceToken, ^{
700 | ISO8601DateFormatter = [[NSDateFormatter alloc] init];
701 | NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
702 | ISO8601DateFormatter.locale = enUSPOSIXLocale;
703 | ISO8601DateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
704 | ISO8601DateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZZZZ";
705 | });
706 | return ISO8601DateFormatter;
707 | }
708 |
709 | @end
710 |
--------------------------------------------------------------------------------
/ios/foodapp/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/ios/foodapp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | foodapp
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSExceptionDomains
32 |
33 | localhost
34 |
35 | NSExceptionAllowsInsecureHTTPLoads
36 |
37 |
38 |
39 |
40 | NSCameraUsageDescription
41 |
42 | NSLocationWhenInUseUsageDescription
43 |
44 | NSMicrophoneUsageDescription
45 |
46 | NSPhotoLibraryUsageDescription
47 |
48 | UIAppFonts
49 |
50 | Entypo.ttf
51 | EvilIcons.ttf
52 | FontAwesome.ttf
53 | Foundation.ttf
54 | Ionicons.ttf
55 | MaterialIcons.ttf
56 | Octicons.ttf
57 | Zocial.ttf
58 | SimpleLineIcons.ttf
59 | Entypo.ttf
60 | EvilIcons.ttf
61 | FontAwesome.ttf
62 | Foundation.ttf
63 | Ionicons.ttf
64 | MaterialCommunityIcons.ttf
65 | MaterialIcons.ttf
66 | Octicons.ttf
67 | SimpleLineIcons.ttf
68 | Zocial.ttf
69 |
70 | UILaunchStoryboardName
71 | LaunchScreen
72 | UIRequiredDeviceCapabilities
73 |
74 | armv7
75 |
76 | UISupportedInterfaceOrientations
77 |
78 | UIInterfaceOrientationPortrait
79 | UIInterfaceOrientationLandscapeLeft
80 | UIInterfaceOrientationLandscapeRight
81 |
82 | UIViewControllerBasedStatusBarAppearance
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/ios/foodapp/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/foodappTests/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/foodappTests/foodappTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2015-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | #import
14 | #import
15 |
16 | #define TIMEOUT_SECONDS 600
17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
18 |
19 | @interface foodappTests : XCTestCase
20 |
21 | @end
22 |
23 | @implementation foodappTests
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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "foodapp",
3 | "version": "1.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node node_modules/react-native/local-cli/cli.js start",
7 | "lint": "esw index.ios.js index.android.js src utils",
8 | "lint:watch": "npm run lint -- --watch",
9 | "test": "jest"
10 | },
11 | "dependencies": {
12 | "axios": "^0.15.2",
13 | "crypto": "0.0.3",
14 | "eslint": "^3.9.1",
15 | "eslint-config-airbnb": "^13.0.0",
16 | "eslint-plugin-import": "^2.2.0",
17 | "eslint-plugin-jsx-a11y": "2.2.3",
18 | "eslint-plugin-react": "^6.6.0",
19 | "eslint-plugin-react-native": "^2.0.0",
20 | "eslint-watch": "2.1.14",
21 | "lodash": "^4.16.6",
22 | "moment": "^2.16.0",
23 | "numeral": "^1.5.3",
24 | "oauth-1.0a": "^2.1.0",
25 | "react": "^15.4.2",
26 | "react-native": "^0.42.3",
27 | "react-native-config": "^0.3.1",
28 | "react-native-crypto": "^2.0.1",
29 | "react-native-image-picker": "^0.26.3",
30 | "react-native-linear-gradient": "^2.0.0",
31 | "react-native-navigation": "v2.0.0-experimental.256",
32 | "react-native-randombytes": "^2.2.0",
33 | "react-native-scrollable-tab-view": "^0.7.3",
34 | "react-native-swiper": "^1.5.4",
35 | "react-native-vector-icons": "^4.0.0",
36 | "react-native-wordpress-editor": "^2.0.1",
37 | "react-redux": "^5.0.3",
38 | "redux": "^3.6.0",
39 | "redux-thunk": "^2.2.0",
40 | "stream": "0.0.2",
41 | "woocommerce": "^2.4.0"
42 | },
43 | "jest": {
44 | "preset": "jest-react-native"
45 | },
46 | "devDependencies": {
47 | "babel-jest": "16.0.0",
48 | "babel-preset-react-native": "1.9.0",
49 | "jest": "16.0.1",
50 | "jest-react-native": "16.0.0",
51 | "react-native-dotenv": "0.0.2",
52 | "react-test-renderer": "15.3.2",
53 | "reactotron-react-native": "^1.1.4",
54 | "redux-immutable-state-invariant": "^1.2.4",
55 | "redux-logger": "^2.7.4"
56 | },
57 | "description": "React Native WooCommerce Food App",
58 | "repository": {
59 | "type": "git",
60 | "url": "https://github.com/ntphuc/react-native-woocommerce-food.git"
61 | },
62 | "author": "Phuc Nguyen ",
63 | "license": "MIT",
64 | "homepage": "https://github.com/ntphuc/react-native-woocommerce-food.git"
65 | }
66 |
--------------------------------------------------------------------------------
/server.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/server.zip
--------------------------------------------------------------------------------
/src/app.android.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import React, { Component } from 'react';
3 | import { View } from 'react-native';
4 | import { Navigation } from 'react-native-navigation';
5 | import { Provider } from 'react-redux';
6 | import { registerScreens } from './screens';
7 |
8 | import { iconsMap, iconsLoaded } from './utils/AppIcons';
9 | import configureStore from './store/configureStore';
10 |
11 | const store = configureStore();
12 |
13 | registerScreens(store, Provider);
14 |
15 | const navigatorStyle = {
16 | navBarTranslucent: true,
17 | drawUnderNavBar: true,
18 | navBarBackgroundColor:'black',
19 | navBarTextColor: 'white',
20 | navBarButtonColor: 'white',
21 | statusBarTextColorScheme: 'light',
22 | drawUnderTabBar: true,
23 | screenBackgroundColor:'black'
24 | };
25 |
26 | class App extends Component {
27 | constructor(props) {
28 | super(props);
29 | iconsLoaded.then(() => {
30 | this.startApp();
31 | });
32 | }
33 |
34 | startApp() {
35 | Navigation.startTabBasedApp({
36 | tabs: [
37 | {
38 | label: 'Home',
39 | screen: 'foodapp.Foods',
40 | icon: iconsMap['ios-home-outline'],
41 | selectedIcon: iconsMap['ios-home'],
42 | title: 'Trang chủ',
43 | navigatorStyle,
44 | navigatorButtons: {
45 | rightButtons: [
46 |
47 | {
48 | title: 'Cart',
49 | id: 'cart',
50 | icon: iconsMap['ios-cart-outline']
51 | }
52 | ]
53 | }
54 | },
55 | {
56 | label: 'Search',
57 | screen: 'foodapp.Search',
58 | icon: iconsMap['ios-search-outline'],
59 | selectedIcon: iconsMap['ios-search'],
60 | title: 'Tìm kiếm',
61 | navigatorStyle,
62 | navigatorButtons: {
63 | rightButtons: [
64 |
65 |
66 | {
67 | title: 'Cart',
68 | id: 'cart',
69 | icon: iconsMap['ios-cart-outline']
70 | }
71 | ]
72 | }
73 | },
74 | {
75 | label: 'Contact',
76 | screen: 'foodapp.Contact',
77 | icon: iconsMap['ios-contact-outline'],
78 | selectedIcon: iconsMap['ios-contact'],
79 | title: 'Liên hệ',
80 | navigatorStyle,
81 | navigatorButtons: {
82 | rightButtons: [
83 |
84 |
85 | {
86 | title: 'Cart',
87 | id: 'cart',
88 | icon: iconsMap['ios-cart-outline']
89 | }
90 | ]
91 | }
92 | }
93 | ],
94 | tabsStyle: {
95 | tabBarButtonColor: 'white',
96 | tabBarSelectedButtonColor: 'white',
97 | tabBarBackgroundColor: 'black'
98 | }
99 | });
100 | }
101 | }
102 |
103 | export default App;
104 |
--------------------------------------------------------------------------------
/src/app.ios.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import React, { Component } from 'react';
3 | import { View } from 'react-native';
4 | import { Navigation } from 'react-native-navigation';
5 | import { Provider } from 'react-redux';
6 | import { registerScreens } from './screens';
7 |
8 | import { iconsMap, iconsLoaded } from './utils/AppIcons';
9 | import configureStore from './store/configureStore';
10 |
11 | const store = configureStore();
12 |
13 | registerScreens(store, Provider);
14 |
15 | const navigatorStyle = {
16 | navBarTranslucent: true,
17 | drawUnderNavBar: true,
18 | navBarBackgroundColor:'black',
19 | navBarTextColor: 'white',
20 | navBarButtonColor: 'white',
21 | statusBarTextColorScheme: 'light',
22 | drawUnderTabBar: true,
23 | screenBackgroundColor:'black'
24 | };
25 |
26 | class App extends Component {
27 | constructor(props) {
28 | super(props);
29 | iconsLoaded.then(() => {
30 | this.startApp();
31 | });
32 | }
33 |
34 | startApp() {
35 | Navigation.startTabBasedApp({
36 | tabs: [
37 | {
38 | label: 'Home',
39 | screen: 'foodapp.Foods',
40 | icon: iconsMap['ios-home-outline'],
41 | selectedIcon: iconsMap['ios-home'],
42 | title: 'Trang chủ',
43 | navigatorStyle,
44 | navigatorButtons: {
45 | rightButtons: [
46 |
47 | {
48 | title: 'Cart',
49 | id: 'cart',
50 | icon: iconsMap['ios-cart-outline']
51 | }
52 | ]
53 | }
54 | },
55 | {
56 | label: 'Search',
57 | screen: 'foodapp.Search',
58 | icon: iconsMap['ios-search-outline'],
59 | selectedIcon: iconsMap['ios-search'],
60 | title: 'Tìm kiếm',
61 | navigatorStyle,
62 | navigatorButtons: {
63 | rightButtons: [
64 |
65 |
66 | {
67 | title: 'Cart',
68 | id: 'cart',
69 | icon: iconsMap['ios-cart-outline']
70 | }
71 | ]
72 | }
73 | },
74 | {
75 | label: 'Contact',
76 | screen: 'foodapp.Contact',
77 | icon: iconsMap['ios-contact-outline'],
78 | selectedIcon: iconsMap['ios-contact'],
79 | title: 'Liên hệ',
80 | navigatorStyle,
81 | navigatorButtons: {
82 | rightButtons: [
83 |
84 |
85 | {
86 | title: 'Cart',
87 | id: 'cart',
88 | icon: iconsMap['ios-cart-outline']
89 | }
90 | ]
91 | }
92 | }
93 | ],
94 | tabsStyle: {
95 | tabBarButtonColor: 'white',
96 | tabBarSelectedButtonColor: 'white',
97 | tabBarBackgroundColor: 'black'
98 | }
99 | });
100 | }
101 | }
102 |
103 | export default App;
104 |
--------------------------------------------------------------------------------
/src/constants/actionTypes.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export const RETRIEVE_NOWPLAYING_FOODS_SUCCESS = 'RETRIEVE_NOWPLAYING_FOODS_SUCCESS';
4 | export const RETRIEVE_FOODS_SEARCH_RESULT_SUCCESS = 'RETRIEVE_FOODS_SEARCH_RESULT_SUCCESS';
5 | export const RETRIEVE_CATEGORIES_SUCCESS = 'RETRIEVE_CATEGORIES_SUCCESS';
6 | export const ADD_TO_CARDS_SUCCESS = 'ADD_TO_CARDS_SUCCESS';
7 |
--------------------------------------------------------------------------------
/src/constants/api.js:
--------------------------------------------------------------------------------
1 | import Config from 'react-native-config';
2 |
3 | //ios web url
4 | //export const WEB_URL = "http://localhost:8082";
5 | //android web url
6 | export const WEB_URL = "http://192.168.10.113:8082";
7 |
--------------------------------------------------------------------------------
/src/constants/constants.js:
--------------------------------------------------------------------------------
1 |
2 | export const PRICE_UNIT = '$';
3 | export const CARDS_KEY = '@CARDSSSS:key';
4 |
--------------------------------------------------------------------------------
/src/img/arrow-down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/src/img/arrow-down@2x.png
--------------------------------------------------------------------------------
/src/img/ios-arrow-down@1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/src/img/ios-arrow-down@1.png
--------------------------------------------------------------------------------
/src/img/ios-arrow-down@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/src/img/ios-arrow-down@2x.png
--------------------------------------------------------------------------------
/src/img/ios-arrow-down@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/src/img/ios-arrow-down@3x.png
--------------------------------------------------------------------------------
/src/img/ios-search@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/src/img/ios-search@1x.png
--------------------------------------------------------------------------------
/src/img/ios-search@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntphuc/react-native-woocommerce-food/b0bcde37bda58ef086afdf40c9acee1d5b4d9911/src/img/ios-search@2x.png
--------------------------------------------------------------------------------
/src/lib/WooCommerce/Api.js:
--------------------------------------------------------------------------------
1 | import WooCommerceAPI from "./WooCommerceAPI";
2 | import Constants from './Config';
3 |
4 | var Api = new WooCommerceAPI({
5 | url: Constants.URL.root,
6 | consumerKey: Constants.Keys.ConsumeKey,
7 | consumerSecret: Constants.Keys.ConsumerSecret,
8 | wp_api: true,
9 | version: 'wc/v1',
10 | queryStringAuth: true
11 | });
12 |
13 | export default Api;
14 |
--------------------------------------------------------------------------------
/src/lib/WooCommerce/Config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Constants = {
4 | URL: {
5 | root: 'http://harborcity.top/sfood'
6 | },
7 | Keys: {
8 | ConsumeKey: 'ck_85abe624e16dd56f30da1170e2b50f1291e27a41',
9 | ConsumerSecret: 'cs_85424c0bb696e0ea3a58d3a86623b5c0cddf6f75'
10 | }
11 | }
12 |
13 | module.exports = Constants;
14 |
--------------------------------------------------------------------------------
/src/modules/_global/Drawer.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import {
3 | Text,
4 | View,
5 | TouchableOpacity,
6 | ToastAndroid
7 | } from 'react-native';
8 | import Icon from 'react-native-vector-icons/Ionicons';
9 | import LinearGradient from 'react-native-linear-gradient';
10 |
11 | import styles from './styles/Drawer';
12 |
13 | class Drawer extends Component {
14 | constructor(props) {
15 | super(props);
16 |
17 | this._goToFoods = this._goToFoods.bind(this);
18 | this._openSearch = this._openSearch.bind(this);
19 | }
20 |
21 | _openSearch() {
22 | this._toggleDrawer();
23 | this.props.navigator.showModal({
24 | screen: 'foodapp.Search',
25 | title: 'Search'
26 | });
27 | }
28 |
29 | _goToFoods() {
30 | this._toggleDrawer();
31 | this.props.navigator.popToRoot({
32 | screen: 'foodapp.Foods'
33 | });
34 | }
35 |
36 | _toggleDrawer() {
37 | this.props.navigator.toggleDrawer({
38 | to: 'closed',
39 | side: 'left',
40 | animated: true
41 | });
42 | }
43 |
44 | render() {
45 | const iconSearch = ();
46 | const iconFoods = ();
47 | const iconTV = ();
48 | return (
49 |
50 |
51 |
52 |
53 |
54 | {iconSearch}
55 |
56 | Search
57 |
58 |
59 |
60 |
61 |
62 | {iconFoods}
63 |
64 | Foods
65 |
66 |
67 |
68 |
69 | {iconTV}
70 | ToastAndroid.show('Coming Soon!', ToastAndroid.SHORT)}>
71 | List Foods
72 |
73 |
74 |
75 |
76 | {/* 'v1.0.0' */}
77 |
78 |
79 |
80 | );
81 | }
82 | }
83 |
84 | Drawer.propTypes = {
85 | navigator: PropTypes.object
86 | };
87 |
88 | export default Drawer;
89 |
--------------------------------------------------------------------------------
/src/modules/_global/ProgressBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | View,
4 | ActivityIndicator,
5 | StyleSheet,
6 | Platform
7 | } from 'react-native';
8 |
9 | const ProgressBar = () => (
10 |
11 |
12 |
13 | );
14 |
15 | const styles = StyleSheet.create({
16 | progressBar: {
17 | flex: 1,
18 | justifyContent: 'center'
19 | }
20 | });
21 |
22 | export default ProgressBar;
23 |
--------------------------------------------------------------------------------
/src/modules/_global/scrollableTabView/Button.android.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import {
3 | TouchableNativeFeedback
4 | } from 'react-native';
5 |
6 |
7 | const Button = props => (
8 |
12 |
13 | {props.children}
14 |
15 | );
16 |
17 | Button.propTypes = {
18 | children: PropTypes.object
19 | };
20 |
21 | module.exports = Button;
--------------------------------------------------------------------------------
/src/modules/_global/scrollableTabView/Button.ios.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import {
3 | TouchableOpacity
4 | } from 'react-native';
5 |
6 | const Button = props => (
7 |
8 | {props.children}
9 |
10 | );
11 |
12 | Button.propTypes = {
13 | children: PropTypes.object
14 | };
15 |
16 | module.exports = Button;
--------------------------------------------------------------------------------
/src/modules/_global/scrollableTabView/DefaultTabBar.js:
--------------------------------------------------------------------------------
1 | /*
2 | * https://github.com/skv-headless/react-native-scrollable-tab-view/blob/master/DefaultTabBar.js
3 | */
4 |
5 | const React = require('react');
6 | const ReactNative = require('react-native');
7 | const {
8 | StyleSheet,
9 | Text,
10 | View,
11 | Animated,
12 | } = ReactNative;
13 | const Button = require('./Button');
14 |
15 | const DefaultTabBar = React.createClass({
16 | propTypes: {
17 | goToPage: React.PropTypes.func,
18 | activeTab: React.PropTypes.number,
19 | tabs: React.PropTypes.array,
20 | backgroundColor: React.PropTypes.string,
21 | activeTextColor: React.PropTypes.string,
22 | inactiveTextColor: React.PropTypes.string,
23 | textStyle: Text.propTypes.style,
24 | tabStyle: View.propTypes.style,
25 | renderTab: React.PropTypes.func,
26 | underlineStyle: View.propTypes.style,
27 | },
28 |
29 | getDefaultProps() {
30 | return {
31 | activeTextColor: 'navy',
32 | inactiveTextColor: 'black',
33 | backgroundColor: null,
34 | };
35 | },
36 |
37 | renderTabOption(name, page) {
38 | },
39 |
40 | renderTab(name, page, isTabActive, onPressHandler) {
41 | const { activeTextColor, inactiveTextColor, textStyle, } = this.props;
42 | const textColor = isTabActive ? activeTextColor : inactiveTextColor;
43 | const fontWeight = isTabActive ? 'bold' : 'normal';
44 |
45 | return ;
59 | },
60 |
61 | render() {
62 | const containerWidth = this.props.containerWidth;
63 | const numberOfTabs = this.props.tabs.length;
64 | const tabUnderlineStyle = {
65 | position: 'absolute',
66 | width: containerWidth / numberOfTabs,
67 | height: 4,
68 | backgroundColor: 'navy',
69 | bottom: 0,
70 | };
71 |
72 | const left = this.props.scrollValue.interpolate({
73 | inputRange: [0, 1, ], outputRange: [0, containerWidth / numberOfTabs, ],
74 | });
75 | return (
76 |
77 | {this.props.tabs.map((name, page) => {
78 | const isTabActive = this.props.activeTab === page;
79 | const renderTab = this.props.renderTab || this.renderTab;
80 | return renderTab(name, page, isTabActive, this.props.goToPage);
81 | })}
82 |
83 |
84 | );
85 | },
86 | });
87 |
88 | const styles = StyleSheet.create({
89 | tab: {
90 | flex: 1,
91 | alignItems: 'center',
92 | justifyContent: 'center',
93 | paddingBottom: 10
94 | },
95 | tabs: {
96 | height: 50,
97 | flexDirection: 'row',
98 | justifyContent: 'space-around',
99 | borderWidth: 1,
100 | borderTopWidth: 0,
101 | borderLeftWidth: 0,
102 | borderRightWidth: 0,
103 | borderColor: '#141414',
104 | },
105 | });
106 |
107 | module.exports = DefaultTabBar;
--------------------------------------------------------------------------------
/src/modules/_global/styles/Drawer.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | paddingLeft: 25,
7 | justifyContent: 'center'
8 | },
9 | drawerList: {
10 |
11 | },
12 | drawerListIcon: {
13 | width: 27
14 | },
15 | drawerListItem: {
16 | flexDirection: 'row',
17 | alignItems: 'center',
18 | marginBottom: 23
19 | },
20 | drawerListItemText: {
21 | color: 'white',
22 | fontWeight: 'bold',
23 | fontSize: 23,
24 | paddingLeft: 15,
25 | flex: 1
26 | },
27 | linearGradient: {
28 | // top: 0,
29 | // left: 0,
30 | // right: 0,
31 | // height: 248,
32 | // position: 'absolute'
33 | flex: 1
34 | },
35 | _version: {
36 | color: '#3c3c3c',
37 | position: 'absolute',
38 | bottom: 25,
39 | marginLeft: 53
40 | }
41 | });
42 |
43 | export default styles;
--------------------------------------------------------------------------------
/src/modules/foods/Cart.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes,Component} from "react";
2 | import {View, Text, ScrollView, AsyncStorage, Platform} from "react-native";
3 | import ItemCart from "./components/ItemCart";
4 | import styles from './styles/Cart';
5 | import { PRICE_UNIT, CARDS_KEY } from '../../constants/constants';
6 | var mang=[];
7 | var c;
8 |
9 | export default class Cart extends Component{
10 | constructor(props){
11 | super(props);
12 | this.state={
13 | giohang:[],
14 | tt:0,
15 | };
16 | c=this;
17 | console.log("constructer card");
18 | this.props.navigator.setOnNavigatorEvent(this._onNavigatorEvent.bind(this));
19 | }
20 | _onNavigatorEvent(event) {
21 | if (event.type === 'NavBarButtonPress') {
22 | if (event.id === 'search') {
23 | let rightButtons = [];
24 | if (Platform.OS === 'ios') {
25 | rightButtons = [
26 | {
27 | id: 'close',
28 | title: 'Close',
29 | icon: iconsMap['ios-close']
30 | }
31 | ];
32 | }
33 | this.props.navigator.showModal({
34 | screen: 'foodapp.Search',
35 | title: 'Search',
36 | navigatorButtons: {
37 | rightButtons
38 | }
39 | });
40 | }
41 | }
42 | }
43 | plusItem(i){
44 | console.log(i);
45 | var stt = mang.findIndex((e)=>{
46 | return e.id ==i
47 | });
48 | mang[stt]['soluong'] += 1;
49 | c.saveGioHang().done();
50 | this.loadGioHang().done();
51 | }
52 |
53 | saveGioHang = async()=>{
54 | try{
55 | await AsyncStorage.setItem(CARDS_KEY, JSON.stringify(mang))
56 | }catch(e){
57 | console.log(e);
58 | }
59 | }
60 |
61 | loadGioHang=async()=>{
62 |
63 | var v = await AsyncStorage.getItem(CARDS_KEY);
64 | console.log("xxxxxxxxxxxx");
65 | console.log(v);
66 | if(v!==null){
67 | mang = JSON.parse(v);
68 |
69 | var tong = 0;
70 | mang.map(function(o, i){
71 | tong = tong + o.soluong * parseInt(o.price);
72 | });
73 | this.setState({
74 | tt:tong
75 | })
76 |
77 | console.log(mang);
78 | this.setState({
79 | giohang: mang
80 | });
81 | }
82 |
83 |
84 | }
85 |
86 |
87 | render(){
88 | // console.log("render render");
89 | // this.loadGioHang().done();
90 | return(
91 |
92 |
93 |
94 | {(this.state.giohang.length==0)?
95 | Chưa có sản phẩm nào trong giỏ hàng:
96 | this.state.giohang.map(function(o, i){
97 | return {c.plusItem(e)}}
104 | />;
105 | })
106 |
107 |
108 | }
109 |
110 |
111 |
112 | Tong tien: {this.state.tt}
113 |
114 |
115 | );
116 | }
117 |
118 | componentDidMount(){
119 | console.log("++++++++++++++");
120 | this.loadGioHang().done();
121 | }
122 | }
123 |
124 | Cart.propTypes = {
125 |
126 | navigator: PropTypes.object
127 | };
128 |
--------------------------------------------------------------------------------
/src/modules/foods/Contact.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {View, Text,StyleSheet} from "react-native";
3 | import CardContact from './components/CardContact';
4 | export default class Contact extends Component{
5 | render(){
6 | return(
7 |
8 |
9 |
10 | );
11 | }
12 | }
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | backgroundColor: 'black',
17 | paddingTop: 64,
18 | marginBottom:50
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/src/modules/foods/CreatePost.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {
3 | AppRegistry,
4 | StyleSheet,
5 | Text,
6 | View,
7 | PixelRatio,
8 | TouchableOpacity,
9 | Image,
10 | } from 'react-native';
11 | import ImagePicker from 'react-native-image-picker';
12 |
13 | export default class CreatePost extends Component{
14 | constructor(props) {
15 | super(props);
16 | this.state = {
17 | avatarSource: null
18 | };
19 |
20 | this.props.navigator.setOnNavigatorEvent(this._onNavigatorEvent.bind(this));
21 | }
22 | _onNavigatorEvent(event) {
23 | if (event.type === 'NavBarButtonPress') {
24 | if (event.id === 'close') {
25 | this.props.navigator.dismissModal();
26 | }
27 | }
28 | }
29 | render(){
30 | return(
31 |
32 |
33 |
34 | { this.state.avatarSource === null ? Select a Photo :
35 |
36 | }
37 |
38 |
39 |
40 |
41 |
42 |
43 | );
44 | }
45 |
46 | selectPhotoTapped() {
47 | const options = {
48 | quality: 1.0,
49 | maxWidth: 500,
50 | maxHeight: 500,
51 | storageOptions: {
52 | skipBackup: true
53 | }
54 | };
55 |
56 | ImagePicker.showImagePicker(options, (response) => {
57 | console.log('Response = ', response);
58 |
59 | if (response.didCancel) {
60 | console.log('User cancelled photo picker');
61 | }
62 | else if (response.error) {
63 | console.log('ImagePicker Error: ', response.error);
64 | }
65 | else if (response.customButton) {
66 | console.log('User tapped custom button: ', response.customButton);
67 | }
68 | else {
69 | let source = { uri: response.uri };
70 |
71 | // You can also display the image using data:
72 | // let source = { uri: 'data:image/jpeg;base64,' + response.data };
73 |
74 | this.setState({
75 | avatarSource: source
76 | });
77 | }
78 | });
79 | }
80 |
81 |
82 | }
83 |
84 | const styles = StyleSheet.create({
85 | container: {
86 | flex: 1,
87 | justifyContent: 'center',
88 | alignItems: 'center',
89 | backgroundColor: '#F5FCFF'
90 | },
91 | avatarContainer: {
92 | borderColor: '#9B9B9B',
93 | borderWidth: 1 / PixelRatio.get(),
94 | justifyContent: 'center',
95 | alignItems: 'center'
96 | },
97 | avatar: {
98 | borderRadius: 75,
99 | width: 150,
100 | height: 150
101 | }
102 | });
103 |
--------------------------------------------------------------------------------
/src/modules/foods/DetailFood.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes } from 'react';
2 | import {
3 | Image,
4 | Text,
5 | TouchableOpacity,
6 | View,
7 | ScrollView,
8 | AsyncStorage,
9 | Platform
10 | } from 'react-native';
11 | import Icon from 'react-native-vector-icons/Ionicons';
12 | import LinearGradient from 'react-native-linear-gradient';
13 | import Swiper from 'react-native-swiper';
14 | import styles from './styles/DetailFood';
15 | import { iconsMap } from '../../utils/AppIcons';
16 | import { PRICE_UNIT,CARDS_KEY } from '../../constants/constants';
17 | const iconStar = ();
18 | var cards=[];
19 | var foodDTO={};
20 | export default class DetailFood extends Component{
21 | constructor(props){
22 | super(props);
23 | // this.state={
24 | // foodDTO:null
25 | // };
26 | c=this;
27 | this.props.navigator.setOnNavigatorEvent(this._onNavigatorEvent.bind(this));
28 | }
29 | _onNavigatorEvent(event) {
30 | if (event.type === 'NavBarButtonPress') {
31 | if (event.id === 'close') {
32 | this.props.navigator.dismissModal();
33 | }
34 | else if (event.id === 'cart') {
35 | let rightButtons = [];
36 | if (Platform.OS === 'ios') {
37 | rightButtons = [
38 | {
39 | id: 'close',
40 | title: 'Close',
41 | icon: iconsMap['ios-close']
42 | }
43 | ];
44 | }
45 | this.props.navigator.push({
46 | screen: 'foodapp.Cart',
47 | title: 'Giỏ hàng',
48 | navigatorButtons: {
49 |
50 | }
51 | });
52 | }
53 | }
54 | }
55 | componentWillMount() {
56 |
57 |
58 |
59 | foodDTO= this.clone(this.props.foodDTO);
60 | // Object.defineProperty(foodDTO, 'soluong', {
61 | // value: 0,
62 | // writable: true,
63 | // enumerable:true
64 | //
65 | // });
66 | foodDTO['soluong']=0;
67 |
68 |
69 | }
70 |
71 | getCards = async () => {
72 | try {
73 |
74 | v = await AsyncStorage.getItem(CARDS_KEY);
75 | if (v!=null){
76 | console.log('v=====', v);
77 | cards = JSON.parse(v);
78 | var isExist=false;
79 | console.log('list cards before', cards.length);
80 |
81 |
82 | var stt = cards.findIndex((e)=>{
83 | return e.id==foodDTO.id
84 | });
85 |
86 | if (stt==-1){
87 | //khong tim thay
88 |
89 | foodDTO['soluong']=1;
90 | cards.push(foodDTO);
91 | }else{
92 | cards[stt]['soluong'] += 1;
93 | }
94 |
95 | } else{
96 | console.log("is null",v);
97 |
98 | foodDTO['soluong']=1;
99 | cards.push(foodDTO);
100 | }
101 | console.log('new foodDTO', foodDTO['soluong']);
102 | console.log('list cards after', cards.length);
103 | console.log('content cards after', cards)
104 | this.setCards().done();
105 | }catch (error){
106 | console.log('getCards', error);
107 | }
108 | }
109 | setCards = async () => {
110 | try {
111 | await AsyncStorage.setItem(CARDS_KEY,JSON.stringify(cards));
112 | }catch (error){
113 | console.log('setCards', error);
114 | }
115 | }
116 | checkOut(foodDTO){
117 | console.log('checkout', foodDTO.title);
118 |
119 | try{
120 |
121 | this.getCards().done();
122 |
123 |
124 |
125 |
126 |
127 |
128 | }
129 | catch (error){
130 | console.log("test cards error",error);
131 | }
132 |
133 |
134 | }
135 |
136 | render(){
137 |
138 | return(
139 |
140 |
142 |
150 | {
151 | foodDTO.images.map((item, index) => (
152 |
153 |
154 |
155 |
156 | ))
157 | }
158 |
159 |
160 |
161 |
162 | {foodDTO.title}
163 | {foodDTO.categories}
164 |
165 |
166 |
167 | {iconStar}
168 | {foodDTO.average_rating}
169 |
170 |
171 |
172 | {foodDTO.price + PRICE_UNIT}
173 |
174 |
175 | this.checkOut(foodDTO)}>
176 |
177 | Đặt mua
178 |
179 |
180 |
181 | {foodDTO.description.replace(/<\/?[^>]+(>|$)/g, "")}
182 |
183 |
184 |
185 |
186 | );
187 | }
188 |
189 | clone(obj) {
190 | var copy;
191 |
192 | // Handle the 3 simple types, and null or undefined
193 | if (null == obj || "object" != typeof obj) return obj;
194 |
195 | // Handle Date
196 | if (obj instanceof Date) {
197 | copy = new Date();
198 | copy.setTime(obj.getTime());
199 | return copy;
200 | }
201 |
202 | // Handle Array
203 | if (obj instanceof Array) {
204 | copy = [];
205 | for (var i = 0, len = obj.length; i < len; i++) {
206 | copy[i] = this.clone(obj[i]);
207 | }
208 | return copy;
209 | }
210 |
211 | // Handle Object
212 | if (obj instanceof Object) {
213 | copy = {};
214 | for (var attr in obj) {
215 | if (obj.hasOwnProperty(attr)) copy[attr] = this.clone(obj[attr]);
216 | }
217 | return copy;
218 | }
219 |
220 | throw new Error("Unable to copy obj! Its type isn't supported.");
221 | }
222 | }
223 |
224 |
225 | DetailFood.propTypes = {
226 | foodDTO: PropTypes.object.isRequired
227 |
228 | };
229 |
230 | //export default DetailFood;
231 |
--------------------------------------------------------------------------------
/src/modules/foods/Foods.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react';
2 | import {
3 | RefreshControl,
4 | ScrollView,
5 | Text,
6 | TouchableOpacity,
7 | View,
8 | Platform,
9 | Linking,
10 | AsyncStorage
11 | } from 'react-native';
12 | import Icon from 'react-native-vector-icons/Ionicons';
13 | import Swiper from 'react-native-swiper';
14 | import { bindActionCreators } from 'redux';
15 | import { connect } from 'react-redux';
16 |
17 | import * as foodsActions from './foods.actions';
18 | import CardCategory from './components/CardCategory';
19 | import CardOne from './components/CardOne';
20 | import CardTwo from './components/CardTwo';
21 | import CardContact from './components/CardContact';
22 | import ProgressBar from '../_global/ProgressBar';
23 | import styles from './styles/Foods';
24 | import { iconsMap } from '../../utils/AppIcons';
25 | var context;
26 | const navigatorStyle = {
27 | navBarTranslucent: true,
28 | drawUnderNavBar: true,
29 | navBarBackgroundColor:'black',
30 | navBarTextColor: 'white',
31 | navBarButtonColor: 'white',
32 | statusBarTextColorScheme: 'light',
33 | drawUnderTabBar: true,
34 | screenBackgroundColor:'black'
35 | };
36 | class Foods extends Component {
37 | constructor(props) {
38 | super(props);
39 | context=this;
40 |
41 | this.state = {
42 | isLoading: true,
43 | isRefreshing: false
44 | };
45 | this.taoGiohang().done();
46 | this._viewFood = this._viewFood.bind(this);
47 | this._viewFoodsList = this._viewFoodsList.bind(this);
48 | this._onRefresh = this._onRefresh.bind(this);
49 | this.props.navigator.setOnNavigatorEvent(this._onNavigatorEvent.bind(this));
50 | }
51 | //xu ly: TAO MANG GIO HANG
52 | taoGiohang = async()=>{
53 | try{
54 | var mang=[
55 | new ITEM("Ionic Framework vs React Native | 24x7Developers",1, 100, 44, "http://static.lazada.vn/cms/2015/campaign-06/seo/ip5-v2.png"),
56 | new ITEM("iPhone 3",1, 100, 45, "http://static.lazada.vn/cms/2015/campaign-06/seo/ip5-v2.png"),
57 | new ITEM("iPhone 3",1, 100, 46, "http://static.lazada.vn/cms/2015/campaign-06/seo/ip5-v2.png"),
58 | new ITEM("iPhone 3",1, 100, 47, "http://static.lazada.vn/cms/2015/campaign-06/seo/ip5-v2.png"),
59 | new ITEM("iPhone 3",1, 100, 48, "http://static.lazada.vn/cms/2015/campaign-06/seo/ip5-v2.png")
60 | ];
61 | await AsyncStorage.setItem("@GIOHANG1:key", JSON.stringify(mang))
62 | }catch(e){
63 | console.log(e);
64 | }
65 | }
66 | componentWillMount() {
67 | this._retrieveFoods();
68 | }
69 |
70 | componentWillReceiveProps(nextProps) {
71 | if (nextProps.nowPlayingFoods && nextProps.categories) {
72 | this.setState({ isLoading: false });
73 | }
74 | }
75 |
76 | _retrieveFoods(isRefreshed) {
77 | console.log("_retrieveFoods"," is runnning");
78 | this.props.actions.retrieveCategories();
79 | this.props.actions.retrieveNowPlayingFoods();
80 | if (isRefreshed && this.setState({ isRefreshing: false }));
81 | }
82 |
83 | _viewFoodsList(info, listByCat) {
84 | let rightButtons = [];
85 | if (Platform.OS === 'ios') {
86 | rightButtons = [
87 | {
88 | id: 'close',
89 | title: 'Close',
90 | icon: iconsMap['ios-close']
91 | }
92 | ];
93 | }
94 | this.props.navigator.showModal({
95 | title: 'Category',
96 | screen: 'foodapp.FoodsList',
97 | passProps: {
98 | info,
99 | listByCat
100 | },
101 | navigatorButtons: {
102 | rightButtons
103 | }
104 | });
105 | }
106 |
107 | _viewFood(foodDTO) {
108 | this.props.navigator.push({
109 | screen: 'foodapp.DetailFood',
110 | title: 'Chi tiết sản phẩm',
111 | passProps: {
112 | foodDTO
113 | },
114 | navigatorStyle,
115 | navigatorButtons: {
116 | rightButtons: [
117 |
118 |
119 | {
120 | title: 'Cart',
121 | id: 'cart',
122 | icon: iconsMap['ios-cart-outline']
123 | }
124 | ]
125 | }
126 | });
127 | }
128 |
129 | _onRefresh() {
130 | this.setState({ isRefreshing: true });
131 | this._retrieveFoods('isRefreshed');
132 | }
133 |
134 | _onNavigatorEvent(event) {
135 | if (event.type === 'NavBarButtonPress') {
136 | if (event.id === 'cart') {
137 | let rightButtons = [];
138 | if (Platform.OS === 'ios') {
139 | rightButtons = [
140 | {
141 | id: 'close',
142 | title: 'Close',
143 | icon: iconsMap['ios-close']
144 | }
145 | ];
146 | }
147 | this.props.navigator.push({
148 | screen: 'foodapp.Cart',
149 | title: 'Giỏ hàng',
150 | navigatorButtons: {
151 |
152 | }
153 | });
154 | }else if (event.id === 'create') {
155 | // let s = "Gửi thông tin cửa hàng";
156 | // let b = "Họ tên:\n Tên cửa hàng:\n Ngành nghề:\n Giới thiệu: \n SĐT: \n Đính kèm hình ảnh \n";
157 | // Linking.openURL('mailto:thienphuc0510@gmail.com?subject='+s+'&body='+b);
158 | this.props.navigator.push({
159 | screen: 'foodapp.Cart',
160 | title: 'Giỏ hàng',
161 |
162 | backButtonHidden: false,
163 |
164 | });
165 | //this.props.actions.addToCards(this.props.nowPlayingFoods[0]);
166 | // let rightButtons = [];
167 | // if (Platform.OS === 'ios') {
168 | // rightButtons = [
169 | // {
170 | // id: 'close',
171 | // title: 'Close',
172 | // icon: iconsMap['ios-close']
173 | // }
174 | // ];
175 | // }
176 | // this.props.navigator.showModal({
177 | // screen: 'foodapp.CreatePost',
178 | // title: 'CreatePost',
179 | // navigatorButtons: {
180 | // rightButtons
181 | // }
182 | // });
183 | }
184 | }
185 | }
186 | // listFeaturedFoods(){
187 | // return {nowPlayingFoods.map((info) => (
188 | // return ();
189 | // ))};
190 | // }
191 | render() {
192 | const { categories, nowPlayingFoods } = this.props;
193 | const iconPlay = ;
194 | const iconTop = ;
195 | const iconUp = ;
196 |
197 | return (
198 | this.state.isLoading ? :
199 |
211 | }>
212 |
217 |
218 | {nowPlayingFoods.map(info => (
219 | ))}
220 |
221 |
222 |
223 | {categories.map(function(info) {
224 | var listByCat=[];
225 | nowPlayingFoods.map(function(e){
226 | //console.log ('categories.map',info);
227 | if (e.categories.indexOf(info.name) !== -1){
228 | listByCat.push(e);
229 | // console.log ('nowPlayingFoods.map',e);
230 | }
231 | })
232 | return ();
234 | })}
235 |
236 |
237 |
238 |
239 | );
240 | }
241 | }
242 |
243 | Foods.propTypes = {
244 | actions: PropTypes.object.isRequired,
245 | categories: PropTypes.array.isRequired,
246 | nowPlayingFoods: PropTypes.array.isRequired,
247 | navigator: PropTypes.object
248 | };
249 |
250 | function mapStateToProps(state, ownProps) {
251 | return {
252 | categories: state.foods.categories,
253 | nowPlayingFoods: state.foods.nowPlayingFoods
254 | };
255 | }
256 |
257 | function mapDispatchToProps(dispatch) {
258 | return {
259 | actions: bindActionCreators(foodsActions, dispatch)
260 | };
261 | }
262 |
263 | function ITEM(n, s, d, i, h ){
264 | this.NAME=n
265 | this.SOLUONG=s
266 | this.DONGIA=d
267 | this.IDSP=i
268 | this.HINH=h
269 | }
270 | export default connect(mapStateToProps, mapDispatchToProps)(Foods);
271 |
--------------------------------------------------------------------------------
/src/modules/foods/FoodsList.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react';
2 | import {
3 | Platform,
4 | View,
5 | ListView,
6 | RefreshControl,
7 | Text,
8 | ScrollView
9 | } from 'react-native';
10 | import { bindActionCreators } from 'redux';
11 | import { connect } from 'react-redux';
12 |
13 | import { TMDB_URL, TMDB_API_KEY } from '../../constants/api';
14 | import * as foodsActions from './foods.actions';
15 | import CardThree from './components/CardThree';
16 | import CardTwo from './components/CardTwo';
17 | import ProgressBar from '../_global/ProgressBar';
18 | import styles from './styles/FoodsList';
19 | import { iconsMap } from '../../utils/AppIcons';
20 |
21 | class FoodsList extends Component {
22 | constructor(props) {
23 | super(props);
24 |
25 |
26 | // this.state = {
27 | // dataSource: ds.cloneWithRows([]),
28 | // };
29 |
30 | this._viewFood = this._viewFood.bind(this);
31 | this._onRefresh = this._onRefresh.bind(this);
32 | this.props.navigator.setOnNavigatorEvent(this._onNavigatorEvent.bind(this));
33 | }
34 |
35 |
36 |
37 |
38 |
39 | _viewFood(foodDTO) {
40 | this.props.navigator.push({
41 | screen: 'foodapp.DetailFood',
42 | title: 'Chi tiết sản phẩm',
43 | passProps: {
44 | foodDTO
45 | },
46 | backButtonHidden: false,
47 |
48 | });
49 | }
50 | componentWillMount() {
51 |
52 | const ds = new ListView.DataSource({
53 | rowHasChanged: (r1, r2) => r1 !== r2
54 | });
55 | console.log("list by cat",this.props.listByCat);
56 | this.setState({
57 | dataSource: ds.cloneWithRows(this.props.listByCat)
58 | });
59 | }
60 | _onRefresh() {
61 | this.setState({ isRefreshing: true });
62 | }
63 |
64 | _onNavigatorEvent(event) {
65 | if (event.type === 'NavBarButtonPress') {
66 | if (event.id === 'close') {
67 | this.props.navigator.dismissModal();
68 | }
69 | }
70 | }
71 |
72 | render() {
73 |
74 | const {info,listByCat, categories, nowPlayingFoods}=this.props;
75 | return (
76 |
77 | }
82 | renderSeparator={(sectionId, rowId) => }
83 | />
84 |
85 |
86 | );
87 | }
88 | }
89 |
90 | FoodsList.propTypes = {
91 | actions: PropTypes.object.isRequired,
92 | info: PropTypes.object.isRequired,
93 | listByCat: PropTypes.array.isRequired,
94 | navigator: PropTypes.object
95 | };
96 |
97 | let navigatorStyle = {};
98 |
99 | if (Platform.OS === 'ios') {
100 | navigatorStyle = {
101 | navBarTranslucent: true,
102 | drawUnderNavBar: true
103 | };
104 | } else {
105 | navigatorStyle = {
106 | navBarBackgroundColor: '#0a0a0a'
107 | };
108 | }
109 |
110 | FoodsList.navigatorStyle = {
111 | ...navigatorStyle,
112 | statusBarColor: 'black',
113 | statusBarTextColorScheme: 'light',
114 | navBarTextColor: 'white',
115 | navBarButtonColor: 'white'
116 | };
117 |
118 | function mapStateToProps(state, ownProps) {
119 | return {
120 | categories: state.foods.categories,
121 | nowPlayingFoods: state.foods.nowPlayingFoods
122 | };
123 | }
124 |
125 | function mapDispatchToProps(dispatch) {
126 | return {
127 | actions: bindActionCreators(foodsActions, dispatch)
128 | };
129 | }
130 |
131 | export default connect(mapStateToProps, mapDispatchToProps)(FoodsList);
132 |
133 | //
135 | //
136 | //
137 | // {nowPlayingFoods.map(info => (
138 | // ))}
139 | //
140 |
--------------------------------------------------------------------------------
/src/modules/foods/Search.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react';
2 | import {
3 | View,
4 | ListView,
5 | TextInput
6 | } from 'react-native';
7 | import axios from 'axios';
8 | import { bindActionCreators } from 'redux';
9 | import { connect } from 'react-redux';
10 |
11 | import { WEB_URL } from '../../constants/api';
12 | import * as foodsActions from './foods.actions';
13 | import CardThree from './components/CardThree';
14 | import styles from './styles/Search';
15 | import { iconsMap } from '../../utils/AppIcons';
16 | var isEnd=false;
17 | class Search extends Component {
18 | constructor(props) {
19 | super(props);
20 | const ds = new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2 });
21 | // const dataSource = ds.cloneWithRows(nextProps.searchResults);
22 |
23 | this.state = {
24 | isLoading: true,
25 | currentPage: 1,
26 | dataSource: ds.cloneWithRows(['row 1', 'row 2']),
27 | query: null
28 | };
29 |
30 | this._viewFood = this._viewFood.bind(this);
31 | this._handleTextInput = this._handleTextInput.bind(this);
32 | this.props.navigator.setOnNavigatorEvent(this._onNavigatorEvent.bind(this));
33 | }
34 |
35 | _handleTextInput(event) {
36 | const query = event.nativeEvent.text;
37 | this.setState({ query });
38 | if (!query) this.setState({ query: '' });
39 |
40 | setTimeout(() => {
41 | if (query.length) {
42 | this.props.actions.retrieveFoodsSearchResults(this.state.query, 1);
43 |
44 | }
45 | }, 500);
46 | }
47 |
48 | componentWillReceiveProps(nextProps) {
49 |
50 | // const ds = new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2 });
51 | // const dataSource = ds.cloneWithRows(nextProps.searchResults);
52 | this.setState({
53 | dataSource:this.state.dataSource.cloneWithRows(nextProps.searchResults),
54 | isLoading: false
55 | });
56 |
57 | }
58 | _retrieveNextPage() {
59 | if (!isEnd) {
60 | this.setState({
61 | currentPage: this.state.currentPage + 1
62 | });
63 |
64 | let page;
65 | if (this.state.currentPage === 1) {
66 | page = 2;
67 | this.setState({ currentPage: 2 });
68 | } else {
69 | page = this.state.currentPage + 1;
70 | }
71 | fetch(WEB_URL+"/products?page="+page+"&filter[q]="+this.state.query)
72 | .then((response) => response.json())
73 | .then((responseData) => {
74 | //console.log("nowplaying data", responseData);
75 | const data = this.state.dataSource;
76 | const newData = responseData;
77 | if (newData.length==0)
78 | isEnd=true;
79 | newData.map((item, index) => data.push(item));
80 |
81 | this.setState({
82 | dataSource: this.state.dataSource.cloneWithRows(data)});
83 | });
84 |
85 | }
86 | }
87 |
88 | _viewFood(foodDTO) {
89 | this.props.navigator.push({
90 | screen: 'foodapp.DetailFood',
91 | title: 'Chi tiết sản phẩm',
92 | passProps: {
93 | foodDTO
94 | },
95 | backButtonHidden: false,
96 |
97 | });
98 | }
99 |
100 | _onNavigatorEvent(event) {
101 | if (event.type === 'NavBarButtonPress') {
102 | if (event.id === 'close') {
103 | this.props.navigator.dismissModal();
104 | }
105 | else if (event.id === 'cart') {
106 | let rightButtons = [];
107 | if (Platform.OS === 'ios') {
108 | rightButtons = [
109 | {
110 | id: 'close',
111 | title: 'Close',
112 | icon: iconsMap['ios-close']
113 | }
114 | ];
115 | }
116 | this.props.navigator.push({
117 | screen: 'foodapp.Cart',
118 | title: 'Giỏ hàng',
119 | navigatorButtons: {
120 |
121 | }
122 | });
123 | }
124 | }
125 | }
126 |
127 | _renderListView() {
128 | let listView;
129 | if (this.state.query && this.state.dataSource!=0 ) {
130 | listView = (
131 | }
136 | renderSeparator={(sectionId, rowId) => }
137 | />
138 | );
139 | } else {
140 | listView = ;
141 | }
142 |
143 | return listView;
144 | }
145 |
146 | render() {
147 | return (
148 |
149 |
150 |
151 |
158 |
159 |
160 | { !this.state.isLoading && this._renderListView() }
161 |
162 |
163 | );
164 | }
165 | }
166 |
167 | Search.propTypes = {
168 | actions: PropTypes.object.isRequired,
169 | searchResults: PropTypes.array.isRequired,
170 | navigator: PropTypes.object
171 | };
172 |
173 | Search.navigatorStyle = {
174 | statusBarColor: 'black',
175 | statusBarTextColorScheme: 'light',
176 | navBarBackgroundColor: '#0a0a0a',
177 | navBarTextColor: 'white',
178 | navBarButtonColor: 'white'
179 | };
180 |
181 | function mapStateToProps(state, ownProps) {
182 | return {
183 | searchResults: state.foods.searchResults
184 | };
185 | }
186 |
187 | function mapDispatchToProps(dispatch) {
188 | return {
189 | actions: bindActionCreators(foodsActions, dispatch)
190 | };
191 | }
192 |
193 | export default connect(mapStateToProps, mapDispatchToProps)(Search);
194 |
--------------------------------------------------------------------------------
/src/modules/foods/Temp.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {View, Text} from "react-native";
3 |
4 | export default class Cart extends Component{
5 | render(){
6 | return(
7 |
8 | Contact
9 |
10 | );
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/modules/foods/components/CardCategory.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import {
3 | Image,
4 | Text,
5 | TouchableOpacity,
6 | View,
7 | ScrollView
8 | } from 'react-native';
9 | import CardTwo from './CardTwo';
10 | import styles from '../styles/Foods';
11 | import * as foodsActions from '../foods.actions';
12 | //const { nowPlayingFoods } = this.props;
13 | const CardCategory = ({ info, listFoods, viewFoodsList, viewFood }) => (
14 |
15 |
16 | {info.name}
17 |
18 |
21 | Xem tất cả
22 |
23 |
24 |
25 |
26 |
27 | {
28 |
29 | listFoods.map(info => (
30 |
31 | ))
32 | }
33 |
34 |
35 | );
36 |
37 | CardCategory.propTypes = {
38 | info: PropTypes.object.isRequired,
39 | viewFood: PropTypes.func.isRequired,
40 | viewFoodsList: PropTypes.func.isRequired,
41 | listFoods: PropTypes.array.isRequired
42 | };
43 |
44 |
45 | export default CardCategory;
46 |
--------------------------------------------------------------------------------
/src/modules/foods/components/CardContact.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes } from 'react';
2 | import {
3 | Image,
4 | Text,
5 | TouchableOpacity,
6 | View,
7 | Linking
8 | } from 'react-native';
9 | import Icon from 'react-native-vector-icons/Ionicons';
10 | import LinearGradient from 'react-native-linear-gradient';
11 |
12 | import styles from './styles/CardOne';
13 |
14 | const iconStar = ();
15 |
16 | export default class CardContact extends Component{
17 | call (){
18 | Linking.openURL('tel:0969007688');
19 | }
20 | gotoWeb (){
21 | Linking.openURL('google.com.vn').catch(err => console.error('An error occurred', err));
22 | }
23 | email (){
24 |
25 | Linking.openURL('mailto:thienphuc0510@gmail.com').catch(err => console.error('An error occurred', err));
26 | }
27 | render(){
28 | //const { info, viewFood } = this.props;
29 |
30 | return(
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Quán ăn Việt Sub
39 |
40 |
41 | Ăn là nhớ, Ăn là ghiền
42 |
43 |
44 |
45 | {iconStar}
46 | 9.0
47 |
48 |
49 |
50 |
51 | 22 Lý Thái Tổ, P10, Q10, TPHCM
52 |
53 | this.gotoWeb()}>
54 |
55 | Website: google.com.vn
56 |
57 |
58 | this.email()}>
59 |
60 | Email: thienphuc0510@gmail.com
61 |
62 |
63 | this.call()}>
64 |
65 | 0906096096
66 |
67 |
68 |
69 |
70 |
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/modules/foods/components/CardOne.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes } from 'react';
2 | import {
3 | Image,
4 | Text,
5 | TouchableOpacity,
6 | View
7 | } from 'react-native';
8 | import Icon from 'react-native-vector-icons/Ionicons';
9 | import LinearGradient from 'react-native-linear-gradient';
10 |
11 | import styles from './styles/CardOne';
12 |
13 | const iconStar = ();
14 |
15 | export default class CardOne extends Component{
16 | render(){
17 | const { info, viewFood } = this.props;
18 |
19 | // if (info.better_featured_image!=null)
20 | // image_url = info.better_featured_image.media_details.sizes.thumbnail.source_url ;
21 |
22 | // try {
23 | // image_url = info.better_featured_image.media_details.sizes.thumbnail.source_url ;
24 | // }
25 | // catch(err) {
26 | //
27 | // }
28 | var cat = info.categories.map(c=>(c+" "));
29 | return(
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {info.title}
38 |
39 |
40 | {cat}
41 |
42 |
43 |
44 | {iconStar}
45 | {info.average_rating}
46 |
47 |
48 |
49 |
50 | {info.description.replace(/<\/?[^>]+(>|$)/g, "")}
51 |
52 |
53 |
54 | Xem chi tiết
55 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 | }
63 | // const CardOne = ({ info, viewFood }) => (
64 | //
65 | //
66 | //
67 | //
68 | //
69 | //
70 | //
71 | // {info.title.rendered}
72 | //
73 | //
74 | // Action
75 | //
76 | //
77 | //
78 | // {iconStar}
79 | // 8.9
80 | //
81 | //
82 | //
83 | //
84 | // {info.content.rendered}
85 | //
86 | //
87 | //
88 | // View Details
89 | //
90 | //
91 | //
92 | //
93 | //
94 | // );
95 |
96 | CardOne.propTypes = {
97 | info: PropTypes.object.isRequired,
98 | viewFood: PropTypes.func.isRequired
99 | };
100 |
101 | //export default CardOne;
102 |
--------------------------------------------------------------------------------
/src/modules/foods/components/CardThree.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable new-cap */
2 | import React, { PropTypes, Component } from 'react';
3 | import {
4 | Image,
5 | Text,
6 | TouchableOpacity,
7 | View
8 | } from 'react-native';
9 | import Icon from 'react-native-vector-icons/Ionicons';
10 | import { connect } from 'react-redux';
11 |
12 | import styles from './styles/CardThree';
13 |
14 | const iconStar = ;
15 |
16 | export default class CardThree extends Component {
17 |
18 | constructor(props) {
19 | super(props);
20 | }
21 |
22 | render() {
23 | const { info, viewFood } = this.props;
24 | console.log('card three', info);
25 | var cat = info.categories.map(c=>(c+" "));
26 | //cat= cat.slice(0,-1);
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
36 | {info.title}
37 |
38 |
39 | {cat}
40 |
41 |
42 |
43 | {iconStar}
44 | {info.average_rating}
45 |
46 |
47 |
48 | {info.description.replace(/<\/?[^>]+(>|$)/g, "")}
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | CardThree.propTypes = {
59 | info: PropTypes.object.isRequired,
60 | viewFood: PropTypes.func.isRequired
61 | };
62 |
--------------------------------------------------------------------------------
/src/modules/foods/components/CardTwo.js:
--------------------------------------------------------------------------------
1 | import React, { Component,PropTypes } from 'react';
2 | import {
3 | Image,
4 | Text,
5 | TouchableOpacity,
6 | View
7 | } from 'react-native';
8 |
9 | import styles from './styles/CardTwo';
10 | import { PRICE_UNIT } from '../../../constants/constants';
11 |
12 | export default class CardTwo extends Component{
13 | render(){
14 | const { info, viewFood } = this.props;
15 |
16 | return(
17 |
18 |
19 |
20 |
21 | {info.title}
22 | {info.price+ PRICE_UNIT}
23 |
24 |
25 |
26 | )
27 | }
28 | }
29 |
30 | CardTwo.propTypes = {
31 | info: PropTypes.object.isRequired,
32 | viewFood: PropTypes.func.isRequired
33 | };
34 |
--------------------------------------------------------------------------------
/src/modules/foods/components/ItemCart.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {View, Text, StyleSheet, Image, TouchableOpacity} from "react-native";
3 | import { PRICE_UNIT, CARDS_KEY } from '../../../constants/constants';
4 | export default class ItemCart extends Component{
5 | render(){
6 | return(
7 |
8 |
9 |
10 |
11 |
12 | {this.props.NAME}
13 |
14 |
15 |
16 | -
17 |
18 | {this.props.SOLUONG}
19 | {this.props.plusItem(this.props.IDSP)}}>
20 | +
21 |
22 |
23 | {this.props.SOLUONG * this.props.DONGIA+PRICE_UNIT}
24 |
25 |
26 | );
27 | }
28 |
29 | // render(){
30 | // return(
31 | //
32 | // Contact
33 | //
34 | // );
35 | // }
36 | }
37 |
38 | var styles=StyleSheet.create({
39 | item:{flexDirection:"row", padding:10, borderWidth:0.5, borderColor:"gray",alignItems:'center'},
40 | img:{width:70, height:70},
41 | left:{flex:1},
42 | right:{flex:5, flexDirection:"column"},
43 | ten:{flex:4,marginLeft:20},
44 | ngang:{flex:1,flexDirection:"row"}
45 | });
46 |
--------------------------------------------------------------------------------
/src/modules/foods/components/styles/CardOne.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | linearGradient: {
5 | top: 0,
6 | left: 0,
7 | right: 0,
8 | height: 248,
9 | position: 'absolute'
10 | },
11 | imageBackdrop: {
12 | // flex: 1,
13 | height: 248,
14 | backgroundColor: 'black'
15 | },
16 | cardContainer: {
17 | position: 'absolute',
18 | top: 32,
19 | right: 16,
20 | left: 16,
21 | flexDirection: 'row'
22 | },
23 | cardImage: {
24 | height: 184,
25 | width: 135,
26 | borderRadius: 3
27 | },
28 | cardDetails: {
29 | paddingLeft: 10,
30 | flex: 1
31 | },
32 | cardTitle: {
33 | color: 'white',
34 | fontSize: 19,
35 | fontWeight: '500'
36 | },
37 | cardGenre: {
38 | flexDirection: 'row'
39 | },
40 | cardGenreItem: {
41 | fontSize: 11,
42 | marginRight: 5,
43 | color: 'white'
44 | },
45 | cardDescription: {
46 | color: '#f7f7f7',
47 | fontSize: 13,
48 | marginTop: 5
49 | },
50 | cardNumbers: {
51 | flexDirection: 'row',
52 | marginTop: 5
53 | },
54 | cardStar: {
55 | flexDirection: 'row'
56 | },
57 | cardStarRatings: {
58 | marginLeft: 5,
59 | fontSize: 12,
60 | color: 'white'
61 | },
62 | cardRunningHours: {
63 | marginLeft: 5,
64 | fontSize: 12
65 | },
66 | viewButton: {
67 | justifyContent: 'center',
68 | padding: 10,
69 | borderRadius: 3,
70 | backgroundColor: '#EA0000',
71 | width: 120,
72 | height: 30,
73 | marginTop: 10,
74 |
75 | },
76 | viewButtonText: {
77 | color: 'white',
78 | textAlign: 'center'
79 | }
80 | });
81 |
82 | export default styles;
83 |
--------------------------------------------------------------------------------
/src/modules/foods/components/styles/CardThree.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | cardContainer: {
5 | flex: 1,
6 | marginHorizontal: 16
7 | },
8 | card: {
9 | backgroundColor: 'white',
10 | borderRadius: 3,
11 | minHeight: 148,
12 | flexDirection: 'row',
13 | paddingRight: 16,
14 | overflow: 'hidden'
15 | },
16 | cardDetails: {
17 | paddingLeft: 10,
18 | flex: 1
19 | },
20 | cardImage: {
21 | height: 163,
22 | width: 120,
23 | borderTopLeftRadius: 3,
24 | borderBottomLeftRadius: 3
25 | },
26 | cardTitle: {
27 | color: 'black',
28 | fontSize: 13,
29 | fontWeight: '500',
30 | paddingTop: 10
31 | },
32 | cardGenre: {
33 | flexDirection: 'row',
34 | marginTop: 5
35 | },
36 | cardGenreItem: {
37 | fontSize: 11,
38 | marginRight: 5
39 | },
40 | cardDescription: {
41 | color: '#636363',
42 | fontSize: 13,
43 | marginTop: 5
44 | },
45 | cardNumbers: {
46 | flexDirection: 'row',
47 | marginTop: 5
48 | },
49 | cardStar: {
50 | flexDirection: 'row'
51 | },
52 | cardStarRatings: {
53 | marginLeft: 5,
54 | fontSize: 12
55 | },
56 | cardRunningHours: {
57 | marginLeft: 5,
58 | fontSize: 12
59 | }
60 | });
61 |
62 | export default styles;
63 |
--------------------------------------------------------------------------------
/src/modules/foods/components/styles/CardTwo.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | cardContainer: {
5 | height: 231,
6 | width: 135,
7 | backgroundColor: 'white',
8 | flexDirection: 'column',
9 | marginRight: 10,
10 | borderRadius: 3
11 | },
12 | cardImage: {
13 | width: 135,
14 | height: 184,
15 | borderTopLeftRadius: 3,
16 | borderTopRightRadius: 3
17 | },
18 | cardTitleContainer: {
19 | flex: 1,
20 | justifyContent: 'center',
21 | flexDirection: 'column'
22 | },
23 | cardTitle: {
24 | color: 'black',
25 | fontSize: 13,
26 | fontWeight: '500',
27 | textAlign: 'center',
28 | paddingHorizontal: 1
29 | },
30 | price: {
31 | color: 'red',
32 | fontSize: 13,
33 | fontWeight: '500',
34 | textAlign: 'center',
35 | paddingHorizontal: 1
36 | }
37 | });
38 |
39 | export default styles;
40 |
--------------------------------------------------------------------------------
/src/modules/foods/foods.actions.js:
--------------------------------------------------------------------------------
1 |
2 | import {AsyncStorage} from "react-native";
3 | import * as types from '../../constants/actionTypes';
4 | import { TMDB_URL, TMDB_API_KEY, WEB_URL, GET_POSTS } from '../../constants/api';
5 | import axios from 'axios';
6 |
7 |
8 | async function getItem(item) {
9 | try {
10 | const value = await AsyncStorage.getItem(item);
11 | console.log("hehe",value);
12 | return value;
13 | } catch (error) {
14 | // Handle errors here
15 | }
16 | }
17 |
18 | async function setItem(item, mang) {
19 | try{
20 | await AsyncStorage.setItem(item, JSON.stringify(mang))
21 | }catch(e){
22 | console.log(e);
23 | }
24 | }
25 |
26 | export function addToCardsSuccess(res) {
27 | return {
28 | type: types.ADD_TO_CARDS_SUCCESS,
29 | cards: res
30 | };
31 | }
32 |
33 | export function addToCards(foodDTO) {
34 | return function (dispatch) {
35 | try{
36 |
37 | // console.log("foodDTO",foodDTO);
38 | //var v = dispatch(getItem("@CARDS:key"));
39 | var v;
40 | async () => {
41 | try {
42 | v = await AsyncStorage.getItem("@CARDS:key");
43 | var mang=[];
44 |
45 | if (v==null || v.getRowCount()==0){
46 | console.log("is null",v);
47 | foodDTO["soluong"]=0;
48 | mang.push(foodDTO);
49 | }else{
50 | console.log("has phan tu",v);
51 | var isExist=false;
52 | mang = JSON.parse(v);
53 | mang.map(function(e){
54 | if (e.id==foodDTO.id){
55 | e["soluong"]=e["soluong"]+1;
56 | isExist=true;
57 | }
58 | });
59 | if (!isExist){
60 | foodDTO["soluong"]=0;
61 | mang.push(foodDTO);
62 | }
63 |
64 | }
65 | async (mang) => {
66 | try {
67 | await AsyncStorage.setItem("@CARDS:key",mang);
68 | console.log("mang return ",mang);
69 | dispatch(addToCardsSuccess(mang));
70 | } catch (error) {
71 | // Handle errors here
72 | }
73 | };
74 | } catch (error) {
75 | // Handle errors here
76 | }
77 | };
78 |
79 |
80 | //dispatch(setItem("@CARDS:key",JSON.stringify(mang)));
81 |
82 | // AsyncStorage.setItem('@CARDS2:key', JSON.stringify(mang));
83 |
84 | }
85 | catch (error){
86 | console.log("test cards error",error);
87 | }
88 |
89 |
90 | };
91 | }
92 |
93 |
94 |
95 |
96 |
97 | // GET CATEGORIES
98 | export function retrieveCategoriesSuccess(res) {
99 | return {
100 | type: types.RETRIEVE_CATEGORIES_SUCCESS,
101 | categories: res
102 | };
103 | }
104 |
105 | export function retrieveCategories() {
106 |
107 |
108 | return function (dispatch) {
109 | //console.log("url", 'http://harborcity.top/sfood/wp-json/wp/v2/categories');
110 |
111 | fetch(WEB_URL+"/products/categories")
112 | .then((response) => response.json())
113 | .then((responseData) => {
114 | //console.log("categories data", responseData);
115 | dispatch(retrieveCategoriesSuccess(responseData));
116 | });
117 |
118 | };
119 | }
120 | // NOW PLAYING
121 | export function retrieveNowPlayingFoodsSuccess(res) {
122 | return {
123 | type: types.RETRIEVE_NOWPLAYING_FOODS_SUCCESS,
124 | nowPlayingFoods: res
125 | };
126 | }
127 |
128 | export function retrieveNowPlayingFoods(page) {
129 |
130 |
131 | return function (dispatch) {
132 | //console.log("url", 'http://harborcity.top/sfood/wp-json/wp/v2/posts');
133 | fetch(WEB_URL+"/products")
134 | .then((response) => response.json())
135 | .then((responseData) => {
136 | //console.log("nowplaying data", responseData);
137 | dispatch(retrieveNowPlayingFoodsSuccess(responseData));
138 | });
139 |
140 | };
141 | }
142 |
143 |
144 |
145 | // SEARCH RESULTS
146 | export function retrieveFoodsSearchResultsSuccess(res) {
147 | return {
148 | type: types.RETRIEVE_FOODS_SEARCH_RESULT_SUCCESS,
149 | searchResults: res
150 | };
151 | }
152 |
153 | export function retrieveFoodsSearchResults(query, page) {
154 | return function (dispatch) {
155 | //console.log("url", 'http://harborcity.top/sfood/wp-json/wp/v2/posts');
156 | fetch(WEB_URL+"/products?page="+page+"&filter[q]="+query)
157 | .then((response) => response.json())
158 | .then((responseData) => {
159 | //console.log("nowplaying data", responseData);
160 | dispatch(retrieveFoodsSearchResultsSuccess(responseData));
161 | });
162 |
163 | };
164 | }
165 |
--------------------------------------------------------------------------------
/src/modules/foods/foods.reducer.js:
--------------------------------------------------------------------------------
1 | import * as types from '../../constants/actionTypes';
2 | import initialState from '../../reducers/initialState';
3 |
4 | export default function (state = initialState.foods, action) {
5 | switch (action.type) {
6 | case types.RETRIEVE_CATEGORIES_SUCCESS:
7 | return {
8 | ...state,
9 | categories: action.categories
10 | };
11 |
12 | case types.ADD_TO_CARDS_SUCCESS:
13 | return {
14 | ...state,
15 | cards: action.cards
16 | };
17 | case types.RETRIEVE_NOWPLAYING_FOODS_SUCCESS:
18 | return {
19 | ...state,
20 | nowPlayingFoods: action.nowPlayingFoods
21 | };
22 |
23 |
24 | case types.RETRIEVE_FOODS_SEARCH_RESULT_SUCCESS:
25 | return {
26 | ...state,
27 | searchResults: action.searchResults
28 | };
29 | default:
30 | return state;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/modules/foods/styles/Cart.js:
--------------------------------------------------------------------------------
1 | import { Platform, StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | backgroundColor: 'black',
6 | ...Platform.select({
7 | ios: {
8 | paddingTop: 64
9 | },
10 |
11 | }),
12 | ...Platform.select({
13 | ios: {
14 | marginBottom: 55
15 | },
16 | android: {
17 | marginBottom: 5
18 | }
19 | }),
20 | flex:1
21 | },
22 | progressBar: {
23 | backgroundColor: '#0a0a0a',
24 | flex: 1,
25 | justifyContent: 'center',
26 | alignItems: 'center'
27 | },
28 | listHeading: {
29 | paddingHorizontal: 16,
30 | flexDirection: 'row',
31 | justifyContent: 'space-between',
32 | alignItems: 'center',
33 | marginBottom: 15,
34 | marginTop: 30
35 | },
36 | listHeadingLeft: {
37 | color: 'white',
38 | fontWeight: 'bold',
39 | fontSize: 18
40 | },
41 | listHeadingRight: {
42 | color: 'white',
43 | ...Platform.select({
44 | ios: {
45 | fontSize: 15
46 | },
47 | android: {
48 | fontSize: 16
49 | }
50 | })
51 | },
52 | browseList: {
53 | marginTop: 30,
54 | paddingHorizontal: 16,
55 | ...Platform.select({
56 | ios: {
57 | marginBottom: 60
58 | },
59 | android: {
60 | marginBottom: 30
61 | }
62 | })
63 | },
64 | browseListItem: {
65 | ...Platform.select({
66 | ios: {
67 | paddingVertical: 8
68 | },
69 | android: {
70 | paddingVertical: 10
71 | }
72 | }),
73 | flexDirection: 'row'
74 | },
75 | browseListItemText: {
76 | flex: 1,
77 | color: 'white',
78 | paddingLeft: 10,
79 | ...Platform.select({
80 | ios: {
81 | fontSize: 15,
82 | fontWeight: '500'
83 | },
84 | android: {
85 | fontSize: 16,
86 | fontWeight: '100'
87 | }
88 | })
89 | }
90 | });
91 |
92 | export default styles;
93 |
--------------------------------------------------------------------------------
/src/modules/foods/styles/DetailFood.js:
--------------------------------------------------------------------------------
1 | import { Platform,StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | backgroundColor: 'black',
6 | paddingTop: 64,
7 | marginBottom:55
8 | },
9 | textStyle: {
10 | color: 'white',
11 | paddingTop: 10,
12 | fontSize: 12,
13 | fontWeight: 'bold'
14 | },
15 | underlineStyle: {
16 | backgroundColor: '#EA0000'
17 | },
18 | tabBar: {
19 | backgroundColor: '#131313'
20 | },
21 | contentContainer: {
22 | flex: 1,
23 | marginTop: 20,
24 | padding:10
25 | },
26 | progressBar: {
27 | backgroundColor: '#0a0a0a',
28 | flex: 1,
29 | justifyContent: 'center',
30 | alignItems: 'center'
31 | },
32 | container: {
33 | backgroundColor: '#0a0a0a'
34 | },
35 | swiper: {
36 | // position: 'absolute',
37 | // flex: 1
38 | },
39 | linearGradient: {
40 | top: 0,
41 | left: 0,
42 | right: 0,
43 | height: 248,
44 | position: 'absolute'
45 | },
46 | imageBackdrop: {
47 | // flex: 1,
48 | height: 248,
49 | backgroundColor: 'black'
50 | },
51 | cardContainer: {
52 | flex: 1,
53 | position: 'absolute',
54 | top: 200,
55 | right: 16,
56 | left: 16,
57 | flexDirection: 'row'
58 | },
59 | cardImage: {
60 | height: 184,
61 | width: 135,
62 | borderRadius: 3
63 | },
64 | cardDetails: {
65 | paddingLeft: 10,
66 | flex: 1,
67 | paddingTop: 50
68 | },
69 | cardTitle: {
70 | color: 'white',
71 | fontSize: 19,
72 | fontWeight: '500',
73 | paddingTop: 10
74 | },
75 | cardTagline: {
76 | color: 'white',
77 | fontSize: 15
78 | },
79 | cardGenre: {
80 | flexDirection: 'row'
81 | },
82 | cardGenreItem: {
83 | textAlign: 'left',
84 | fontSize: 11,
85 | marginRight: 5,
86 | color: 'white'
87 | },
88 | cardNumbers: {
89 | flexDirection: 'row',
90 | marginTop: 5
91 | },
92 | cardStar: {
93 | flexDirection: 'row'
94 | },
95 | cardStarRatings: {
96 | marginLeft: 5,
97 | fontSize: 12,
98 | color: 'white'
99 | },
100 | cardRunningHours: {
101 | marginLeft: 5,
102 | fontSize: 12
103 | },
104 | price: {
105 | marginLeft: 5,
106 | color: 'red',
107 | fontSize: 20,
108 | fontWeight: '500',
109 | paddingHorizontal: 1,
110 | marginTop: 10
111 | },
112 | viewButtonContainer: {
113 | flex:1,
114 | marginTop:150,
115 | flexDirection: 'row',
116 | justifyContent: 'center',
117 | alignItems:'center'
118 | },
119 | viewButtonText: {
120 | color: 'white',
121 | textAlign: 'center',
122 |
123 | padding: 5,
124 | borderRadius: 3,
125 | backgroundColor: '#EA0000',
126 | width: 150,
127 | height: 30
128 | }
129 | });
130 |
131 | export default styles;
132 |
--------------------------------------------------------------------------------
/src/modules/foods/styles/Food.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | textStyle: {
5 | color: 'white',
6 | paddingTop: 10,
7 | fontSize: 12,
8 | fontWeight: 'bold'
9 | },
10 | underlineStyle: {
11 | backgroundColor: '#EA0000'
12 | },
13 | tabBar: {
14 | backgroundColor: '#131313'
15 | },
16 | contentContainer: {
17 | flex: 1,
18 | marginTop: 157
19 | },
20 | progressBar: {
21 | backgroundColor: '#0a0a0a',
22 | flex: 1,
23 | justifyContent: 'center',
24 | alignItems: 'center'
25 | },
26 | container: {
27 | backgroundColor: '#0a0a0a'
28 | },
29 | swiper: {
30 | // position: 'absolute',
31 | // flex: 1
32 | },
33 | linearGradient: {
34 | top: 0,
35 | left: 0,
36 | right: 0,
37 | height: 248,
38 | position: 'absolute'
39 | },
40 | imageBackdrop: {
41 | // flex: 1,
42 | height: 248,
43 | backgroundColor: 'black'
44 | },
45 | cardContainer: {
46 | flex: 1,
47 | position: 'absolute',
48 | top: 200,
49 | right: 16,
50 | left: 16,
51 | flexDirection: 'row'
52 | },
53 | cardImage: {
54 | height: 184,
55 | width: 135,
56 | borderRadius: 3
57 | },
58 | cardDetails: {
59 | paddingLeft: 10,
60 | flex: 1,
61 | paddingTop: 50
62 | },
63 | cardTitle: {
64 | color: 'white',
65 | fontSize: 19,
66 | fontWeight: '500',
67 | paddingTop: 10
68 | },
69 | cardTagline: {
70 | color: 'white',
71 | fontSize: 15
72 | },
73 | cardGenre: {
74 | flexDirection: 'row'
75 | },
76 | cardGenreItem: {
77 | textAlign: 'left',
78 | fontSize: 11,
79 | marginRight: 5,
80 | color: 'white'
81 | },
82 | cardNumbers: {
83 | flexDirection: 'row',
84 | marginTop: 5
85 | },
86 | cardStar: {
87 | flexDirection: 'row'
88 | },
89 | cardStarRatings: {
90 | marginLeft: 5,
91 | fontSize: 12,
92 | color: 'white'
93 | },
94 | cardRunningHours: {
95 | marginLeft: 5,
96 | fontSize: 12
97 | }
98 | });
99 |
100 | export default styles;
101 |
--------------------------------------------------------------------------------
/src/modules/foods/styles/Foods.js:
--------------------------------------------------------------------------------
1 | import { Platform, StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | backgroundColor: 'black',
6 | paddingTop: 64,
7 | marginBottom:55
8 | },
9 | progressBar: {
10 | backgroundColor: '#0a0a0a',
11 | flex: 1,
12 | justifyContent: 'center',
13 | alignItems: 'center'
14 | },
15 | listHeading: {
16 | paddingHorizontal: 16,
17 | flexDirection: 'row',
18 | justifyContent: 'space-between',
19 | alignItems: 'center',
20 | marginBottom: 15,
21 | marginTop: 30
22 | },
23 | listHeadingLeft: {
24 | color: 'white',
25 | fontWeight: 'bold',
26 | fontSize: 18
27 | },
28 | listHeadingRight: {
29 | color: 'white',
30 | fontSize: 15
31 | },
32 | browseList: {
33 | marginTop: 30,
34 | paddingHorizontal: 16,
35 | marginBottom: 60
36 | },
37 | browseListItem: {
38 | paddingVertical: 8,
39 | flexDirection: 'row'
40 | },
41 | browseListItemText: {
42 | flex: 1,
43 | color: 'white',
44 | paddingLeft: 10,
45 | fontSize: 15,
46 | fontWeight: '500'
47 | }
48 | });
49 |
50 | export default styles;
51 |
--------------------------------------------------------------------------------
/src/modules/foods/styles/FoodsList.js:
--------------------------------------------------------------------------------
1 | import { Platform, StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | backgroundColor: 'black',
6 | paddingTop: 64,
7 | marginBottom:55
8 | },
9 | headerTitle: {
10 | backgroundColor: '#000000',
11 | flex: 1
12 | },
13 | textTitle: {
14 | color: '#ffffff',
15 | flex: 1
16 | },
17 | listview: {
18 | backgroundColor: '#0a0a0a',
19 | flex: 9
20 | },
21 | progressBar: {
22 | backgroundColor: '#0a0a0a',
23 | flex: 1,
24 | justifyContent: 'center',
25 | alignItems: 'center'
26 | },
27 | seperator: {
28 | marginTop: 10,
29 | backgroundColor: '#8E8E8E'
30 | }
31 | });
32 |
33 | export default styles;
34 |
--------------------------------------------------------------------------------
/src/modules/foods/styles/Search.js:
--------------------------------------------------------------------------------
1 | import { Platform, StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | backgroundColor: 'black',
7 | ...Platform.select({
8 | ios: {
9 | paddingTop: 64
10 | },
11 | android: {
12 | paddingTop: 64
13 | }
14 |
15 | }),
16 | ...Platform.select({
17 | ios: {
18 | marginBottom: 55
19 | },
20 | android: {
21 | marginBottom: 5
22 | }
23 | }),
24 | },
25 | textInput: {
26 | backgroundColor: 'white',
27 | ...Platform.select({
28 | ios: {
29 | height: 35
30 | },
31 | android: {
32 | height: 48
33 | }
34 | })
35 | },
36 | searchboxBorder: {
37 | borderRadius: 3,
38 | backgroundColor: 'white',
39 | paddingHorizontal: 3
40 | },
41 | searchbox: {
42 | backgroundColor: '#191919',
43 | paddingHorizontal: 16,
44 | paddingVertical: 8,
45 | marginBottom: 16
46 | },
47 | seperator: {
48 | marginTop: 10,
49 | backgroundColor: '#8E8E8E'
50 | }
51 | });
52 |
53 | export default styles;
54 |
--------------------------------------------------------------------------------
/src/reducers/initialState.js:
--------------------------------------------------------------------------------
1 | export default {
2 | foods: {
3 |
4 | categories: [],
5 | nowPlayingFoods: [],
6 | searchResults: [],
7 | cards: []
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/src/reducers/rootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import foods from '../modules/foods/foods.reducer';
3 |
4 | const rootReducer = combineReducers({
5 | foods
6 | });
7 |
8 | export default rootReducer;
9 |
--------------------------------------------------------------------------------
/src/screens.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 | import { Navigation } from 'react-native-navigation';
3 |
4 | import Drawer from './modules/_global/Drawer';
5 | import Foods from './modules/foods/Foods';
6 | import FoodsList from './modules/foods/FoodsList';
7 | import DetailFood from './modules/foods/DetailFood';
8 | import Search from './modules/foods/Search';
9 | import CreatePost from './modules/foods/CreatePost';
10 | import Cart from './modules/foods/Cart';
11 | import Contact from './modules/foods/Contact';
12 | export function registerScreens(store, Provider) {
13 | Navigation.registerComponent('foodapp.DetailFood', () => DetailFood, store, Provider);
14 | Navigation.registerComponent('foodapp.Foods', () => Foods, store, Provider);
15 | Navigation.registerComponent('foodapp.FoodsList', () => FoodsList, store, Provider);
16 | Navigation.registerComponent('foodapp.Cart', () => Cart, store, Provider);
17 | Navigation.registerComponent('foodapp.Contact', () => Contact, store, Provider);
18 | Navigation.registerComponent('foodapp.Search', () => Search, store, Provider);
19 | Navigation.registerComponent('foodapp.CreatePost', () => CreatePost, store, Provider);
20 | Navigation.registerComponent('foodapp.Drawer', () => Drawer);
21 | }
22 |
--------------------------------------------------------------------------------
/src/store/configureStore.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | /* eslint-disable no-undef */
3 | import { createStore, applyMiddleware } from 'redux';
4 | import thunk from 'redux-thunk';
5 | import rootReducer from '../reducers/rootReducer';
6 |
7 | let middleware = [thunk];
8 |
9 | // if (__DEV__) {
10 | // const reduxImmutableStateInvariant = require('redux-immutable-state-invariant')();
11 | // const createLogger = require('redux-logger');
12 | //
13 | // const logger = createLogger({ collapsed: true });
14 | // middleware = [...middleware, reduxImmutableStateInvariant, logger];
15 | // } else {
16 | // middleware = [...middleware];
17 | // }
18 |
19 | export default function configureStore(initialState) {
20 | return createStore(
21 | rootReducer,
22 | initialState,
23 | applyMiddleware(...middleware)
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/utils/AppIcons.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable new-cap */
2 | import { PixelRatio } from 'react-native';
3 | import Ionicons from 'react-native-vector-icons/Ionicons';
4 |
5 | const navIconSize = (__DEV__ === false && Platform.OS === 'android') ? PixelRatio.getPixelSizeForLayoutSize(40) : 40; // eslint-disable-line
6 | const replaceSuffixPattern = /--(active|big|small|very-big)/g;
7 | const icons = {
8 | 'ios-home-outline': [30],
9 | 'ios-home': [30],
10 | 'ios-contact-outline': [30],
11 | 'ios-contact': [30],
12 | 'ios-film-outline': [30],
13 | 'ios-film': [30],
14 | 'ios-desktop-outline': [30],
15 | 'ios-desktop': [30],
16 | 'ios-search': [30],
17 | 'ios-search-outline': [30],
18 | 'ios-cart': [30],
19 | 'ios-cart-outline': [30],
20 | 'ios-create-outline': [30],
21 | 'ios-arrow-round-down': [navIconSize],
22 | 'ios-close': [40]
23 | };
24 |
25 | const iconsMap = {};
26 | const iconsLoaded = new Promise((resolve, reject) => {
27 | new Promise.all(
28 | Object.keys(icons).map(iconName =>
29 | // IconName--suffix--other-suffix is just the mapping name in iconsMap
30 | Ionicons.getImageSource(
31 | iconName.replace(replaceSuffixPattern, ''),
32 | icons[iconName][0],
33 | icons[iconName][1]
34 | ))
35 | ).then(sources => {
36 | Object.keys(icons)
37 | .forEach((iconName, idx) => (iconsMap[iconName] = sources[idx]));
38 |
39 | // Call resolve (and we are done)
40 | resolve(true);
41 | });
42 | });
43 |
44 | export {
45 | iconsMap,
46 | iconsLoaded
47 | };
48 |
--------------------------------------------------------------------------------
/temp.txt:
--------------------------------------------------------------------------------
1 | // {nowPlayingFoods.map( function(info) {
2 | // console.log ("image url ", info.better_featured_image.media_details.sizes.medium.source_url);
3 | // return ();
4 | // })}
5 | this.state.giohang.map(function(o, i){
6 | return {c.plusItem(e)}}
13 | />;
14 | })
15 |
--------------------------------------------------------------------------------
/utils/reactotronConfig.js:
--------------------------------------------------------------------------------
1 | import Reactotron from 'reactotron-react-native';
2 |
3 | Reactotron
4 | .configure() // we can use plugins here -- more on this later
5 | .connect(); // let's connect!
--------------------------------------------------------------------------------