├── .watchmanconfig ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── fastlane ├── metadata │ └── android │ │ └── en-GB │ │ ├── video.txt │ │ ├── title.txt │ │ ├── full_description.txt │ │ └── short_description.txt ├── Appfile ├── update-app-version.sh ├── README.md └── Fastfile ├── src ├── tests │ ├── __mocks__ │ │ ├── react-native-tab-view.js │ │ ├── react-navigation-stack.js │ │ ├── react-native-reanimated.js │ │ ├── react-native-gesture-handler.js │ │ ├── @react-native-community │ │ │ └── async-storage.js │ │ └── react-redux.js │ ├── components │ │ ├── UI │ │ │ ├── Loading.test.js │ │ │ ├── __snapshots__ │ │ │ │ ├── Loading.test.js.snap │ │ │ │ ├── Spacer.test.js.snap │ │ │ │ ├── Header.test.js.snap │ │ │ │ ├── Messages.test.js.snap │ │ │ │ └── Error.test.js.snap │ │ │ ├── Spacer.test.js │ │ │ ├── Header.test.js │ │ │ ├── Error.test.js │ │ │ └── Messages.test.js │ │ └── Articles │ │ │ ├── List.test.js │ │ │ └── Single.test.js │ ├── lib │ │ ├── pagination.test.js │ │ ├── images.test.js │ │ ├── format-error-messages.test.js │ │ └── string.test.js │ └── models │ │ └── articles.test.js ├── models │ ├── index.js │ └── articles.js ├── images │ ├── launch.png │ └── app-icon.png ├── containers │ ├── index.js │ └── Articles │ │ ├── Single.js │ │ ├── Form.js │ │ └── List.js ├── components │ ├── UI │ │ ├── index.js │ │ ├── Spacer.js │ │ ├── Loading.js │ │ ├── Header.js │ │ ├── Messages.js │ │ └── Error.js │ ├── About.js │ └── Articles │ │ ├── Single.js │ │ ├── Form.js │ │ └── List.js ├── lib │ ├── pagination.js │ ├── images.js │ ├── format-error-messages.js │ ├── api.js │ └── string.js ├── constants │ ├── config.js │ ├── navigation.js │ └── messages.js ├── store │ ├── index.js │ └── articles.js ├── index.js └── routes │ └── index.js ├── app.json ├── babel.config.js ├── android ├── app │ ├── debug.keystore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ └── launch_screen.png │ │ │ │ └── layout │ │ │ │ │ └── launch_screen.xml │ │ │ ├── assets │ │ │ │ └── fonts │ │ │ │ │ ├── Entypo.ttf │ │ │ │ │ ├── Feather.ttf │ │ │ │ │ ├── Roboto.ttf │ │ │ │ │ ├── Zocial.ttf │ │ │ │ │ ├── AntDesign.ttf │ │ │ │ │ ├── EvilIcons.ttf │ │ │ │ │ ├── Fontisto.ttf │ │ │ │ │ ├── Ionicons.ttf │ │ │ │ │ ├── Octicons.ttf │ │ │ │ │ ├── FontAwesome.ttf │ │ │ │ │ ├── Foundation.ttf │ │ │ │ │ ├── MaterialIcons.ttf │ │ │ │ │ ├── Roboto_medium.ttf │ │ │ │ │ ├── SimpleLineIcons.ttf │ │ │ │ │ ├── rubicon-icon-font.ttf │ │ │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ │ │ └── MaterialCommunityIcons.ttf │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── reactnativestarterkit │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── AndroidManifest.xml │ │ └── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── reactnativestarterkit │ │ │ └── ReactNativeFlipper.java │ ├── proguard-rules.pro │ ├── build_defs.bzl │ └── _BUCK ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── settings.gradle ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── documentation ├── rnsk-logo.jpg ├── contributing.md ├── file-structure.md ├── testing.md ├── faqs.md └── deploy.md ├── ios ├── ReactNativeStarterKit │ ├── Images.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── 120.png │ │ │ ├── 180.png │ │ │ └── Contents.json │ │ └── LaunchScreen.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage2x.png │ │ │ ├── LaunchImage3x.png │ │ │ └── Contents.json │ ├── AppDelegate.h │ ├── main.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Info.plist │ ├── AppDelegate.m │ └── LaunchScreen.storyboard ├── ReactNativeStarterKit.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings ├── ReactNativeStarterKitTests │ ├── Info.plist │ └── ReactNativeStarterKitTests.m ├── Podfile └── ReactNativeStarterKit.xcodeproj │ └── xcshareddata │ └── xcschemes │ └── ReactNativeStarterKit.xcscheme ├── .buckconfig ├── .gitattributes ├── native-base-theme └── components │ ├── Picker.ios.js │ ├── Spinner.js │ ├── Switch.js │ ├── Tab.js │ ├── Body.js │ ├── Label.js │ ├── Left.js │ ├── Picker.js │ ├── Picker.android.js │ ├── Right.js │ ├── View.js │ ├── Icon.js │ ├── Content.js │ ├── H1.js │ ├── H2.js │ ├── H3.js │ ├── Text.js │ ├── Input.js │ ├── Container.js │ ├── Fab.js │ ├── Subtitle.js │ ├── Textarea.js │ ├── Title.js │ ├── Thumbnail.js │ ├── TabContainer.js │ ├── Card.js │ ├── Radio.js │ ├── Badge.js │ ├── Toast.js │ ├── SwipeRow.js │ ├── CheckBox.js │ ├── TabHeading.js │ ├── Separator.js │ ├── TabBar.js │ ├── Segment.js │ ├── Form.js │ ├── FooterTab.js │ ├── Footer.js │ ├── InputGroup.js │ ├── CardItem.js │ ├── index.js │ └── Item.js ├── .prettierrc.js ├── index.js ├── .editorconfig ├── App.js ├── metro.config.js ├── ISSUE_TEMPLATE.md ├── .gitignore ├── .eslintrc.js ├── LICENSE ├── .flowconfig ├── package.json └── README.md /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: mcnamee 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-GB/video.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-GB/title.txt: -------------------------------------------------------------------------------- 1 | ReactNativeStarterKit 2 | -------------------------------------------------------------------------------- /src/tests/__mocks__/react-native-tab-view.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /src/tests/__mocks__/react-navigation-stack.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /src/tests/__mocks__/react-native-reanimated.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-GB/full_description.txt: -------------------------------------------------------------------------------- 1 | ReactNativeStarterKit 2 | -------------------------------------------------------------------------------- /src/tests/__mocks__/react-native-gesture-handler.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-GB/short_description.txt: -------------------------------------------------------------------------------- 1 | ReactNativeStarterKit 2 | -------------------------------------------------------------------------------- /src/models/index.js: -------------------------------------------------------------------------------- 1 | export { default as articles } from './articles'; // eslint-disable-line 2 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactNativeStarterKit", 3 | "displayName": "ReactNativeStarterKit" 4 | } 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /src/images/launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/src/images/launch.png -------------------------------------------------------------------------------- /src/images/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/src/images/app-icon.png -------------------------------------------------------------------------------- /android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/debug.keystore -------------------------------------------------------------------------------- /documentation/rnsk-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/documentation/rnsk-logo.jpg -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ReactNativeStarterKit 3 | 4 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Windows files should use crlf line endings 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | *.bat text eol=crlf 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/tests/__mocks__/@react-native-community/async-storage.js: -------------------------------------------------------------------------------- 1 | export default from '@react-native-community/async-storage/jest/async-storage-mock'; 2 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Feather.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Feather.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Roboto.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /native-base-theme/components/Picker.ios.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const pickerTheme = {}; 5 | 6 | return pickerTheme; 7 | }; 8 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/AntDesign.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/AntDesign.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Fontisto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Fontisto.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Roboto_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/Roboto_medium.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/rubicon-icon-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/rubicon-icon-font.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | printWidth: 100, 6 | jsxBracketSameLine: false, 7 | }; 8 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/launch_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/drawable-xxhdpi/launch_screen.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /native-base-theme/components/Spinner.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const spinnerTheme = { 5 | height: 80 6 | }; 7 | 8 | return spinnerTheme; 9 | }; 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /native-base-theme/components/Switch.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const switchTheme = { 5 | marginVertical: -5 6 | }; 7 | 8 | return switchTheme; 9 | }; 10 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/ios/ReactNativeStarterKit/Images.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/ios/ReactNativeStarterKit/Images.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /native-base-theme/components/Tab.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const tabTheme = { 5 | flex: 1, 6 | backgroundColor: '#FFF' 7 | }; 8 | 9 | return tabTheme; 10 | }; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # All files 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/LaunchImage2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/LaunchImage2x.png -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/LaunchImage3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcnamee/react-native-starter-kit/HEAD/ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/LaunchImage3x.png -------------------------------------------------------------------------------- /src/containers/index.js: -------------------------------------------------------------------------------- 1 | // Articles 2 | export { default as ArticlesForm } from './Articles/Form'; 3 | export { default as ArticlesList } from './Articles/List'; 4 | export { default as ArticlesSingle } from './Articles/Single'; 5 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ReactNativeStarterKit' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /native-base-theme/components/Body.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const bodyTheme = { 5 | flex: 1, 6 | alignItems: 'center', 7 | alignSelf: 'center' 8 | }; 9 | 10 | return bodyTheme; 11 | }; 12 | -------------------------------------------------------------------------------- /native-base-theme/components/Label.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const labelTheme = { 5 | '.focused': { 6 | width: 0 7 | }, 8 | fontSize: 17 9 | }; 10 | 11 | return labelTheme; 12 | }; 13 | -------------------------------------------------------------------------------- /native-base-theme/components/Left.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const leftTheme = { 5 | flex: 1, 6 | alignSelf: 'center', 7 | alignItems: 'flex-start' 8 | }; 9 | 10 | return leftTheme; 11 | }; 12 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/UI/index.js: -------------------------------------------------------------------------------- 1 | export { default as Error } from './Error'; 2 | export { default as Header } from './Header'; 3 | export { default as Loading } from './Loading'; 4 | export { default as Messages } from './Messages'; 5 | export { default as Spacer } from './Spacer'; 6 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Root from './src/index'; 3 | import configureStore from './src/store/index'; 4 | 5 | const { persistor, store } = configureStore(); 6 | 7 | export default function App() { 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /native-base-theme/components/Picker.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const pickerTheme = { 5 | '.note': { 6 | color: '#8F8E95' 7 | }, 8 | // width: 90, 9 | marginRight: -4, 10 | flexGrow: 1 11 | }; 12 | 13 | return pickerTheme; 14 | }; 15 | -------------------------------------------------------------------------------- /native-base-theme/components/Picker.android.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const pickerTheme = { 5 | '.note': { 6 | color: '#8F8E95' 7 | }, 8 | // width: 90, 9 | marginRight: -4, 10 | flexGrow: 1 11 | }; 12 | 13 | return pickerTheme; 14 | }; 15 | -------------------------------------------------------------------------------- /native-base-theme/components/Right.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const rightTheme = { 5 | 'NativeBase.Button': { 6 | alignSelf: null 7 | }, 8 | flex: 1, 9 | alignSelf: 'center', 10 | alignItems: 'flex-end' 11 | }; 12 | 13 | return rightTheme; 14 | }; 15 | -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | json_key_file("android/app/google-play-android-developer.json") # Path to the Android json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one 2 | package_name("com.ReactNativeStarterKit") # e.g. com.krausefx.app 3 | # itc_team_id "" # e.g. 1233445 - the iTunes Connect Team ID 4 | -------------------------------------------------------------------------------- /native-base-theme/components/View.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const viewTheme = { 7 | '.padder': { 8 | padding: variables.contentPadding 9 | } 10 | }; 11 | 12 | return viewTheme; 13 | }; 14 | -------------------------------------------------------------------------------- /native-base-theme/components/Icon.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const iconTheme = { 7 | fontSize: variables.iconFontSize, 8 | color: variables.textColor 9 | }; 10 | 11 | return iconTheme; 12 | }; 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /native-base-theme/components/Content.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const contentTheme = { 5 | flex: 1, 6 | backgroundColor: 'transparent', 7 | 'NativeBase.Segment': { 8 | borderWidth: 0, 9 | backgroundColor: 'transparent' 10 | } 11 | }; 12 | 13 | return contentTheme; 14 | }; 15 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /native-base-theme/components/H1.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const h1Theme = { 7 | color: variables.textColor, 8 | fontSize: variables.fontSizeH1, 9 | lineHeight: variables.lineHeightH1 10 | }; 11 | 12 | return h1Theme; 13 | }; 14 | -------------------------------------------------------------------------------- /native-base-theme/components/H2.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const h2Theme = { 7 | color: variables.textColor, 8 | fontSize: variables.fontSizeH2, 9 | lineHeight: variables.lineHeightH2 10 | }; 11 | 12 | return h2Theme; 13 | }; 14 | -------------------------------------------------------------------------------- /native-base-theme/components/H3.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const h3Theme = { 7 | color: variables.textColor, 8 | fontSize: variables.fontSizeH3, 9 | lineHeight: variables.lineHeightH3 10 | }; 11 | 12 | return h3Theme; 13 | }; 14 | -------------------------------------------------------------------------------- /src/tests/components/UI/Loading.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import Loading from '../../../components/UI/Loading'; 4 | 5 | it(' renders correctly', () => { 6 | const Component = ; 7 | 8 | // Matches snapshot 9 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/UI/Spacer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { View } from 'native-base'; 4 | 5 | const Spacer = ({ size }) => ; 6 | 7 | Spacer.propTypes = { 8 | size: PropTypes.number, 9 | }; 10 | 11 | Spacer.defaultProps = { 12 | size: 20, 13 | }; 14 | 15 | export default Spacer; 16 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: true, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/tests/components/UI/__snapshots__/Loading.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders correctly 1`] = ` 4 | 13 | 17 | 18 | `; 19 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | PreviewsEnabled 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/UI/Loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, ActivityIndicator } from 'react-native'; 3 | import Colors from '../../../native-base-theme/variables/commonColor'; 4 | 5 | const Loading = () => ( 6 | 7 | 8 | 9 | ); 10 | 11 | export default Loading; 12 | -------------------------------------------------------------------------------- /src/lib/pagination.js: -------------------------------------------------------------------------------- 1 | export default (lastPage, link) => { 2 | const pagination = []; 3 | const upTo = parseInt(lastPage, 10); 4 | 5 | if (upTo > 1) { 6 | for (let p = 1; p <= upTo; p++) { // eslint-disable-line 7 | if (p === 1) { 8 | pagination.push({ title: p, link }); 9 | } else { 10 | pagination.push({ title: p, link: `${link}${p}` }); 11 | } 12 | } 13 | } 14 | 15 | return pagination; 16 | }; 17 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /native-base-theme/components/Text.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const textTheme = { 7 | fontSize: variables.DefaultFontSize, 8 | fontFamily: variables.fontFamily, 9 | color: variables.textColor, 10 | '.note': { 11 | color: '#a7a7a7', 12 | fontSize: variables.noteFontSize 13 | } 14 | }; 15 | 16 | return textTheme; 17 | }; 18 | -------------------------------------------------------------------------------- /native-base-theme/components/Input.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const inputTheme = { 7 | '.multiline': { 8 | height: null 9 | }, 10 | height: variables.inputHeightBase, 11 | color: variables.inputColor, 12 | paddingLeft: 5, 13 | paddingRight: 5, 14 | flex: 1, 15 | fontSize: variables.inputFontSize 16 | }; 17 | 18 | return inputTheme; 19 | }; 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/tests/lib/pagination.test.js: -------------------------------------------------------------------------------- 1 | import pagination from '../../lib/pagination'; 2 | 3 | it('lib/pagination: pagination returns correctly', () => { 4 | const threePages = pagination(3, '/articles/'); 5 | expect(threePages).toEqual([ 6 | { title: 1, link: '/articles/' }, 7 | { title: 2, link: '/articles/2' }, 8 | { title: 3, link: '/articles/3' }, 9 | ]); 10 | 11 | // No links when only 1 page 12 | const onePage = pagination({ last_page: 1 }, '/articles/'); 13 | expect(onePage).toEqual([]); 14 | }); 15 | -------------------------------------------------------------------------------- /native-base-theme/components/Container.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform, Dimensions } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | const deviceHeight = Dimensions.get('window').height; 9 | export default (variables /* : * */ = variable) => { 10 | const theme = { 11 | flex: 1, 12 | height: Platform.OS === PLATFORM.IOS ? deviceHeight : deviceHeight - 20, 13 | backgroundColor: variables.containerBgColor 14 | }; 15 | 16 | return theme; 17 | }; 18 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/constants/config.js: -------------------------------------------------------------------------------- 1 | const isDevEnv = process.env.NODE_ENV === 'development'; 2 | 3 | export default { 4 | // App Details 5 | appName: 'ReactNativeStarterKit', 6 | 7 | // Build Configuration - eg. Debug or Release? 8 | isDevEnv, 9 | 10 | // Date Format 11 | dateFormat: 'Do MMM YYYY', 12 | 13 | // API 14 | apiBaseUrl: isDevEnv 15 | ? 'https://digitalsupply.co/wp-json/wp' 16 | : 'https://digitalsupply.co/wp-json/wp', 17 | 18 | // Google Analytics - uses a 'dev' account while we're testing 19 | gaTrackingId: isDevEnv ? 'UA-84284256-2' : 'UA-84284256-1', 20 | }; 21 | -------------------------------------------------------------------------------- /native-base-theme/components/Fab.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const fabTheme = { 5 | 'NativeBase.Button': { 6 | alignItems: 'center', 7 | padding: null, 8 | justifyContent: 'center', 9 | 'NativeBase.Icon': { 10 | alignSelf: 'center', 11 | fontSize: 20, 12 | marginLeft: 0, 13 | marginRight: 0 14 | }, 15 | 'NativeBase.IconNB': { 16 | alignSelf: 'center', 17 | fontSize: 20, 18 | marginLeft: 0, 19 | marginRight: 0 20 | } 21 | } 22 | }; 23 | 24 | return fabTheme; 25 | }; 26 | -------------------------------------------------------------------------------- /src/tests/components/UI/__snapshots__/Spacer.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders with correctly with size: 10 1`] = ` 4 | 15 | `; 16 | 17 | exports[` renders with correctly with size: 15 1`] = ` 18 | 29 | `; 30 | -------------------------------------------------------------------------------- /src/tests/lib/images.test.js: -------------------------------------------------------------------------------- 1 | import { getFeaturedImageUrl } from '../../lib/images'; 2 | 3 | it('lib/string: getFeaturedImageUrl returns correctly', () => { 4 | expect(getFeaturedImageUrl({ 5 | _embedded: { 6 | 'wp:featuredmedia': [{ 7 | media_details: { 8 | sizes: { 9 | full: { 10 | source_url: 'https://www.digitalsupply.co/wp-content/uploads/2018/03/glacier-blue.jpg', 11 | }, 12 | }, 13 | }, 14 | }], 15 | }, 16 | })).toEqual('https://www.digitalsupply.co/wp-content/uploads/2018/03/glacier-blue.jpg'); 17 | }); 18 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Help us reproduce - tell us about your environment 4 | 5 | 6 | 1. 7 | 8 | ### Steps to reproduce 9 | 10 | 1. 11 | 12 | ### Expected result 13 | 14 | 1. 15 | 16 | ### Actual result 17 | 18 | 1. [Screenshot, logs] 19 | -------------------------------------------------------------------------------- /src/tests/components/UI/Spacer.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import Spacer from '../../../components/UI/Spacer'; 4 | 5 | it(' renders with correctly with size: 10', () => { 6 | const Component = ; 7 | 8 | // Matches snapshot 9 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 10 | }); 11 | 12 | it(' renders with correctly with size: 15', () => { 13 | const Component = ; 14 | 15 | // Matches snapshot 16 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 17 | }); 18 | -------------------------------------------------------------------------------- /src/tests/components/UI/Header.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { render } from '@testing-library/react-native'; 4 | import Header from '../../../components/UI/Header'; 5 | 6 | it('
renders with message', () => { 7 | const Component =
; 8 | 9 | // Matches snapshot 10 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 11 | 12 | // Has the correct text on the page 13 | const { getByText } = render(Component); 14 | expect(getByText('hello boy')); 15 | expect(getByText("I'm here")); 16 | }); 17 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Images.xcassets/LaunchScreen.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/tests/__mocks__/react-redux.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This mock will make sure that we are able to access mapStateToProps, 3 | * mapDispatchToProps and reactComponent in the test file. 4 | */ 5 | 6 | // To use this, just do `jest.mock('react-redux');` in your test.js file. 7 | const mock = jest.fn((action) => action); 8 | 9 | module.exports = { 10 | connect: (mapStateToProps, mapDispatchToProps) => (reactComponent) => ({ 11 | mapStateToProps, 12 | mapDispatchToProps: (dispatch = mock, ownProps) => mapDispatchToProps(dispatch, ownProps), 13 | reactComponent, 14 | mock, 15 | }), 16 | Provider: ({ children }) => children, 17 | }; 18 | -------------------------------------------------------------------------------- /src/lib/images.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sort through the mass amounts of data in 3 | * an endpoint and return the featured image URL 4 | */ 5 | // eslint-disable-next-line 6 | export const getFeaturedImageUrl = (item) => ( 7 | (item._embedded 8 | && item._embedded['wp:featuredmedia'] 9 | && item._embedded['wp:featuredmedia']['0'] 10 | && item._embedded['wp:featuredmedia']['0'].media_details 11 | && item._embedded['wp:featuredmedia']['0'].media_details.sizes 12 | && item._embedded['wp:featuredmedia']['0'].media_details.sizes.full 13 | && item._embedded['wp:featuredmedia']['0'].media_details.sizes.full.source_url) 14 | || null 15 | ); 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: macOS-latest 8 | strategy: 9 | matrix: 10 | node-version: [12.x] 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Use Node.js ${{ matrix.node-version }} 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: ${{ matrix.node-version }} 18 | - name: Install 19 | run: | 20 | yarn 21 | - name: Lint 22 | run: | 23 | ./node_modules/.bin/eslint "src/**/*.js" 24 | - name: Jest Tests 25 | run: | 26 | yarn test 27 | env: 28 | CI: true 29 | -------------------------------------------------------------------------------- /native-base-theme/components/Subtitle.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const subtitleTheme = { 10 | fontSize: variables.subTitleFontSize, 11 | fontFamily: variables.titleFontfamily, 12 | color: variables.subtitleColor, 13 | textAlign: Platform.OS === PLATFORM.IOS ? 'center' : 'left', 14 | paddingLeft: Platform.OS === PLATFORM.IOS ? 4 : 0, 15 | marginLeft: Platform.OS === PLATFORM.IOS ? undefined : -3 16 | }; 17 | 18 | return subtitleTheme; 19 | }; 20 | -------------------------------------------------------------------------------- /native-base-theme/components/Textarea.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const textAreaTheme = { 7 | '.underline': { 8 | borderBottomWidth: variables.borderWidth, 9 | marginTop: 5, 10 | borderColor: variables.inputBorderColor 11 | }, 12 | '.bordered': { 13 | borderWidth: 1, 14 | marginTop: 5, 15 | borderColor: variables.inputBorderColor 16 | }, 17 | color: variables.textColor, 18 | paddingLeft: 10, 19 | paddingRight: 5, 20 | fontSize: 15, 21 | textAlignVertical: 'top' 22 | }; 23 | 24 | return textAreaTheme; 25 | }; 26 | -------------------------------------------------------------------------------- /android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /src/constants/navigation.js: -------------------------------------------------------------------------------- 1 | import Colors from '../../native-base-theme/variables/commonColor'; 2 | 3 | export default { 4 | navbarProps: { 5 | navigationBarStyle: { backgroundColor: 'white' }, 6 | titleStyle: { 7 | color: Colors.textColor, 8 | alignSelf: 'center', 9 | fontSize: Colors.fontSizeBase, 10 | }, 11 | backButtonTintColor: Colors.textColor, 12 | }, 13 | 14 | tabProps: { 15 | swipeEnabled: false, 16 | activeBackgroundColor: 'rgba(255,255,255,0.1)', 17 | inactiveBackgroundColor: Colors.brandPrimary, 18 | tabBarStyle: { backgroundColor: Colors.brandPrimary }, 19 | }, 20 | 21 | icons: { 22 | style: { color: 'white', height: 30, width: 30 }, 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/components/UI/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { View } from 'react-native'; 4 | import { Text, H1 } from 'native-base'; 5 | import Spacer from './Spacer'; 6 | 7 | const Header = ({ title, content }) => ( 8 | 9 | 10 |

{title}

11 | {!!content && ( 12 | 13 | 14 | {content} 15 | 16 | )} 17 | 18 |
19 | ); 20 | 21 | Header.propTypes = { 22 | title: PropTypes.string, 23 | content: PropTypes.string, 24 | }; 25 | 26 | Header.defaultProps = { 27 | title: 'Missing title', 28 | content: '', 29 | }; 30 | 31 | export default Header; 32 | -------------------------------------------------------------------------------- /native-base-theme/components/Title.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const titleTheme = { 10 | fontSize: variables.titleFontSize, 11 | fontFamily: variables.titleFontfamily, 12 | color: variables.titleFontColor, 13 | fontWeight: Platform.OS === PLATFORM.IOS ? '700' : undefined, 14 | textAlign: Platform.OS === PLATFORM.IOS ? 'center' : 'left', 15 | paddingLeft: Platform.OS === PLATFORM.IOS ? 4 : 0, 16 | marginLeft: Platform.OS === PLATFORM.IOS ? undefined : -3, 17 | paddingTop: 1 18 | }; 19 | 20 | return titleTheme; 21 | }; 22 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactnativestarterkit/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reactnativestarterkit; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import android.os.Bundle; 5 | import org.devio.rn.splashscreen.SplashScreen; 6 | 7 | public class MainActivity extends ReactActivity { 8 | // Show RN Splash Screen on launch 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | SplashScreen.show(this); 12 | super.onCreate(savedInstanceState); 13 | } 14 | 15 | /** 16 | * Returns the name of the main component registered from JavaScript. This is used to schedule 17 | * rendering of the component. 18 | */ 19 | @Override 20 | protected String getMainComponentName() { 21 | return "ReactNativeStarterKit"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/tests/lib/format-error-messages.test.js: -------------------------------------------------------------------------------- 1 | import formatErrors from '../../lib/format-error-messages'; 2 | 3 | it('Errors to be in a consistent format', () => { 4 | // Object passed 5 | expect(formatErrors({ message: 'Hi!' })).toStrictEqual(new Error('Hi!')); 6 | 7 | // Error passed 8 | expect(formatErrors(new Error('Hi!'))).toStrictEqual(new Error('Hi!')); 9 | 10 | // Laravel Error Object passed 11 | const validationError = { 12 | message: '422 Unprocessable Entity', 13 | errors: { 14 | firstName: ['The first name must be a valid name.'], 15 | email: ['The email must be a valid email address.'], 16 | }, 17 | }; 18 | expect(formatErrors(validationError)) 19 | .toStrictEqual(new Error('The first name must be a valid name.The email must be a valid email address.')); 20 | }); 21 | -------------------------------------------------------------------------------- /native-base-theme/components/Thumbnail.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const thumbnailTheme = { 5 | '.square': { 6 | borderRadius: 0, 7 | '.small': { 8 | width: 36, 9 | height: 36, 10 | borderRadius: 0 11 | }, 12 | '.large': { 13 | width: 80, 14 | height: 80, 15 | borderRadius: 0 16 | } 17 | }, 18 | '.small': { 19 | width: 36, 20 | height: 36, 21 | borderRadius: 18, 22 | '.square': { 23 | borderRadius: 0 24 | } 25 | }, 26 | '.large': { 27 | width: 80, 28 | height: 80, 29 | borderRadius: 40, 30 | '.square': { 31 | borderRadius: 0 32 | } 33 | }, 34 | width: 56, 35 | height: 56, 36 | borderRadius: 28 37 | }; 38 | 39 | return thumbnailTheme; 40 | }; 41 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 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/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '11.0' 5 | 6 | target 'ReactNativeStarterKit' do 7 | config = use_native_modules! 8 | 9 | use_react_native!( 10 | :path => config[:reactNativePath], 11 | # to enable hermes on iOS, change `false` to `true` and then install pods 12 | :hermes_enabled => false 13 | ) 14 | 15 | target 'ReactNativeStarterKitTests' do 16 | inherit! :complete 17 | # Pods for testing 18 | end 19 | 20 | # Enables Flipper. 21 | # 22 | # Note that if you have use_frameworks! enabled, Flipper will not work and 23 | # you should disable the next line. 24 | use_flipper!() 25 | 26 | post_install do |installer| 27 | react_native_post_install(installer) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | /* global */ 2 | import { init } from '@rematch/core'; 3 | import createPersistPlugin, { getPersistor } from '@rematch/persist'; 4 | import createLoadingPlugin from '@rematch/loading'; 5 | import AsyncStorage from '@react-native-community/async-storage'; 6 | import * as models from '../models'; 7 | 8 | // Create plugins 9 | const persistPlugin = createPersistPlugin({ 10 | key: 'root', 11 | storage: AsyncStorage, 12 | blacklist: [], 13 | }); 14 | const loadingPlugin = createLoadingPlugin({}); 15 | 16 | const configureStore = () => { 17 | const store = init({ 18 | models, 19 | redux: { 20 | middlewares: [], 21 | }, 22 | plugins: [persistPlugin, loadingPlugin], 23 | }); 24 | 25 | const persistor = getPersistor(); 26 | const { dispatch } = store; 27 | 28 | return { persistor, store, dispatch }; 29 | }; 30 | 31 | export default configureStore; 32 | -------------------------------------------------------------------------------- /src/components/UI/Messages.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { View } from 'react-native'; 4 | import { Text } from 'native-base'; 5 | 6 | import Colors from '../../../native-base-theme/variables/commonColor'; 7 | 8 | const Messages = ({ message, type }) => ( 9 | 17 | {message} 18 | 19 | ); 20 | 21 | Messages.propTypes = { 22 | message: PropTypes.string, 23 | type: PropTypes.oneOf(['error', 'success', 'info']), 24 | }; 25 | 26 | Messages.defaultProps = { 27 | message: 'An unexpected error came up', 28 | type: 'error', 29 | }; 30 | 31 | export default Messages; 32 | -------------------------------------------------------------------------------- /src/tests/components/UI/Error.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { render } from '@testing-library/react-native'; 4 | import Error from '../../../components/UI/Error'; 5 | 6 | it(' renders with message', () => { 7 | const Component = ; 8 | 9 | // Matches snapshot 10 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 11 | 12 | // Has the correct text on the page 13 | const { getByText } = render(Component); 14 | expect(getByText('hello boy')); 15 | }); 16 | 17 | it(' renders with a button', () => { 18 | const Component = {}} />; 19 | 20 | // Matches snapshot 21 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 22 | 23 | // Has the correct text on the page 24 | const { getByText } = render(Component); 25 | expect(getByText('hello boy')); 26 | expect(getByText('Try Again')); 27 | }); 28 | -------------------------------------------------------------------------------- /src/tests/lib/string.test.js: -------------------------------------------------------------------------------- 1 | import { ucfirst, truncate } from '../../lib/string'; 2 | 3 | it('lib/string: ucfirst returns correctly', () => { 4 | const lcWord = ucfirst('hello'); 5 | expect(lcWord).toEqual('Hello'); 6 | 7 | const upWord = ucfirst('Hello'); 8 | expect(upWord).toEqual('Hello'); 9 | 10 | const lcWords = ucfirst('hello world'); 11 | expect(lcWords).toEqual('Hello world'); 12 | 13 | const upWords = ucfirst('Hello world'); 14 | expect(upWords).toEqual('Hello world'); 15 | 16 | const numbers = ucfirst('1234 world'); 17 | expect(numbers).toEqual('1234 world'); 18 | }); 19 | 20 | it('lib/string: truncate returns correctly', () => { 21 | const lcWord = truncate('hello world this is', 2); 22 | expect(lcWord).toEqual('hello world…'); 23 | 24 | const upWord = truncate('Hello', 3); 25 | expect(upWord).toEqual('Hello'); 26 | 27 | const lcWords = truncate('hello world world this is a big'); 28 | expect(lcWords).toEqual('hello world world this is a big'); 29 | }); 30 | -------------------------------------------------------------------------------- /native-base-theme/components/TabContainer.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const platformStyle = variables.platformStyle; 10 | 11 | const tabContainerTheme = { 12 | elevation: 3, 13 | height: 50, 14 | flexDirection: 'row', 15 | shadowColor: platformStyle === PLATFORM.MATERIAL ? '#000' : undefined, 16 | shadowOffset: 17 | platformStyle === PLATFORM.MATERIAL ? { width: 0, height: 2 } : undefined, 18 | shadowOpacity: platformStyle === PLATFORM.MATERIAL ? 0.2 : undefined, 19 | shadowRadius: platformStyle === PLATFORM.MATERIAL ? 1.2 : undefined, 20 | justifyContent: 'space-around', 21 | borderBottomWidth: Platform.OS === PLATFORM.IOS ? variables.borderWidth : 0, 22 | borderColor: variables.topTabBarBorderColor 23 | }; 24 | 25 | return tabContainerTheme; 26 | }; 27 | -------------------------------------------------------------------------------- /fastlane/update-app-version.sh: -------------------------------------------------------------------------------- 1 | ANDROID_FILE="android/app/build.gradle" 2 | TEMP_ANDROID_FILE="${ANDROID_FILE}.txt" 3 | 4 | IOS_FILE="ios/ReactNativeStarterKit/Info.plist" 5 | TEMP_IOS_FILE="${IOS_FILE}.txt" 6 | 7 | IOS_BUILD_NUMBER=1 8 | 9 | CURRENT_VERSION_NAME=$( /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "${IOS_FILE}" ) 10 | 11 | # --- 12 | 13 | echo "••• What's the new App Version? (current version: $CURRENT_VERSION_NAME)" 14 | read APP_VERSION_NAME 15 | 16 | # --- 17 | 18 | # Android 19 | cat ${ANDROID_FILE} | sed "s/versionName \".*\"/versionName \"${APP_VERSION_NAME}\"/" > ${TEMP_ANDROID_FILE} 20 | echo "$(awk '{sub(/versionCode [[:digit:]]+$/,"versionCode "$2+1)}1' ${TEMP_ANDROID_FILE})" > ${TEMP_ANDROID_FILE} 21 | cat ${TEMP_ANDROID_FILE} > ${ANDROID_FILE} 22 | rm -f ${TEMP_ANDROID_FILE} 23 | 24 | # iOS 25 | /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${APP_VERSION_NAME}" "${IOS_FILE}" 26 | /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${IOS_BUILD_NUMBER}" "${IOS_FILE}" 27 | -------------------------------------------------------------------------------- /src/components/UI/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Container, Text, H3, Button, View, 5 | } from 'native-base'; 6 | import Spacer from './Spacer'; 7 | 8 | const Error = ({ title, content, tryAgain }) => ( 9 | 10 | 11 | 12 |

{title}

13 | {content} 14 | {tryAgain && ( 15 | 18 | )} 19 | 20 |
21 |
22 | ); 23 | 24 | Error.propTypes = { 25 | title: PropTypes.string, 26 | content: PropTypes.string, 27 | tryAgain: PropTypes.func, 28 | }; 29 | 30 | Error.defaultProps = { 31 | title: 'Uh oh', 32 | content: 'An unexpected error came up', 33 | tryAgain: null, 34 | }; 35 | 36 | export default Error; 37 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios certificates 20 | ``` 21 | fastlane ios certificates 22 | ``` 23 | Fetch certificates and provisioning profiles 24 | ### ios beta 25 | ``` 26 | fastlane ios beta 27 | ``` 28 | Ship to Testflight. 29 | 30 | ---- 31 | 32 | ## Android 33 | ### android beta 34 | ``` 35 | fastlane android beta 36 | ``` 37 | Ship to Playstore Beta. 38 | 39 | ---- 40 | 41 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 42 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 43 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 44 | -------------------------------------------------------------------------------- /native-base-theme/components/Card.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const cardTheme = { 7 | '.transparent': { 8 | shadowColor: null, 9 | shadowOffset: null, 10 | shadowOpacity: null, 11 | shadowRadius: null, 12 | elevation: null, 13 | backgroundColor: 'transparent', 14 | borderWidth: 0 15 | }, 16 | '.noShadow': { 17 | shadowColor: null, 18 | shadowOffset: null, 19 | shadowOpacity: null, 20 | elevation: null 21 | }, 22 | marginVertical: 5, 23 | marginHorizontal: 2, 24 | borderWidth: variables.borderWidth, 25 | borderRadius: variables.cardBorderRadius, 26 | borderColor: variables.cardBorderColor, 27 | flexWrap: 'nowrap', 28 | backgroundColor: variables.cardDefaultBg, 29 | shadowColor: '#000', 30 | shadowOffset: { width: 0, height: 2 }, 31 | shadowOpacity: 0.1, 32 | shadowRadius: 1.5, 33 | elevation: 3 34 | }; 35 | 36 | return cardTheme; 37 | }; 38 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /native-base-theme/components/Radio.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const radioTheme = { 10 | '.selected': { 11 | 'NativeBase.IconNB': { 12 | color: 13 | Platform.OS === PLATFORM.IOS 14 | ? variables.radioColor 15 | : variables.radioSelectedColorAndroid, 16 | lineHeight: 17 | Platform.OS === PLATFORM.IOS ? 25 : variables.radioBtnLineHeight, 18 | height: Platform.OS === PLATFORM.IOS ? 20 : undefined 19 | } 20 | }, 21 | 'NativeBase.IconNB': { 22 | color: Platform.OS === PLATFORM.IOS ? 'transparent' : undefined, 23 | lineHeight: 24 | Platform.OS === PLATFORM.IOS ? undefined : variables.radioBtnLineHeight, 25 | fontSize: 26 | Platform.OS === PLATFORM.IOS ? undefined : variables.radioBtnSize 27 | } 28 | }; 29 | 30 | return radioTheme; 31 | }; 32 | -------------------------------------------------------------------------------- /.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 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /src/lib/format-error-messages.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a readable error message for the front-end user 3 | */ 4 | export default (error) => { 5 | /* 6 | For an error response like: 7 | { 8 | "message": "422 Unprocessable Entity", 9 | "errors": { 10 | "email": [ 11 | "The email must be a valid email address." 12 | ] 13 | } 14 | } 15 | */ 16 | if (error && error.errors) { 17 | let errors = ''; 18 | Object.entries(error.errors).forEach((v) => { 19 | errors += v[1].join(', '); 20 | }); 21 | return Error(errors); 22 | } 23 | 24 | /* 25 | For an error response like: 26 | { 27 | "error": { 28 | "message": "403 Forbidden", 29 | "status_code": 403 30 | } 31 | } 32 | */ 33 | if (error && error.message) { 34 | return Error(error.message); 35 | } 36 | 37 | // When an Error - return the error 38 | if (error instanceof Error) { 39 | return error; 40 | } 41 | 42 | // Otherwise create an error 43 | return new Error('Uh oh - something happened'); 44 | }; 45 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'airbnb', 3 | parser: 'babel-eslint', 4 | plugins: [ 5 | 'jest' 6 | ], 7 | parserOptions: { 8 | ecmaFeatures: { 9 | classes: true 10 | } 11 | }, 12 | env: { 13 | 'jest/globals': true 14 | }, 15 | rules: { 16 | 'max-len': [2, {'code': 110, 'tabWidth': 2, 'ignoreUrls': true}], 17 | 'react/jsx-filename-extension': ['error', { 'extensions': ['.js', '.jsx'] }], 18 | 'global-require': 'off', 19 | 'no-console': 'off', 20 | 'import/no-extraneous-dependencies': 'off', 21 | 'import/extensions': 'off', 22 | 'import/no-unresolved': 'off', 23 | 'jsx-a11y/anchor-is-valid': 'off', 24 | 'no-underscore-dangle': 'off', 25 | 'prefer-promise-reject-errors': 'off', 26 | 'no-nested-ternary': 'off', 27 | 'react/no-multi-comp': 'off', 28 | 'react/no-unescaped-entities': 'off', 29 | 'jsx-a11y/click-events-have-key-events': 'off', 30 | 'jsx-a11y/no-static-element-interactions': 'off', 31 | 'react/jsx-props-no-spreading': 'off', 32 | 'react/jsx-fragments': 'off', 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /native-base-theme/components/Badge.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const badgeTheme = { 7 | '.primary': { 8 | backgroundColor: variables.buttonPrimaryBg 9 | }, 10 | '.warning': { 11 | backgroundColor: variables.buttonWarningBg 12 | }, 13 | '.info': { 14 | backgroundColor: variables.buttonInfoBg 15 | }, 16 | '.success': { 17 | backgroundColor: variables.buttonSuccessBg 18 | }, 19 | '.danger': { 20 | backgroundColor: variables.buttonDangerBg 21 | }, 22 | 'NativeBase.Text': { 23 | color: variables.badgeColor, 24 | fontSize: variables.fontSizeBase, 25 | lineHeight: variables.lineHeight - 1, 26 | textAlign: 'center', 27 | paddingHorizontal: 3 28 | }, 29 | backgroundColor: variables.badgeBg, 30 | padding: variables.badgePadding, 31 | paddingHorizontal: 6, 32 | alignSelf: 'flex-start', 33 | justifyContent: 'center', 34 | borderRadius: 13.5, 35 | height: 27 36 | }; 37 | return badgeTheme; 38 | }; 39 | -------------------------------------------------------------------------------- /native-base-theme/components/Toast.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | import { PLATFORM } from './../variables/commonColor'; 5 | 6 | export default (variables /* : * */ = variable) => { 7 | const platform = variables.platform; 8 | 9 | const toastTheme = { 10 | '.danger': { 11 | backgroundColor: variables.brandDanger 12 | }, 13 | '.warning': { 14 | backgroundColor: variables.brandWarning 15 | }, 16 | '.success': { 17 | backgroundColor: variables.brandSuccess 18 | }, 19 | backgroundColor: 'rgba(0,0,0,0.8)', 20 | borderRadius: platform === PLATFORM.IOS ? 5 : 0, 21 | flexDirection: 'row', 22 | justifyContent: 'space-between', 23 | alignItems: 'center', 24 | padding: 10, 25 | minHeight: 50, 26 | 'NativeBase.Text': { 27 | color: '#fff', 28 | flex: 1 29 | }, 30 | 'NativeBase.Button': { 31 | backgroundColor: 'transparent', 32 | height: 30, 33 | elevation: 0, 34 | 'NativeBase.Text': { 35 | fontSize: 14 36 | } 37 | } 38 | }; 39 | 40 | return toastTheme; 41 | }; 42 | -------------------------------------------------------------------------------- /src/constants/messages.js: -------------------------------------------------------------------------------- 1 | export const generalMessages = {}; 2 | 3 | export const successMessages = { 4 | // Defaults 5 | defaultForm: 'Success - Form Saved', 6 | 7 | // Member 8 | login: 'You are now logged in', 9 | signUp: 'You are now signed up. Please login to continue.', 10 | forgotPassword: 'Password reset. Please check your email.', 11 | }; 12 | 13 | export const errorMessages = { 14 | // Defaults 15 | default: 'Hmm, an unknown error occured', 16 | timeout: 'Server Timed Out. Check your internet connection', 17 | invalidJson: 'Response returned is not valid JSON', 18 | missingData: 'Missing data', 19 | 20 | // Member 21 | memberNotAuthd: 'You need to be logged in, to update your profile', 22 | memberExists: 'Member already exists', 23 | missingFirstName: 'First name is missing', 24 | missingLastName: 'Last name is missing', 25 | missingEmail: 'Email is missing', 26 | missingPassword: 'Password is missing', 27 | passwordsDontMatch: 'Passwords do not match', 28 | 29 | // Articles 30 | articlesEmpty: 'No articles found', 31 | articles404: 'This article could not be found', 32 | }; 33 | -------------------------------------------------------------------------------- /native-base-theme/components/SwipeRow.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const swipeRowTheme = { 5 | 'NativeBase.ListItem': { 6 | '.list': { 7 | backgroundColor: '#FFF' 8 | }, 9 | marginLeft: 0 10 | }, 11 | 'NativeBase.Left': { 12 | flex: 0, 13 | alignSelf: null, 14 | alignItems: null, 15 | 'NativeBase.Button': { 16 | flex: 1, 17 | alignItems: 'center', 18 | justifyContent: 'center', 19 | alignSelf: 'stretch', 20 | borderRadius: 0 21 | } 22 | }, 23 | 'NativeBase.Right': { 24 | flex: 0, 25 | alignSelf: null, 26 | alignItems: null, 27 | 'NativeBase.Button': { 28 | flex: 1, 29 | alignItems: 'center', 30 | justifyContent: 'center', 31 | alignSelf: 'stretch', 32 | borderRadius: 0 33 | } 34 | }, 35 | 'NativeBase.Button': { 36 | flex: 1, 37 | height: null, 38 | alignItems: 'center', 39 | justifyContent: 'center', 40 | alignSelf: 'stretch', 41 | borderRadius: 0 42 | } 43 | }; 44 | 45 | return swipeRowTheme; 46 | }; 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Matt Mcnamee 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 | -------------------------------------------------------------------------------- /native-base-theme/components/CheckBox.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const checkBoxTheme = { 7 | '.checked': { 8 | 'NativeBase.Icon': { 9 | color: variables.checkboxTickColor 10 | }, 11 | 'NativeBase.IconNB': { 12 | color: variables.checkboxTickColor 13 | } 14 | }, 15 | 'NativeBase.Icon': { 16 | color: 'transparent', 17 | lineHeight: variables.CheckboxIconSize, 18 | marginTop: variables.CheckboxIconMarginTop, 19 | fontSize: variables.CheckboxFontSize 20 | }, 21 | 'NativeBase.IconNB': { 22 | color: 'transparent', 23 | lineHeight: variables.CheckboxIconSize, 24 | marginTop: variables.CheckboxIconMarginTop, 25 | fontSize: variables.CheckboxFontSize 26 | }, 27 | overflow: 'hidden', 28 | width: variables.checkboxSize, 29 | height: variables.checkboxSize, 30 | borderWidth: variables.CheckboxBorderWidth, 31 | paddingLeft: variables.CheckboxPaddingLeft - 1, 32 | paddingBottom: variables.CheckboxPaddingBottom, 33 | left: 10 34 | }; 35 | 36 | return checkBoxTheme; 37 | }; 38 | -------------------------------------------------------------------------------- /src/store/articles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | listPaginated: { 3 | 1: [{ 4 | placeholder: true, 5 | id: 0, 6 | name: '---- --- -- ------', 7 | content: '---- --- -- ------ ---- --- -- ------ ---- --- -- ------ ---- --- -- ------', 8 | excerpt: '---- --- -- ------ ---- --- -- ------ ---- --- -- ------ ---- --- -- ------', 9 | image: 'https://www.digitalsupply.co/wp-content/uploads/2018/03/glacier-blue.jpg', 10 | date: '-- / -- / ----', 11 | slug: '-----', 12 | link: '----.---.--/------', 13 | }], 14 | }, 15 | listFlat: [{ 16 | placeholder: true, 17 | id: 0, 18 | name: '---- --- -- ------', 19 | content: '---- --- -- ------ ---- --- -- ------ ---- --- -- ------ ---- --- -- ------', 20 | excerpt: '---- --- -- ------ ---- --- -- ------ ---- --- -- ------ ---- --- -- ------', 21 | image: 'https://www.digitalsupply.co/wp-content/uploads/2018/03/glacier-blue.jpg', 22 | date: '-- / -- / ----', 23 | slug: '-----', 24 | link: '----.---.--/------', 25 | }], 26 | meta: { 27 | page: 1, 28 | lastPage: null, 29 | total: null, 30 | }, 31 | lastSync: {}, 32 | pagination: [], 33 | userInput: { email: '' }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/tests/components/Articles/List.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { render } from '@testing-library/react-native'; 4 | import ArticlesList from '../../../components/Articles/List'; 5 | import { errorMessages } from '../../../constants/messages'; 6 | 7 | it(' shows a nice error message', () => { 8 | const Component = ; 9 | 10 | // Matches snapshot 11 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 12 | 13 | // Has the correct text on the page 14 | const { getByText } = render(Component); 15 | expect(getByText(errorMessages.articlesEmpty)); 16 | }); 17 | 18 | it(' shows a list of articles correctly', () => { 19 | const listItem = { 20 | id: 0, 21 | name: 'ABC', 22 | excerpt: 'DEF', 23 | contentRaw: 'DEF', 24 | date: '22/33/44', 25 | }; 26 | 27 | const Component = ; 28 | 29 | // Matches snapshot 30 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 31 | 32 | // Has the correct text on the page 33 | const { getByText } = render(Component); 34 | expect(getByText(listItem.name)); 35 | }); 36 | -------------------------------------------------------------------------------- /src/tests/components/Articles/Single.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { render } from '@testing-library/react-native'; 4 | import ArticlesSingle from '../../../components/Articles/Single'; 5 | import { errorMessages } from '../../../constants/messages'; 6 | 7 | it(' shows a nice error message', () => { 8 | const Component = ; 9 | 10 | // Matches snapshot 11 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 12 | 13 | // Has the correct text on the page 14 | const { getByText } = render(Component); 15 | expect(getByText(errorMessages.articles404)); 16 | }); 17 | 18 | it(' shows an article correctly', () => { 19 | const article = { 20 | id: 0, 21 | name: 'ABC', 22 | excerpt: 'DEF', 23 | content: 'DEF', 24 | date: '22/33/44', 25 | }; 26 | 27 | const Component = ; 28 | 29 | // Matches snapshot 30 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 31 | 32 | // Has the correct text on the page 33 | const { getByText } = render(Component); 34 | expect(getByText(article.name)); 35 | }); 36 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "30.0.2" 6 | minSdkVersion = 21 7 | compileSdkVersion = 30 8 | targetSdkVersion = 30 9 | ndkVersion = "20.1.5948944" 10 | } 11 | repositories { 12 | google() 13 | mavenCentral() 14 | } 15 | dependencies { 16 | classpath("com.android.tools.build:gradle:4.2.1") 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | mavenCentral() 25 | mavenLocal() 26 | maven { 27 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 28 | url("$rootDir/../node_modules/react-native/android") 29 | } 30 | maven { 31 | // Android JSC is installed from npm 32 | url("$rootDir/../node_modules/jsc-android/dist") 33 | } 34 | 35 | google() 36 | maven { url 'https://www.jitpack.io' } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/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 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "120.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "180.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ios-marketing", 47 | "size" : "1024x1024", 48 | "scale" : "1x" 49 | } 50 | ], 51 | "info" : { 52 | "version" : 1, 53 | "author" : "xcode" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /native-base-theme/components/TabHeading.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | import { PLATFORM } from './../variables/commonColor'; 5 | 6 | export default (variables /* : * */ = variable) => { 7 | const platform = variables.platform; 8 | 9 | const tabHeadingTheme = { 10 | flexDirection: 'row', 11 | backgroundColor: variables.tabDefaultBg, 12 | flex: 1, 13 | alignItems: 'center', 14 | justifyContent: 'center', 15 | '.scrollable': { 16 | paddingHorizontal: 20, 17 | flex: platform === PLATFORM.ANDROID ? 0 : 1, 18 | minWidth: platform === PLATFORM.ANDROID ? undefined : 60 19 | }, 20 | 'NativeBase.Text': { 21 | color: variables.topTabBarTextColor, 22 | marginHorizontal: 7 23 | }, 24 | 'NativeBase.Icon': { 25 | color: variables.topTabBarTextColor, 26 | fontSize: platform === PLATFORM.IOS ? 26 : undefined 27 | }, 28 | '.active': { 29 | 'NativeBase.Text': { 30 | color: variables.topTabBarActiveTextColor, 31 | fontWeight: '600' 32 | }, 33 | 'NativeBase.Icon': { 34 | color: variables.topTabBarActiveTextColor 35 | } 36 | } 37 | }; 38 | 39 | return tabHeadingTheme; 40 | }; 41 | -------------------------------------------------------------------------------- /src/tests/components/UI/__snapshots__/Header.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`
renders with message 1`] = ` 4 | 5 | 16 | 25 | hello boy 26 | 27 | 28 | 39 | 49 | I'm here 50 | 51 | 52 | 63 | 64 | `; 65 | -------------------------------------------------------------------------------- /src/tests/components/UI/Messages.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import { render } from '@testing-library/react-native'; 4 | import Messages from '../../../components/UI/Messages'; 5 | 6 | it(' renders with error and message', () => { 7 | const Component = ; 8 | 9 | // Matches snapshot 10 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 11 | 12 | // Has the correct text on the page 13 | const { getByText } = render(Component); 14 | expect(getByText('Success')); 15 | }); 16 | 17 | it(' renders with error and message', () => { 18 | const Component = ; 19 | 20 | // Matches snapshot 21 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 22 | 23 | // Has the correct text on the page 24 | const { getByText } = render(Component); 25 | expect(getByText('Error')); 26 | }); 27 | 28 | it(' renders with info and message', () => { 29 | const Component = ; 30 | 31 | // Matches snapshot 32 | expect(renderer.create(Component).toJSON()).toMatchSnapshot(); 33 | 34 | // Has the correct text on the page 35 | const { getByText } = render(Component); 36 | expect(getByText('Warning')); 37 | }); 38 | -------------------------------------------------------------------------------- /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 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.93.0 29 | -------------------------------------------------------------------------------- /native-base-theme/components/Separator.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const theme = { 7 | '.group': { 8 | height: 50, 9 | paddingVertical: variables.listItemPadding - 8, 10 | paddingTop: variables.listItemPadding + 12, 11 | '.bordered': { 12 | height: 50, 13 | paddingVertical: variables.listItemPadding - 8, 14 | paddingTop: variables.listItemPadding + 12 15 | } 16 | }, 17 | '.bordered': { 18 | '.noTopBorder': { 19 | borderTopWidth: 0 20 | }, 21 | '.noBottomBorder': { 22 | borderBottomWidth: 0 23 | }, 24 | height: 35, 25 | paddingTop: variables.listItemPadding + 2, 26 | paddingBottom: variables.listItemPadding, 27 | borderBottomWidth: variables.borderWidth, 28 | borderTopWidth: variables.borderWidth, 29 | borderColor: variables.listBorderColor 30 | }, 31 | 'NativeBase.Text': { 32 | fontSize: variables.tabBarTextSize - 2, 33 | color: '#777' 34 | }, 35 | '.noTopBorder': { 36 | borderTopWidth: 0 37 | }, 38 | '.noBottomBorder': { 39 | borderBottomWidth: 0 40 | }, 41 | height: 38, 42 | backgroundColor: '#F0EFF5', 43 | flex: 1, 44 | justifyContent: 'center', 45 | paddingLeft: variables.listItemPadding + 5 46 | }; 47 | 48 | return theme; 49 | }; 50 | -------------------------------------------------------------------------------- /documentation/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Love to hear any feedback or tips to improve - submit an issue or a fix via a pull request. 4 | 5 | Please ensure you're following the below rules before submitting a PR: 6 | 7 | ## Naming Conventions 8 | 9 | Please follow [Airbnb's Name Conventions](https://github.com/airbnb/javascript#naming-conventions) from the style guide. 10 | 11 | ## File Structure & Naming Conventions 12 | 13 | - __Structure__ 14 | - Follow the existing file structure 15 | - __Files__ 16 | - Should be `lowercase`, with words separated by hyphens (`-`) eg. `logo-cropped.jpg` 17 | - With the exception of Containers and Components, which should be `PascalCase` - eg. `RecipeView.js` 18 | - __Directories__ 19 | - Folder names should be `lowercase,` with words separated by a hyphen (`-`) - eg. `/components/case-studies` 20 | - Folders and files can be named singlular or plural - do what sounds right 21 | - If there's more than a few files in a directory that are related, group them within their own directory 22 | - eg. if I have 2 components: `/components/RecipeListing.js` and `/components/RecipeView.js`, I may choose to create a new directory within components called `recipes` and put the 2 files within (removing `Recipes`). The result would be: `/components/recipes/Listing.js` and `/components/recipes/View.js` 23 | 24 | ## Linting 25 | 26 | Please ensure your code is passing through the linter. 27 | 28 | ## Tests 29 | 30 | Please include tests with your code and ensure your code is passing the existing tests. 31 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | fastlane_version '2.125.0' 2 | 3 | before_all do 4 | # ensure_git_branch 5 | # ensure_git_status_clean 6 | # git_pull 7 | end 8 | 9 | platform :ios do 10 | desc 'Fetch certificates and provisioning profiles' 11 | lane :certificates do 12 | match(app_identifier: 'com.ReactNativeStarterKit.app', type: 'development', readonly: true) 13 | match(app_identifier: 'com.ReactNativeStarterKit.app', type: 'appstore', readonly: true) 14 | end 15 | 16 | desc 'Build the iOS application.' 17 | private_lane :build do 18 | # certificates 19 | # increment_build_number(xcodeproj: './ios/ReactNativeStarterKit.xcodeproj') 20 | gym(scheme: 'ReactNativeStarterKit', workspace: './ios/ReactNativeStarterKit.xcworkspace') 21 | end 22 | 23 | desc 'Ship to Testflight.' 24 | lane :beta do 25 | build 26 | pilot 27 | # commit_version_bump(message: 'Bump build', xcodeproj: './ios/ReactNativeStarterKit.xcodeproj') 28 | # push_to_git_remote 29 | end 30 | end 31 | 32 | platform :android do 33 | desc 'Build the Android application.' 34 | private_lane :build do 35 | gradle(task: 'clean', project_dir: 'android/') 36 | gradle(task: 'bundle', build_type: 'Release', project_dir: 'android/') 37 | end 38 | 39 | desc 'Ship to Playstore Beta.' 40 | lane :beta do 41 | build 42 | supply(track: 'beta', track_promote_to: 'beta') 43 | # git_commit(path: ['./android/gradle.properties'], message: 'Bump versionCode') 44 | # push_to_git_remote 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Container, Content, Text, H1, H2, H3, 4 | } from 'native-base'; 5 | import Spacer from './UI/Spacer'; 6 | 7 | const About = () => ( 8 | 9 | 10 | 11 |

Heading 1

12 | 13 | 14 | Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus 15 | commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. 16 | Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. 17 | {' '} 18 | 19 | 20 | 21 |

Heading 2

22 | 23 | 24 | Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus 25 | commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. 26 | Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. 27 | {' '} 28 | 29 | 30 | 31 |

Heading 3

32 | 33 | 34 | Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus 35 | commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. 36 | Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. 37 | {' '} 38 | 39 |
40 |
41 | ); 42 | 43 | export default About; 44 | -------------------------------------------------------------------------------- /android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.reactnativestarterkit", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.reactnativestarterkit", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /documentation/file-structure.md: -------------------------------------------------------------------------------- 1 | ## File structure 2 | 3 | - `/android` - contains native code specific to the Android OS 4 | - `/documentation` - as the name suggests - any docs 5 | - `/fastlane` - configuration for auto-deploying the app to the app stores via Fastlane 6 | - `/ios` - native code specific to iOS 7 | - `/native-base-theme` - the app uses Native Base for base elements. You can edit the styles in here 8 | - `/src` - contains our JS and CSS code. `index.js` is the entry-point for our file, and is mandatory. 9 | - `/components` - 'Dumb-components' / presentational. [Read More →](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) 10 | - `/constants` - App-wide variables 11 | - `/containers` - 'Smart-components' that connect business logic to presentation [Read More →](https://redux.js.org/docs/basics/UsageWithReact.html#presentational-and-container-components) 12 | - `/images` - hmm...what could this be? 13 | - `/lib` - Utils and custom libraries 14 | - `/models` - Rematch models combining actions, reducers and state. [Read More →](https://github.com/rematch/rematch#step-2-models) 15 | - `/routes`- wire up the router with any & all screens [Read More →](https://github.com/aksonov/react-native-router-flux) 16 | - `/store`- Redux Store - hooks up the stores and provides initial/template states [Read More →](https://redux.js.org/docs/basics/Store.html) 17 | - `/tests` - contains all of our tests, where the test file matches the resptive file from `/src` 18 | - `index.js` - The starting place for our app 19 | -------------------------------------------------------------------------------- /src/lib/api.js: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-community/async-storage'; 2 | import axios from 'axios'; 3 | import Config from '../constants/config'; 4 | 5 | /** 6 | * Axios defaults 7 | */ 8 | axios.defaults.baseURL = Config.apiBaseUrl; 9 | 10 | // Headers 11 | axios.defaults.headers.common['Content-Type'] = 'application/json'; 12 | axios.defaults.headers.common.Accept = 'application/json'; 13 | 14 | /** 15 | * Request Interceptor 16 | */ 17 | axios.interceptors.request.use( 18 | async (inputConfig) => { 19 | const config = inputConfig; 20 | 21 | // Check for and add the stored Auth Token to the header request 22 | let token = ''; 23 | try { 24 | token = await AsyncStorage.getItem('@Auth:token'); 25 | } catch (error) { 26 | /* Nothing */ 27 | } 28 | if (token) { 29 | config.headers.common.Authorization = `Bearer ${token}`; 30 | } 31 | 32 | return config; 33 | }, 34 | (error) => { 35 | throw error; 36 | }, 37 | ); 38 | 39 | /** 40 | * Response Interceptor 41 | */ 42 | axios.interceptors.response.use( 43 | (res) => { 44 | // Status code isn't a success code - throw error 45 | if (!`${res.status}`.startsWith('2')) { 46 | throw res.data; 47 | } 48 | 49 | // Otherwise just return the data 50 | return res; 51 | }, 52 | (error) => { 53 | // Pass the response from the API, rather than a status code 54 | if (error && error.response && error.response.data) { 55 | throw error.response.data; 56 | } 57 | throw error; 58 | }, 59 | ); 60 | 61 | export default axios; 62 | -------------------------------------------------------------------------------- /src/containers/Articles/Single.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import Layout from '../../components/Articles/Single'; 5 | 6 | class ArticlesSingleContainer extends Component { 7 | constructor() { 8 | super(); 9 | this.state = { loading: false, error: null, article: {} }; 10 | } 11 | 12 | componentDidMount = () => this.fetchData(); 13 | 14 | /** 15 | * Fetch Data 16 | */ 17 | fetchData = async () => { 18 | const { fetchData, id } = this.props; 19 | 20 | this.setState({ loading: true, error: null }); 21 | 22 | try { 23 | const article = await fetchData(id); 24 | this.setState({ loading: false, error: null, article }); 25 | } catch (err) { 26 | this.setState({ loading: false, error: err.message, article: {} }); 27 | } 28 | }; 29 | 30 | /** 31 | * Render 32 | */ 33 | render = () => { 34 | const { loading, error, article } = this.state; 35 | 36 | return ; 37 | }; 38 | } 39 | 40 | ArticlesSingleContainer.propTypes = { 41 | fetchData: PropTypes.func.isRequired, 42 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 43 | }; 44 | 45 | ArticlesSingleContainer.defaultProps = { 46 | id: null, 47 | }; 48 | 49 | const mapStateToProps = () => ({}); 50 | 51 | const mapDispatchToProps = (dispatch) => ({ 52 | fetchData: dispatch.articles.fetchSingle, 53 | }); 54 | 55 | export default connect(mapStateToProps, mapDispatchToProps)(ArticlesSingleContainer); 56 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Provider } from 'react-redux'; 4 | import { Router, Stack } from 'react-native-router-flux'; 5 | import { PersistGate } from 'redux-persist/es/integration/react'; 6 | import SplashScreen from 'react-native-splash-screen'; 7 | 8 | import { Root, StyleProvider } from 'native-base'; 9 | import getTheme from '../native-base-theme/components'; 10 | import theme from '../native-base-theme/variables/commonColor'; 11 | 12 | import Routes from './routes/index'; 13 | import Loading from './components/UI/Loading'; 14 | 15 | class App extends React.Component { 16 | constructor() { 17 | super(); 18 | this.state = { loading: true }; 19 | } 20 | 21 | async componentDidMount() { 22 | SplashScreen.hide(); 23 | this.setState({ loading: false }); 24 | } 25 | 26 | render() { 27 | const { loading } = this.state; 28 | const { store, persistor } = this.props; 29 | 30 | if (loading) { 31 | return ; 32 | } 33 | 34 | return ( 35 | 36 | 37 | } persistor={persistor}> 38 | 39 | 40 | {Routes} 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | } 48 | } 49 | 50 | App.propTypes = { 51 | store: PropTypes.shape({}).isRequired, 52 | persistor: PropTypes.shape({}).isRequired, 53 | }; 54 | 55 | export default App; 56 | -------------------------------------------------------------------------------- /native-base-theme/components/TabBar.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const tabBarTheme = { 7 | '.tabIcon': { 8 | height: undefined 9 | }, 10 | '.vertical': { 11 | height: 60 12 | }, 13 | 'NativeBase.Button': { 14 | '.transparent': { 15 | 'NativeBase.Text': { 16 | fontSize: variables.tabFontSize, 17 | color: variables.sTabBarActiveTextColor, 18 | fontWeight: '400' 19 | }, 20 | 'NativeBase.IconNB': { 21 | color: variables.sTabBarActiveTextColor 22 | } 23 | }, 24 | 'NativeBase.IconNB': { 25 | color: variables.sTabBarActiveTextColor 26 | }, 27 | 'NativeBase.Text': { 28 | fontSize: variables.tabFontSize, 29 | color: variables.sTabBarActiveTextColor, 30 | fontWeight: '400' 31 | }, 32 | '.isTabActive': { 33 | 'NativeBase.Text': { 34 | fontWeight: '900' 35 | } 36 | }, 37 | flex: 1, 38 | alignSelf: 'stretch', 39 | alignItems: 'center', 40 | justifyContent: 'center', 41 | borderRadius: null, 42 | borderBottomColor: 'transparent', 43 | backgroundColor: variables.tabBgColor 44 | }, 45 | height: 45, 46 | flexDirection: 'row', 47 | justifyContent: 'space-around', 48 | borderWidth: 1, 49 | borderTopWidth: 0, 50 | borderLeftWidth: 0, 51 | borderRightWidth: 0, 52 | borderBottomColor: '#ccc', 53 | backgroundColor: variables.tabBgColor 54 | }; 55 | 56 | return tabBarTheme; 57 | }; 58 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; Flow doesn't support platforms 12 | .*/Libraries/Utilities/LoadingView.js 13 | 14 | [untyped] 15 | .*/node_modules/@react-native-community/cli/.*/.* 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/interface.js 21 | node_modules/react-native/flow/ 22 | 23 | [options] 24 | emoji=true 25 | 26 | exact_by_default=true 27 | 28 | module.file_ext=.js 29 | module.file_ext=.json 30 | module.file_ext=.ios.js 31 | 32 | munge_underscores=true 33 | 34 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 35 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 36 | 37 | suppress_type=$FlowIssue 38 | suppress_type=$FlowFixMe 39 | suppress_type=$FlowFixMeProps 40 | suppress_type=$FlowFixMeState 41 | 42 | [lints] 43 | sketchy-null-number=warn 44 | sketchy-null-mixed=warn 45 | sketchy-number=warn 46 | untyped-type-import=warn 47 | nonstrict-import=warn 48 | deprecated-type=warn 49 | unsafe-getters-setters=warn 50 | unnecessary-invariant=warn 51 | signature-verification-failure=warn 52 | 53 | [strict] 54 | deprecated-type 55 | nonstrict-import 56 | sketchy-null 57 | unclear-type 58 | unsafe-getters-setters 59 | untyped-import 60 | untyped-type-import 61 | 62 | [version] 63 | ^0.149.0 64 | -------------------------------------------------------------------------------- /documentation/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | ## Linting (with eslint) 4 | 5 | Want to check if your code is formatted consistently + pick up on any syntax errors: 6 | 7 | ``` 8 | ./node_modules/.bin/eslint "src/**/*.js" 9 | ``` 10 | 11 | ## Writing and Running Tests 12 | 13 | This project is set up to use [jest](https://facebook.github.io/jest/) for tests. You can configure whatever testing strategy you like, but jest works out of the box. Create test files within the directory (from root) called `/__tests__/` to have them loaded by jest. See the [the template project](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/App.test.js) for an example test. The [jest documentation](https://facebook.github.io/jest/docs/en/getting-started.html) is also a wonderful resource, as is the [React Native testing tutorial](https://facebook.github.io/jest/docs/en/tutorial-react-native.html). 14 | 15 | #### `yarn test` 16 | 17 | Runs the [jest](https://github.com/facebook/jest) test runner on your tests. 18 | 19 | ## Jest Snapshots 20 | 21 | Run `yarn test` to run a test add `-- --watch` to run it in developer mode. 22 | 23 | To run an individual Jest test: 24 | * Run `jest path/to/test.js` if you have Jest installed globally 25 | * Run `node_modules/.bin/jest path/to/test.js` to use the projects Jest installation 26 | 27 | Tests should be placed in the root `__tests__` directory, followed by their related parents folder to keep consistency, i.e `/__tests__/containers/ExampleForm.js` 28 | 29 | - (Snapshot testing) https://facebook.github.io/jest/docs/tutorial-react-native.html#snapshot-test 30 | - (DOM testing WIP) https://facebook.github.io/jest/docs/tutorial-react.html#dom-testing 31 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Scene, Tabs, Stack } from 'react-native-router-flux'; 3 | import { Icon } from 'native-base'; 4 | import DefaultProps from '../constants/navigation'; 5 | import AppConfig from '../constants/config'; 6 | 7 | import { ArticlesForm, ArticlesList, ArticlesSingle } from '../containers'; 8 | 9 | import AboutComponent from '../components/About'; 10 | 11 | const Index = ( 12 | 13 | 14 | 21 | } 25 | {...DefaultProps.navbarProps} 26 | > 27 | 28 | 29 | 30 | } 34 | {...DefaultProps.navbarProps} 35 | > 36 | 37 | 38 | 39 | 40 | } 44 | {...DefaultProps.navbarProps} 45 | > 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | 53 | export default Index; 54 | -------------------------------------------------------------------------------- /src/containers/Articles/Form.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import Layout from '../../components/Articles/Form'; 5 | 6 | class ArticlesFormContainer extends Component { 7 | constructor() { 8 | super(); 9 | this.state = { error: null, success: null, loading: false }; 10 | } 11 | 12 | /** 13 | * On Form Submission 14 | */ 15 | onFormSubmit = async (data) => { 16 | const { onFormSubmit } = this.props; 17 | 18 | this.setState({ success: null, error: null, loading: true }); 19 | 20 | try { 21 | const success = await onFormSubmit(data); 22 | this.setState({ success, error: null, loading: false }); 23 | } catch (error) { 24 | this.setState({ loading: false, success: null, error: error.message }); 25 | } 26 | } 27 | 28 | /** 29 | * Render 30 | */ 31 | render = () => { 32 | const { userInput } = this.props; 33 | const { error, loading, success } = this.state; 34 | 35 | return ( 36 | 43 | ); 44 | } 45 | } 46 | 47 | ArticlesFormContainer.propTypes = { 48 | userInput: PropTypes.shape({}).isRequired, 49 | onFormSubmit: PropTypes.func.isRequired, 50 | }; 51 | 52 | const mapStateToProps = (state) => ({ 53 | userInput: state.articles.userInput || {}, 54 | }); 55 | 56 | const mapDispatchToProps = (dispatch) => ({ 57 | onFormSubmit: dispatch.articles.save, 58 | }); 59 | 60 | export default connect(mapStateToProps, mapDispatchToProps)(ArticlesFormContainer); 61 | -------------------------------------------------------------------------------- /native-base-theme/components/Segment.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | import { PLATFORM } from './../variables/commonColor'; 5 | 6 | export default (variables /* : * */ = variable) => { 7 | const platform = variables.platform; 8 | 9 | const segmentTheme = { 10 | height: 45, 11 | borderColor: variables.segmentBorderColorMain, 12 | flexDirection: 'row', 13 | justifyContent: 'center', 14 | backgroundColor: variables.segmentBackgroundColor, 15 | 'NativeBase.Button': { 16 | alignSelf: 'center', 17 | borderRadius: 0, 18 | paddingTop: 3, 19 | paddingBottom: 3, 20 | height: 30, 21 | backgroundColor: 'transparent', 22 | borderWidth: 1, 23 | borderLeftWidth: 0, 24 | borderColor: variables.segmentBorderColor, 25 | elevation: 0, 26 | '.active': { 27 | backgroundColor: variables.segmentActiveBackgroundColor, 28 | 'NativeBase.Text': { 29 | color: variables.segmentActiveTextColor 30 | }, 31 | 'NativeBase.Icon': { 32 | color: variables.segmentActiveTextColor 33 | } 34 | }, 35 | '.first': { 36 | borderTopLeftRadius: platform === PLATFORM.IOS ? 5 : undefined, 37 | borderBottomLeftRadius: platform === PLATFORM.IOS ? 5 : undefined, 38 | borderLeftWidth: 1 39 | }, 40 | '.last': { 41 | borderTopRightRadius: platform === PLATFORM.IOS ? 5 : undefined, 42 | borderBottomRightRadius: platform === PLATFORM.IOS ? 5 : undefined 43 | }, 44 | 'NativeBase.Text': { 45 | color: variables.segmentTextColor, 46 | fontSize: 14 47 | }, 48 | 'NativeBase.Icon': { 49 | fontSize: 22, 50 | paddingTop: 0, 51 | color: variables.segmentTextColor 52 | } 53 | } 54 | }; 55 | 56 | return segmentTheme; 57 | }; 58 | -------------------------------------------------------------------------------- /src/lib/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Uppercase the first letter in a string 3 | * @param {str} str 4 | * @return {str} 5 | */ 6 | export const ucfirst = (string) => { 7 | if (!string) { 8 | return ''; 9 | } 10 | 11 | return string.charAt(0).toUpperCase() + string.slice(1); 12 | }; 13 | 14 | /** 15 | * Uppercase the first letter in a string 16 | * @param {str} input content 17 | * @param {num} number of words to cut off at 18 | * @return {str} 19 | */ 20 | export const truncate = (inputContent, numWords) => { 21 | if (!inputContent) { 22 | return ''; 23 | } 24 | const limit = !numWords ? 100 : numWords; 25 | 26 | // Trim whitespace 27 | let content = inputContent.trim(); 28 | 29 | // Convert the content into an array of words 30 | const contentArr = content.split(' '); 31 | 32 | // Remove any words above the limit 33 | content = contentArr.slice(0, limit); 34 | 35 | // Convert the array of words back into a string 36 | return `${content.join(' ')}${contentArr.length > limit ? '…' : ''}`; 37 | }; 38 | 39 | /** 40 | * Strip any HTML from a string 41 | * @param {str} string 42 | */ 43 | export const stripHtml = (string) => { 44 | let returnString = string; 45 | 46 | // Remove DOM tags 47 | returnString = returnString.replace(/<[^>]*>?/gm, ''); 48 | 49 | // Remove entities 50 | const entities = [ 51 | ['amp', '&'], 52 | ['apos', "'"], 53 | ['#x27', "'"], 54 | ['#x2F', '/'], 55 | ['#39', "'"], 56 | ['#47', '/'], 57 | ['lt', '<'], 58 | ['gt', '>'], 59 | ['nbsp', ' '], 60 | ['quot', '"'], 61 | ['hellip', '...'], 62 | ['#8217', "'"], 63 | ['#8230', '...'], 64 | ['#8211', '-'], 65 | ]; 66 | 67 | entities.map((item) => { // eslint-disable-line 68 | returnString = returnString.replace(new RegExp(`&${item[0]};`, 'g'), item[1]); 69 | }); 70 | 71 | return returnString; 72 | }; 73 | -------------------------------------------------------------------------------- /src/components/Articles/Single.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Image } from 'react-native'; 4 | import { 5 | Container, Content, Card, CardItem, Body, H3, Text, 6 | } from 'native-base'; 7 | import { Loading, Error, Spacer } from '../UI'; 8 | import { errorMessages } from '../../constants/messages'; 9 | 10 | const ArticlesSingle = ({ 11 | error, loading, article, reFetch, 12 | }) => { 13 | if (error) { 14 | return ; 15 | } 16 | 17 | if (loading) { 18 | return ; 19 | } 20 | 21 | if (Object.keys(article).length < 1) { 22 | return ; 23 | } 24 | 25 | return ( 26 | 27 | 28 | {!!article.image && ( 29 | 35 | )} 36 | 37 | 38 |

{article.name}

39 | 40 | 41 | {!!article.content && ( 42 | 43 | 44 | Content 45 | 46 | 47 | 48 | {article.content} 49 | 50 | 51 | 52 | )} 53 | 54 |
55 |
56 | ); 57 | }; 58 | 59 | ArticlesSingle.propTypes = { 60 | error: PropTypes.string, 61 | loading: PropTypes.bool, 62 | article: PropTypes.shape(), 63 | reFetch: PropTypes.func, 64 | }; 65 | 66 | ArticlesSingle.defaultProps = { 67 | error: null, 68 | loading: false, 69 | article: {}, 70 | reFetch: null, 71 | }; 72 | 73 | export default ArticlesSingle; 74 | -------------------------------------------------------------------------------- /src/tests/components/UI/__snapshots__/Messages.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders with error and message 1`] = ` 4 | 13 | 29 | Success 30 | 31 | 32 | `; 33 | 34 | exports[` renders with error and message 2`] = ` 35 | 44 | 60 | Error 61 | 62 | 63 | `; 64 | 65 | exports[` renders with info and message 1`] = ` 66 | 75 | 91 | Warning 92 | 93 | 94 | `; 95 | -------------------------------------------------------------------------------- /native-base-theme/components/Form.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default () => { 4 | const theme = { 5 | 'NativeBase.Item': { 6 | '.fixedLabel': { 7 | 'NativeBase.Label': { 8 | paddingLeft: null 9 | }, 10 | marginLeft: 15 11 | }, 12 | '.inlineLabel': { 13 | 'NativeBase.Label': { 14 | paddingLeft: null 15 | }, 16 | marginLeft: 15 17 | }, 18 | '.placeholderLabel': { 19 | 'NativeBase.Input': {} 20 | }, 21 | '.stackedLabel': { 22 | 'NativeBase.Label': { 23 | top: 5, 24 | paddingLeft: null 25 | }, 26 | 'NativeBase.Input': { 27 | paddingLeft: null, 28 | marginLeft: null 29 | }, 30 | 'NativeBase.Icon': { 31 | marginTop: 36 32 | }, 33 | marginLeft: 15 34 | }, 35 | '.floatingLabel': { 36 | 'NativeBase.Input': { 37 | paddingLeft: null, 38 | top: 10, 39 | marginLeft: null 40 | }, 41 | 'NativeBase.Label': { 42 | left: 0, 43 | top: 6 44 | }, 45 | 'NativeBase.Icon': { 46 | top: 6 47 | }, 48 | marginTop: 15, 49 | marginLeft: 15 50 | }, 51 | '.regular': { 52 | 'NativeBase.Label': { 53 | left: 0 54 | }, 55 | marginLeft: 0 56 | }, 57 | '.rounded': { 58 | 'NativeBase.Label': { 59 | left: 0 60 | }, 61 | marginLeft: 0 62 | }, 63 | '.underline': { 64 | 'NativeBase.Label': { 65 | left: 0, 66 | top: 0, 67 | position: 'relative' 68 | }, 69 | 'NativeBase.Input': { 70 | left: -15 71 | }, 72 | marginLeft: 15 73 | }, 74 | '.last': { 75 | marginLeft: 0, 76 | paddingLeft: 15 77 | }, 78 | 'NativeBase.Label': { 79 | paddingRight: 5 80 | }, 81 | marginLeft: 15 82 | } 83 | }; 84 | 85 | return theme; 86 | }; 87 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKitTests/ReactNativeStarterKitTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface ReactNativeStarterKitTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation ReactNativeStarterKitTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /documentation/faqs.md: -------------------------------------------------------------------------------- 1 | # FAQs 2 | 3 | ## Code Style Guide? 4 | 5 | We're using [Airbnb's](https://github.com/airbnb/javascript) JS/React Style Guide with ESLint linting. We just like it :) 6 | 7 | ## React, hah? How do I? 8 | 9 | [React Native Express](http://www.reactnativeexpress.com/) is a great site to get you started, specifically: 10 | 11 | - [Get your head around ES6](http://www.reactnativeexpress.com/es6) 12 | - [What is JSX?](http://www.reactnativeexpress.com/jsx) 13 | - [What are Components?](http://www.reactnativeexpress.com/components) 14 | - [React State](http://www.reactnativeexpress.com/data_component_state) 15 | - [Redux](http://www.reactnativeexpress.com/redux) 16 | - [Rematch](https://rematch.gitbooks.io/rematch/) 17 | 18 | Once you've got your head around the basics, checkout the [React Native](https://facebook.github.io/react-native/) and [React](https://reactjs.org/) websites, specifically 19 | 20 | - Go through ['The Basics'](https://facebook.github.io/react-native/docs/props.html) 21 | - Gain an understanding of the [components](https://facebook.github.io/react-native/docs/activityindicator.html) React Native provides out of the box 22 | 23 | ## How do I change the Reach Native App Icon? 24 | 25 | You might want to change the app icons for iOS and Android. You can use the [app-icon](https://github.com/dwmkerr/app-icon) utility to generate all of the required icons for each required size. 26 | 27 | ```bash 28 | npx app-icon generate -i ./src/images/app-icon.png 29 | ``` 30 | 31 | This will generate the icon in all required sizes. You can also add labels to icons, which can be useful for testing. This example labels the icon with 'beta' and the current version number: 32 | 33 | ```bash 34 | npx app-icon label -i ./src/images/app-icon.png -o temp.png --top beta --bottom $(jq .version package.json) 35 | npx app-icon generate -i temp.png 36 | ``` 37 | 38 | ![Icon Labelled with Beta and Version Number](./icon-label.png) 39 | 40 | ## How do I change the React Native App Name/Bundle ID? 41 | 42 | - Use [react-native-rename](https://www.npmjs.com/package/react-native-rename) 43 | - eg. `npx react-native-rename "The Facebook" -b com.thefacebook.mobile-app` 44 | - Open the project in Xcode and double check that the Bundle ID has been updated (if not, correct it) 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactnativestarterkit", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint ." 11 | }, 12 | "dependencies": { 13 | "@react-native-community/async-storage": "^1.12.1", 14 | "@react-native-community/masked-view": "^0.1.11", 15 | "@react-native-community/toolbar-android": "^0.2.1", 16 | "@rematch/core": "^2.1.0", 17 | "@rematch/loading": "^2.1.0", 18 | "@rematch/persist": "^2.1.0", 19 | "axios": "^0.21.3", 20 | "jsonwebtoken": "^8.5.1", 21 | "moment": "^2.29.1", 22 | "native-base": "2", 23 | "prop-types": "^15.7.2", 24 | "react": "17.0.2", 25 | "react-hook-form": "^7.15.0", 26 | "react-native": "0.65.1", 27 | "react-native-gesture-handler": "^1.10.3", 28 | "react-native-reanimated": "^2.2.0", 29 | "react-native-router-flux": "^4.3.1", 30 | "react-native-safe-area-context": "^3.3.2", 31 | "react-native-screens": "^3.6.0", 32 | "react-native-splash-screen": "^3.2.0", 33 | "react-native-vector-icons": "^8.1.0", 34 | "react-redux": "^7.2.5", 35 | "redux-persist": "^6.0.0" 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "^7.12.9", 39 | "@babel/runtime": "^7.12.5", 40 | "@testing-library/react-native": "^7.2.0", 41 | "babel-eslint": "^10.1.0", 42 | "babel-jest": "^26.6.3", 43 | "eslint": "7.14.0", 44 | "eslint-config-airbnb": "^18.2.1", 45 | "eslint-plugin-import": "^2.24.2", 46 | "eslint-plugin-jest": "^24.4.0", 47 | "eslint-plugin-jsx-a11y": "^6.4.1", 48 | "eslint-plugin-react": "^7.25.1", 49 | "jest": "^26.6.3", 50 | "metro-react-native-babel-preset": "^0.66.0", 51 | "react-native-codegen": "^0.0.7", 52 | "react-test-renderer": "17.0.2" 53 | }, 54 | "jest": { 55 | "preset": "react-native", 56 | "transformIgnorePatterns": [ 57 | "node_modules/(?!((jest-)?react-native|@react-native|react-clone-referenced-element?/.*|react-navigation|redux-persist|native-base(-shoutem-theme)|native-base|react-native-router-flux|@react-native-community/async-storage|moment|@codler))" 58 | ], 59 | "globals": { 60 | "__DEV__": true 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /native-base-theme/components/FooterTab.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const platform = variables.platform; 10 | 11 | const footerTabTheme = { 12 | 'NativeBase.Button': { 13 | '.active': { 14 | 'NativeBase.Text': { 15 | color: variables.tabBarActiveTextColor, 16 | fontSize: variables.tabBarTextSize, 17 | lineHeight: 16 18 | }, 19 | 'NativeBase.Icon': { 20 | color: variables.tabBarActiveTextColor 21 | }, 22 | 'NativeBase.IconNB': { 23 | color: variables.tabBarActiveTextColor 24 | }, 25 | backgroundColor: variables.tabActiveBgColor 26 | }, 27 | flexDirection: null, 28 | backgroundColor: 'transparent', 29 | borderColor: null, 30 | elevation: 0, 31 | shadowColor: null, 32 | shadowOffset: null, 33 | shadowRadius: null, 34 | shadowOpacity: null, 35 | alignSelf: 'center', 36 | flex: 1, 37 | height: variables.footerHeight, 38 | justifyContent: 'center', 39 | '.badge': { 40 | 'NativeBase.Badge': { 41 | 'NativeBase.Text': { 42 | fontSize: 11, 43 | fontWeight: platform === PLATFORM.IOS ? '600' : undefined, 44 | lineHeight: 14 45 | }, 46 | top: -3, 47 | alignSelf: 'center', 48 | left: 10, 49 | zIndex: 99, 50 | height: 18, 51 | padding: 1.7, 52 | paddingHorizontal: 3 53 | }, 54 | 'NativeBase.Icon': { 55 | marginTop: -18 56 | } 57 | }, 58 | 'NativeBase.Icon': { 59 | color: variables.tabBarTextColor 60 | }, 61 | 'NativeBase.IconNB': { 62 | color: variables.tabBarTextColor 63 | }, 64 | 'NativeBase.Text': { 65 | color: variables.tabBarTextColor, 66 | fontSize: variables.tabBarTextSize, 67 | lineHeight: 16 68 | } 69 | }, 70 | backgroundColor: 71 | Platform.OS === PLATFORM.ANDROID ? variables.footerDefaultBg : undefined, 72 | flexDirection: 'row', 73 | justifyContent: 'space-between', 74 | flex: 1, 75 | alignSelf: 'stretch' 76 | }; 77 | 78 | return footerTabTheme; 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/Articles/Form.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { useForm } from 'react-hook-form'; 4 | import { 5 | Container, 6 | Content, 7 | Text, 8 | Form, 9 | Item, 10 | Label, 11 | Input, 12 | Button, 13 | } from 'native-base'; 14 | import { Messages, Header, Spacer } from '../UI'; 15 | import { errorMessages } from '../../constants/messages'; 16 | 17 | const ArticlesForm = ({ 18 | error, loading, success, onFormSubmit, defaultValues, 19 | }) => { 20 | const { 21 | register, handleSubmit, errors, setValue, 22 | } = useForm({ defaultValues }); 23 | 24 | useEffect(() => { 25 | register({ name: 'email' }, { required: errorMessages.missingEmail }); 26 | }, [register]); 27 | 28 | return ( 29 | 30 | 31 |
35 | 36 | {error && } 37 | {loading && } 38 | {success && } 39 | 40 |
41 | 42 | 43 | setValue('email', value)} 50 | /> 51 | 52 | {errors.email && {errors.email.message}} 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | ); 63 | }; 64 | 65 | ArticlesForm.propTypes = { 66 | error: PropTypes.string, 67 | loading: PropTypes.bool, 68 | success: PropTypes.string, 69 | defaultValues: PropTypes.shape({ 70 | email: PropTypes.string, 71 | }), 72 | onFormSubmit: PropTypes.func.isRequired, 73 | }; 74 | 75 | ArticlesForm.defaultProps = { 76 | error: null, 77 | success: null, 78 | loading: false, 79 | defaultValues: {}, 80 | }; 81 | 82 | export default ArticlesForm; 83 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ReactNativeStarterKit 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | UIAppFonts 55 | 56 | AntDesign.ttf 57 | Entypo.ttf 58 | EvilIcons.ttf 59 | Feather.ttf 60 | FontAwesome.ttf 61 | FontAwesome5_Brands.ttf 62 | FontAwesome5_Regular.ttf 63 | FontAwesome5_Solid.ttf 64 | Fontisto.ttf 65 | Foundation.ttf 66 | Ionicons.ttf 67 | MaterialCommunityIcons.ttf 68 | MaterialIcons.ttf 69 | Octicons.ttf 70 | Roboto_medium.ttf 71 | Roboto.ttf 72 | rubicon-icon-font.ttf 73 | SimpleLineIcons.ttf 74 | Zocial.ttf 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | #import "RNSplashScreen.h" 7 | 8 | #ifdef FB_SONARKIT_ENABLED 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | static void InitializeFlipper(UIApplication *application) { 17 | FlipperClient *client = [FlipperClient sharedClient]; 18 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 19 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 20 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 21 | [client addPlugin:[FlipperKitReactPlugin new]]; 22 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 23 | [client start]; 24 | } 25 | #endif 26 | 27 | @implementation AppDelegate 28 | 29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 30 | { 31 | #ifdef FB_SONARKIT_ENABLED 32 | InitializeFlipper(application); 33 | #endif 34 | 35 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 36 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 37 | moduleName:@"ReactNativeStarterKit" 38 | initialProperties:nil]; 39 | 40 | if (@available(iOS 13.0, *)) { 41 | rootView.backgroundColor = [UIColor systemBackgroundColor]; 42 | } else { 43 | rootView.backgroundColor = [UIColor whiteColor]; 44 | } 45 | 46 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 47 | UIViewController *rootViewController = [UIViewController new]; 48 | rootViewController.view = rootView; 49 | self.window.rootViewController = rootViewController; 50 | [self.window makeKeyAndVisible]; 51 | [RNSplashScreen show]; 52 | return YES; 53 | } 54 | 55 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 56 | { 57 | #if DEBUG 58 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 59 | #else 60 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 61 | #endif 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/reactnativestarterkit/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.reactnativestarterkit; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactInstanceManager; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = 17 | new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | @SuppressWarnings("UnnecessaryLocalVariable") 26 | List packages = new PackageList(this).getPackages(); 27 | // Packages that cannot be autolinked yet can be added manually here, for example: 28 | // packages.add(new MyReactNativePackage()); 29 | return packages; 30 | } 31 | 32 | @Override 33 | protected String getJSMainModuleName() { 34 | return "index"; 35 | } 36 | }; 37 | 38 | @Override 39 | public ReactNativeHost getReactNativeHost() { 40 | return mReactNativeHost; 41 | } 42 | 43 | @Override 44 | public void onCreate() { 45 | super.onCreate(); 46 | SoLoader.init(this, /* native exopackage */ false); 47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 48 | } 49 | 50 | /** 51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 53 | * 54 | * @param context 55 | * @param reactInstanceManager 56 | */ 57 | private static void initializeFlipper( 58 | Context context, ReactInstanceManager reactInstanceManager) { 59 | if (BuildConfig.DEBUG) { 60 | try { 61 | /* 62 | We use reflection here to pick up the class that initializes Flipper, 63 | since Flipper library is not available in release mode 64 | */ 65 | Class aClass = Class.forName("com.reactnativestarterkit.ReactNativeFlipper"); 66 | aClass 67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 68 | .invoke(null, context, reactInstanceManager); 69 | } catch (ClassNotFoundException e) { 70 | e.printStackTrace(); 71 | } catch (NoSuchMethodException e) { 72 | e.printStackTrace(); 73 | } catch (IllegalAccessException e) { 74 | e.printStackTrace(); 75 | } catch (InvocationTargetException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/containers/Articles/List.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | import Layout from '../../components/Articles/List'; 5 | 6 | class ArticlesListContainer extends Component { 7 | constructor(props) { 8 | super(); 9 | 10 | // Prioritize (web) page route over last meta value 11 | const page = props.page || props.meta.page; 12 | 13 | this.state = { 14 | error: null, loading: false, page: parseInt(page, 10) || 1, 15 | }; 16 | } 17 | 18 | componentDidMount = () => this.fetchData(); 19 | 20 | /** 21 | * If the page prop changes, update state 22 | */ 23 | componentDidUpdate = (prevProps) => { 24 | const { page } = this.props; 25 | const { page: prevPage } = prevProps; 26 | 27 | if (page !== prevPage) { 28 | // eslint-disable-next-line 29 | this.setState({ 30 | error: null, loading: false, page: parseInt(page, 10) || 1, 31 | }, this.fetchData); 32 | } 33 | } 34 | 35 | /** 36 | * Fetch Data 37 | */ 38 | fetchData = async ({ forceSync = false, incrementPage = false } = {}) => { 39 | const { fetchData } = this.props; 40 | 41 | let { page } = this.state; 42 | page = incrementPage ? page + 1 : page; // Force fetch the next page worth of data when requested 43 | page = forceSync ? 1 : page; // Start from scratch 44 | 45 | this.setState({ loading: true, error: null, page }); 46 | 47 | try { 48 | await fetchData({ forceSync, page }); 49 | this.setState({ loading: false, error: null }); 50 | } catch (err) { 51 | this.setState({ loading: false, error: err.message }); 52 | } 53 | }; 54 | 55 | /** 56 | * Render 57 | */ 58 | render = () => { 59 | const { 60 | listFlat, listPaginated, pagination, meta, 61 | } = this.props; 62 | const { loading, error, page } = this.state; 63 | 64 | return ( 65 | 75 | ); 76 | }; 77 | } 78 | 79 | ArticlesListContainer.propTypes = { 80 | listFlat: PropTypes.arrayOf(PropTypes.shape({})).isRequired, 81 | listPaginated: PropTypes.shape({}).isRequired, 82 | meta: PropTypes.shape({ 83 | page: PropTypes.number, 84 | }).isRequired, 85 | fetchData: PropTypes.func.isRequired, 86 | pagination: PropTypes.arrayOf(PropTypes.shape()).isRequired, 87 | page: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 88 | }; 89 | 90 | ArticlesListContainer.defaultProps = { 91 | page: 1, 92 | }; 93 | 94 | const mapStateToProps = (state) => ({ 95 | listFlat: state.articles.listFlat || [], 96 | listPaginated: state.articles.listPaginated || {}, 97 | meta: state.articles.meta || [], 98 | pagination: state.articles.pagination || {}, 99 | }); 100 | 101 | const mapDispatchToProps = (dispatch) => ({ 102 | fetchData: dispatch.articles.fetchList, 103 | }); 104 | 105 | export default connect(mapStateToProps, mapDispatchToProps)(ArticlesListContainer); 106 | -------------------------------------------------------------------------------- /android/app/src/debug/java/com/reactnativestarterkit/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.reactnativestarterkit; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /native-base-theme/components/Footer.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | import { PLATFORM } from './../variables/commonColor'; 5 | 6 | export default (variables /* : * */ = variable) => { 7 | const platformStyle = variables.platformStyle; 8 | const platform = variables.platform; 9 | 10 | const iconCommon = { 11 | 'NativeBase.Icon': { 12 | color: variables.tabBarActiveTextColor 13 | } 14 | }; 15 | const iconNBCommon = { 16 | 'NativeBase.IconNB': { 17 | color: variables.tabBarActiveTextColor 18 | } 19 | }; 20 | const textCommon = { 21 | 'NativeBase.Text': { 22 | color: variables.tabBarActiveTextColor 23 | } 24 | }; 25 | const footerTheme = { 26 | 'NativeBase.Left': { 27 | 'NativeBase.Button': { 28 | '.transparent': { 29 | backgroundColor: 'transparent', 30 | borderColor: null, 31 | elevation: 0, 32 | shadowColor: null, 33 | shadowOffset: null, 34 | shadowRadius: null, 35 | shadowOpacity: null, 36 | ...iconCommon, 37 | ...iconNBCommon, 38 | ...textCommon 39 | }, 40 | alignSelf: null, 41 | ...iconCommon, 42 | ...iconNBCommon 43 | // ...textCommon 44 | }, 45 | flex: 1, 46 | alignSelf: 'center', 47 | alignItems: 'flex-start' 48 | }, 49 | 'NativeBase.Body': { 50 | flex: 1, 51 | alignItems: 'center', 52 | alignSelf: 'center', 53 | flexDirection: 'row', 54 | 'NativeBase.Button': { 55 | alignSelf: 'center', 56 | '.transparent': { 57 | backgroundColor: 'transparent', 58 | borderColor: null, 59 | elevation: 0, 60 | shadowColor: null, 61 | shadowOffset: null, 62 | shadowRadius: null, 63 | shadowOpacity: null, 64 | ...iconCommon, 65 | ...iconNBCommon, 66 | ...textCommon 67 | }, 68 | '.full': { 69 | height: variables.footerHeight, 70 | paddingBottom: variables.footerPaddingBottom, 71 | flex: 1 72 | }, 73 | ...iconCommon, 74 | ...iconNBCommon 75 | // ...textCommon 76 | } 77 | }, 78 | 'NativeBase.Right': { 79 | 'NativeBase.Button': { 80 | '.transparent': { 81 | backgroundColor: 'transparent', 82 | borderColor: null, 83 | elevation: 0, 84 | shadowColor: null, 85 | shadowOffset: null, 86 | shadowRadius: null, 87 | shadowOpacity: null, 88 | ...iconCommon, 89 | ...iconNBCommon, 90 | ...textCommon 91 | }, 92 | alignSelf: null, 93 | ...iconCommon, 94 | ...iconNBCommon 95 | // ...textCommon 96 | }, 97 | flex: 1, 98 | alignSelf: 'center', 99 | alignItems: 'flex-end' 100 | }, 101 | backgroundColor: variables.footerDefaultBg, 102 | flexDirection: 'row', 103 | justifyContent: 'center', 104 | borderTopWidth: 105 | platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL 106 | ? variables.borderWidth 107 | : undefined, 108 | borderColor: 109 | platform === PLATFORM.IOS && platformStyle !== PLATFORM.MATERIAL 110 | ? '#cbcbcb' 111 | : undefined, 112 | height: variables.footerHeight, 113 | paddingBottom: variables.footerPaddingBottom, 114 | elevation: 3, 115 | left: 0, 116 | right: 0 117 | }; 118 | return footerTheme; 119 | }; 120 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit.xcodeproj/xcshareddata/xcschemes/ReactNativeStarterKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/components/Articles/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Actions } from 'react-native-router-flux'; 4 | import { FlatList, TouchableOpacity, Image } from 'react-native'; 5 | import { 6 | Container, Card, CardItem, Body, Text, Button, 7 | } from 'native-base'; 8 | import { Error, Spacer } from '../UI'; 9 | import { errorMessages } from '../../constants/messages'; 10 | 11 | const ArticlesList = ({ 12 | error, loading, listFlat, reFetch, meta, 13 | }) => { 14 | if (error) { 15 | return ; 16 | } 17 | 18 | if (listFlat.length < 1) { 19 | return ; 20 | } 21 | 22 | return ( 23 | 24 | reFetch({ forceSync: true })} 27 | refreshing={loading} 28 | renderItem={({ item }) => ( 29 | 30 | ( 33 | !item.placeholder 34 | ? Actions.articlesSingle({ id: item.id, title: item.name }) 35 | : null 36 | )} 37 | style={{ flex: 1 }} 38 | > 39 | 40 | {!!item.image && ( 41 | 53 | )} 54 | 55 | 56 | 57 | 58 | {item.name} 59 | 60 | {!!item.excerpt && {item.excerpt}} 61 | 62 | 63 | 64 | 65 | 66 | )} 67 | keyExtractor={(item) => `${item.id}-${item.name}`} 68 | ListFooterComponent={(meta && meta.page && meta.lastPage && meta.page < meta.lastPage) 69 | ? () => ( 70 | 71 | 72 | 79 | 80 | ) : null} 81 | /> 82 | 83 | 84 | 85 | ); 86 | }; 87 | 88 | ArticlesList.propTypes = { 89 | error: PropTypes.string, 90 | loading: PropTypes.bool, 91 | listFlat: PropTypes.arrayOf( 92 | PropTypes.shape({ 93 | placeholder: PropTypes.bool, 94 | id: PropTypes.number, 95 | name: PropTypes.string, 96 | date: PropTypes.string, 97 | content: PropTypes.string, 98 | excerpt: PropTypes.string, 99 | image: PropTypes.string, 100 | }), 101 | ), 102 | reFetch: PropTypes.func, 103 | meta: PropTypes.shape({ page: PropTypes.number, lastPage: PropTypes.number }), 104 | }; 105 | 106 | ArticlesList.defaultProps = { 107 | listFlat: [], 108 | error: null, 109 | reFetch: null, 110 | meta: { page: null, lastPage: null }, 111 | loading: false, 112 | }; 113 | 114 | export default ArticlesList; 115 | -------------------------------------------------------------------------------- /native-base-theme/components/InputGroup.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import variable from './../variables/platform'; 4 | 5 | export default (variables /* : * */ = variable) => { 6 | const inputGroupTheme = { 7 | 'NativeBase.Icon': { 8 | fontSize: 24, 9 | color: variables.sTabBarActiveTextColor, 10 | paddingHorizontal: 5 11 | }, 12 | 'NativeBase.IconNB': { 13 | fontSize: 24, 14 | color: variables.sTabBarActiveTextColor, 15 | paddingHorizontal: 5 16 | }, 17 | 'NativeBase.Input': { 18 | height: variables.inputHeightBase, 19 | color: variables.inputColor, 20 | paddingLeft: 5, 21 | paddingRight: 5, 22 | flex: 1, 23 | fontSize: variables.inputFontSize, 24 | lineHeight: variables.inputLineHeight 25 | }, 26 | '.underline': { 27 | '.success': { 28 | borderColor: variables.inputSuccessBorderColor 29 | }, 30 | '.error': { 31 | borderColor: variables.inputErrorBorderColor 32 | }, 33 | paddingLeft: 5, 34 | borderWidth: variables.borderWidth, 35 | borderTopWidth: 0, 36 | borderRightWidth: 0, 37 | borderLeftWidth: 0, 38 | borderColor: variables.inputBorderColor 39 | }, 40 | '.regular': { 41 | '.success': { 42 | borderColor: variables.inputSuccessBorderColor 43 | }, 44 | '.error': { 45 | borderColor: variables.inputErrorBorderColor 46 | }, 47 | paddingLeft: 5, 48 | borderWidth: variables.borderWidth, 49 | borderColor: variables.inputBorderColor 50 | }, 51 | '.rounded': { 52 | '.success': { 53 | borderColor: variables.inputSuccessBorderColor 54 | }, 55 | '.error': { 56 | borderColor: variables.inputErrorBorderColor 57 | }, 58 | paddingLeft: 5, 59 | borderWidth: variables.borderWidth, 60 | borderRadius: variables.inputGroupRoundedBorderRadius, 61 | borderColor: variables.inputBorderColor 62 | }, 63 | 64 | '.success': { 65 | 'NativeBase.Icon': { 66 | color: variables.inputSuccessBorderColor 67 | }, 68 | 'NativeBase.IconNB': { 69 | color: variables.inputSuccessBorderColor 70 | }, 71 | '.rounded': { 72 | borderRadius: 30, 73 | borderColor: variables.inputSuccessBorderColor 74 | }, 75 | '.regular': { 76 | borderColor: variables.inputSuccessBorderColor 77 | }, 78 | '.underline': { 79 | borderWidth: variables.borderWidth, 80 | borderTopWidth: 0, 81 | borderRightWidth: 0, 82 | borderLeftWidth: 0, 83 | borderColor: variables.inputSuccessBorderColor 84 | }, 85 | borderColor: variables.inputSuccessBorderColor 86 | }, 87 | 88 | '.error': { 89 | 'NativeBase.Icon': { 90 | color: variables.inputErrorBorderColor 91 | }, 92 | 'NativeBase.IconNB': { 93 | color: variables.inputErrorBorderColor 94 | }, 95 | '.rounded': { 96 | borderRadius: 30, 97 | borderColor: variables.inputErrorBorderColor 98 | }, 99 | '.regular': { 100 | borderColor: variables.inputErrorBorderColor 101 | }, 102 | '.underline': { 103 | borderWidth: variables.borderWidth, 104 | borderTopWidth: 0, 105 | borderRightWidth: 0, 106 | borderLeftWidth: 0, 107 | borderColor: variables.inputErrorBorderColor 108 | }, 109 | borderColor: variables.inputErrorBorderColor 110 | }, 111 | '.disabled': { 112 | 'NativeBase.Icon': { 113 | color: '#384850' 114 | }, 115 | 'NativeBase.IconNB': { 116 | color: '#384850' 117 | } 118 | }, 119 | 120 | paddingLeft: 5, 121 | borderWidth: variables.borderWidth, 122 | borderTopWidth: 0, 123 | borderRightWidth: 0, 124 | borderLeftWidth: 0, 125 | borderColor: variables.inputBorderColor, 126 | backgroundColor: 'transparent', 127 | flexDirection: 'row', 128 | alignItems: 'center' 129 | }; 130 | 131 | return inputGroupTheme; 132 | }; 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | React Native Starter Kit 3 |

4 |

React Native App

5 |

6 | 7 | 8 | builds 9 | 10 | 11 | license 12 | 13 | 14 |
15 |

16 | What is this? 17 |   —   18 | Usage 19 |   —   20 | Docs 21 |   —   22 | Need help? 23 |

24 |
25 |
26 | 27 | --- 28 | 29 | ### Looking for something else? 30 | 31 | - [React Native Starter Kit (Expo) / Boilerplate](https://github.com/mcnamee/react-native-expo-starter-kit) 32 | - [React Starter Kit (web) / Boilerplate](https://github.com/mcnamee/react-starter-kit) 33 | - [Previous Version (React Starter Kit (Web + Native) w/ Firebase)](https://github.com/mcnamee/react-native-starter-kit/tree/archive/v3) 34 | 35 | --- 36 | 37 | ## 👋 Intro 38 | 39 | This project was bootstrapped with the [React Boilerplate Builder](https://github.com/mcnamee/react-native-boilerplate-builder) by [Matt McNamee](https://mcnam.ee). 40 | 41 | The project is _super_ helpful to kick-start your next project, as it provides a lot of the common tools you may reach for, all ready to go. Specifically: 42 | 43 | - __Flux architecture__ 44 | - [Redux](https://redux.js.org/docs/introduction/) 45 | - Redux Wrapper: [Rematch](https://github.com/rematch/rematch) 46 | - __Routing and navigation__ 47 | - [React Native Router Flux](https://github.com/aksonov/react-native-router-flux) for native mobile navigation 48 | - __Data Caching / Offline__ 49 | - [Redux Persist](https://github.com/rt2zz/redux-persist) 50 | - __UI Toolkit/s__ 51 | - [Native Base](https://nativebase.io/) for native mobile 52 | - __Code Linting__ with 53 | - [Airbnb's JS Linting](https://github.com/airbnb/javascript) guidelines 54 | - __Deployment strategy__ 55 | - [Both manual and automated strategies](documentation/deploy.md) 56 | - __Splash Screen + Assets__ 57 | - [React Native Splash Screen](https://github.com/crazycodeboy/react-native-splash-screen) 58 | 59 | --- 60 | 61 | ## 🚀 Getting Started 62 | 63 | - Install [React Native Debugger](https://github.com/jhen0409/react-native-debugger/releases) and open before running the app 64 | - Install `eslint`, `prettier` and `editor config` plugins into your IDE 65 | - Ensure your machine has the [React Native dependencies installed](https://facebook.github.io/react-native/docs/getting-started) 66 | 67 | ```bash 68 | # Install dependencies 69 | yarn install && ( cd ios && pod install ) 70 | ``` 71 | 72 | #### iOS 73 | 74 | ```bash 75 | # Start in the iOS Simulator 76 | npx react-native run-ios --simulator="iPhone 11" 77 | ``` 78 | 79 | #### Android 80 | 81 | ```bash 82 | # Start in the Android Simulator 83 | # - Note: open Android Studio > Tools > AVD > Run a device 84 | # - Example device specs: https://medium.com/pvtl/react-native-android-development-on-mac-ef7481f65e47#d5da 85 | npx react-native run-android 86 | ``` 87 | 88 | --- 89 | 90 | ## 📖 Docs 91 | 92 | - [Contributing to this project](documentation/contributing.md) 93 | - [FAQs & Opinions](documentation/faqs.md) 94 | - [Tests & testing](documentation/testing.md) 95 | - [Understanding the file structure](documentation/file-structure.md) 96 | - [Deploy the app](documentation/deploy.md) 97 | 98 | --- 99 | 100 | ## 👊 Further Help? 101 | 102 | This repo is a great place to start. But...if you'd prefer to sit back and have your new project built for you or just need some consultation, [get in touch with me directly](https://mcnam.ee) and I can organise a quote. 103 | -------------------------------------------------------------------------------- /ios/ReactNativeStarterKit/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/tests/models/articles.test.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import Api from '../../lib/api'; 3 | import model from '../../models/articles'; 4 | import { successMessages } from '../../constants/messages'; 5 | 6 | /** 7 | * Mocks 8 | */ 9 | jest.mock('axios'); 10 | afterEach(jest.resetAllMocks); 11 | 12 | const mockInput = { 13 | title: { rendered: 'hello world' }, 14 | content: { rendered: '

Hello there fellows

' }, 15 | excerpt: { rendered: '

Hello there fellows

' }, 16 | _embedded: { 17 | 'wp:featuredmedia': [{ 18 | media_details: { 19 | sizes: { 20 | full: { 21 | source_url: 'https://www.digitalsupply.co/wp-content/uploads/2018/03/glacier-blue.jpg', 22 | }, 23 | }, 24 | }, 25 | }], 26 | }, 27 | date: '2017-04-14T15:32:29', 28 | slug: 'using-open-source-software-to-build-your-instagram-followers', 29 | link: 'https://www.digitalsupply.co/using-open-source-software-to-build-your-instagram-followers/', 30 | }; 31 | 32 | const mockOutput = { 33 | id: 0, 34 | name: 'Hello world', 35 | content: 'Hello there fellows', 36 | contentRaw: '

Hello there fellows

', 37 | excerpt: 'Hello there fellows', 38 | image: 'https://www.digitalsupply.co/wp-content/uploads/2018/03/glacier-blue.jpg', 39 | date: '14th Apr 2017', 40 | slug: 'using-open-source-software-to-build-your-instagram-followers', 41 | link: 'https://www.digitalsupply.co/using-open-source-software-to-build-your-instagram-followers/', 42 | }; 43 | 44 | /** 45 | * Tests 46 | */ 47 | it('Articles fetchList() returns correctly', async () => { 48 | Api.get.mockResolvedValue({ data: [mockInput], headers: { 'x-wp-totalpages': 3 } }); 49 | const initialState = { articles: { lastSync: {} } }; 50 | const dispatch = { articles: { replace: jest.fn((res) => res) } }; 51 | 52 | await model.effects(dispatch).fetchList({ page: 2 }, initialState).then((res) => { 53 | expect(Api.get).toHaveBeenCalledWith('/v2/posts?per_page=4&page=2&orderby=modified&_embed'); 54 | expect(dispatch.articles.replace).toHaveBeenCalledTimes(1); 55 | expect(res).toEqual({ 56 | data: [mockInput], 57 | headers: { 'x-wp-totalpages': 3 }, 58 | page: 2, 59 | }); 60 | }); 61 | }); 62 | 63 | it('Articles fetchList() does not go to API if lastSync just set', async () => { 64 | const initialState = { articles: { lastSync: { 2: moment() } } }; 65 | await model.effects().fetchList({ page: 2 }, initialState).then((res) => expect(res).toEqual(true)); 66 | }); 67 | 68 | it('Articles fetchSingle() returns correctly', async () => { 69 | Api.get.mockResolvedValue({ data: mockInput }); 70 | 71 | await model.effects().fetchSingle(222).then((res) => { 72 | expect(Api.get).toHaveBeenCalledWith('/v2/posts/222?_embed'); 73 | expect(res).toEqual(mockOutput); 74 | }); 75 | }); 76 | 77 | it('Articles Model returns correctly', () => { 78 | expect(model.reducers.replace({}, { 79 | page: 1, 80 | headers: { 81 | 'x-wp-totalpages': 2, 82 | 'x-wp-total': 2, 83 | }, 84 | data: [mockInput], 85 | })).toMatchObject({ 86 | meta: { 87 | lastPage: 2, 88 | total: 2, 89 | }, 90 | pagination: [ 91 | { title: 1, link: '/articles/' }, 92 | { title: 2, link: '/articles/2' }, 93 | ], 94 | listPaginated: { 95 | 1: [mockOutput], 96 | }, 97 | }); 98 | }); 99 | 100 | it('Articles save() returns correctly', () => { 101 | const dispatch = { articles: { replaceUserInput: jest.fn((res) => res) } }; 102 | 103 | model.effects(dispatch).save('hello@hello.com').then((res) => { 104 | expect(res).toEqual(successMessages.defaultForm); 105 | }); 106 | }); 107 | 108 | it('Articles Model Save returns correctly', () => { 109 | expect(model.reducers.replaceUserInput({ 110 | meta: { 111 | lastPage: 2, 112 | total: 2, 113 | }, 114 | pagination: [ 115 | { title: 1, link: '/articles/' }, 116 | { title: 2, link: '/articles/2' }, 117 | ], 118 | listPaginated: { 119 | 1: [mockOutput], 120 | }, 121 | }, 'hello@hello.com')).toMatchObject({ 122 | meta: { 123 | lastPage: 2, 124 | total: 2, 125 | }, 126 | pagination: [ 127 | { title: 1, link: '/articles/' }, 128 | { title: 2, link: '/articles/2' }, 129 | ], 130 | listPaginated: { 131 | 1: [mockOutput], 132 | }, 133 | userInput: 'hello@hello.com', 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /documentation/deploy.md: -------------------------------------------------------------------------------- 1 | # 🚀 Deploying 2 | 3 | ## Setting up a new app: 4 | 5 | The following steps should be followed for new projects. Once completed for your project, you won't need these steps again. 6 | 7 | *General* 8 | 9 | 1. Ensure you have admin access to the destination Google Play and Apple/iTunesConnect Developer accounts 10 | 1. Ensure you've named your app correctly and set a unique bundle identifier: 11 | - Use [react-native-rename](https://www.npmjs.com/package/react-native-rename) 12 | - eg. `react-native-rename "Travel App" -b com.junedomingo.travelapp` 13 | - Open the project in Xcode and double check that the Bundle ID has been updated (if not, correct it) 14 | 1. In both Google Play and iTunes Connect: 15 | - Setup a new app 16 | - Use the _manual_ method below to build and deploy the app for the first time 17 | - _iOS Note: when you deploy the iOS app for the first time, you'll select 'Automatic Key Management'. Xcode will generate a private distribution key. Ensure you save this (eg. to a password safe) so that others can distribute the app too_ 18 | 19 | *Android* 20 | 21 | 1. Generate/configure Android key: 22 | - `( cd android/app && keytool -genkeypair -v -keystore android-release-key.keystore -alias jims-app-release-key -keyalg RSA -keysize 2048 -validity 10000 )` (note: change `jims-app-release-key` to your own alias) 23 | - Save the key to a secure password safe (don't commit it to the repo) 24 | 1. [Setup the Gradle variables](https://reactnative.dev/docs/signed-apk-android#setting-up-gradle-variables), using the alias and password/s (that you set in the previous command) in: `android/gradle.properties` 25 | 1. [Add the release signing config to your app's Gradle config](https://reactnative.dev/docs/signed-apk-android#adding-signing-config-to-your-apps-gradle-config) in: `android/app/build.gradle` 26 | 27 | *Fastlane* 28 | 29 | 1. Using the __account owner's__ login (i.e. we want to create the API credentials from the owner's account) - follow the [steps here](https://docs.fastlane.tools/actions/supply/#setup) to generate API credentials for Google Play. Download and place the json file here: `android/app/google-play-android-developer.json`. Save the key to a secure password safe (don't commit it to the repo) 30 | 1. Update the `package_name` and `itc_team_id` (App Store Connect Team ID) in `faslane/Appfile` to match the bundle of your app 31 | 1. Update the following in `fastlane/Fastfile`: 32 | - `app_identifier: com.app.bundle` - where com.app.bundle is your bundle id 33 | - `name.xcodeproj` - to the name of your Xcode project file 34 | - `scheme: 'name'` - where name is your scheme (eg. AppName) 35 | 1. Run `fastlane supply init` (which will download the meta data of the uploaded app, from the stores) 36 | 37 | --- 38 | 39 | ## Configuring your machine to deploy: 40 | 41 | The following steps are provided for developers who have the project setup on their machine, but have not yet deployed the app. Follow these once, and you won't need these steps again. 42 | 43 | 1. Android (Google Play): 44 | - Add the Android keys (found in the password safe) to your local project: 45 | - `android/app/android-release-key.keystore` 46 | - `android/app/google-play-android-developer.json` 47 | - [Android/Google dependencies](https://facebook.github.io/react-native/docs/getting-started#installing-dependencies-1) 48 | 1. iOS (Apple iTunes Connect): 49 | - In Xcode, login to the appropriate account to give you access to deploy 50 | - Install the appropriate distribution private key (found in your password safe) 51 | - Download the file and double click it to add to Keychain 52 | 1. Fastlane (for automated deployments on both platforms): 53 | - Install Fastlane - `brew cask install fastlane` 54 | - Install Xcode command line tools - `xcode-select --install` 55 | 56 | --- 57 | 58 | ## Deploying 59 | 60 | - Update the __app version__ - `bash fastlane/update-app-version.sh` 61 | - __Merge__ `develop` branch into `master` branch with a _merge commit_ 62 | - Git __Tag__ the master merge commit. The tag name should be the new version number 63 | - Bundle and deploy by the following: 64 | 65 | ### 1.0 (Automated) Fastlane 66 | 67 | Fastlane automatically builds and deploys the app to the app stores (TestFlight and Play Store Beta). 68 | 69 | 1. _Hint: Did you update the version number, merge to master and tag?_ 70 | 1. __iOS__: Deploy to Apple TestFlight - `fastlane ios beta` 71 | 1. __Android__: Deploy to Google Play Beta - `fastlane android beta` 72 | 73 | ### 2.0 Manual 74 | 75 | *2.2.1 iOS* 76 | 77 | _*Note: it may be required to use the legacy build system (XCode -> File -> Project Settings -> Change the build system to 'Legacy Build System')_ 78 | 79 | 1. _Hint: Did you update the version number, merge to master and tag?_ 80 | 1. Ensure you've changed the Xcode 'Build Config' to Release 81 | 1. Select 'Generic iOS Device' from devices 82 | 1. Product > Archive 83 | 1. Open Organiser 84 | - Find the archive and click 'Validate' to check that it's ok 85 | - Click the big 'Upload to App Store...' when ready (untick BitCode checkbox) 86 | 87 | *2.2.2 Android* 88 | 89 | 1. _Hint: Did you update the version number, merge to master and tag?_ 90 | 1. `( cd android && ./gradlew app:bundleRelease )` 91 | 1. Upload the generated file (`/android/app/build/outputs/bundle/release/app.aab`) to Google Play 92 | -------------------------------------------------------------------------------- /src/models/articles.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import Api from '../lib/api'; 3 | import HandleErrorMessage from '../lib/format-error-messages'; 4 | import initialState from '../store/articles'; 5 | import Config from '../constants/config'; 6 | import { getFeaturedImageUrl } from '../lib/images'; 7 | import { ucfirst, stripHtml } from '../lib/string'; 8 | import { errorMessages, successMessages } from '../constants/messages'; 9 | import pagination from '../lib/pagination'; 10 | 11 | /** 12 | * Transform the endpoint data structure into our redux store format 13 | * @param {obj} item 14 | */ 15 | const transform = (item) => ({ 16 | id: item.id || 0, 17 | name: item.title && item.title.rendered ? ucfirst(stripHtml(item.title.rendered)) : '', 18 | content: item.content && item.content.rendered ? stripHtml(item.content.rendered) : '', 19 | contentRaw: item.content && item.content.rendered, 20 | excerpt: item.excerpt && item.excerpt.rendered ? stripHtml(item.excerpt.rendered) : '', 21 | date: moment(item.date).format(Config.dateFormat) || '', 22 | slug: item.slug || null, 23 | link: item.link || null, 24 | image: getFeaturedImageUrl(item), 25 | }); 26 | 27 | export default { 28 | namespace: 'articles', 29 | 30 | /** 31 | * Initial state 32 | */ 33 | state: initialState, 34 | 35 | /** 36 | * Effects/Actions 37 | */ 38 | effects: (dispatch) => ({ 39 | /** 40 | * Get a list from the API 41 | * @param {obj} rootState 42 | * @returns {Promise} 43 | */ 44 | async fetchList({ forceSync = false, page = 1 } = {}, rootState) { 45 | const { articles = {} } = rootState; 46 | const { lastSync = {}, meta = {} } = articles; 47 | const { lastPage } = meta; 48 | 49 | // Only sync when it's been 5mins since last sync 50 | if (lastSync[page]) { 51 | if (!forceSync && moment().isBefore(moment(lastSync[page]).add(5, 'minutes'))) { 52 | return true; 53 | } 54 | } 55 | 56 | // We've reached the end of the list 57 | if (page && lastPage && page > lastPage) { 58 | throw HandleErrorMessage({ message: `Page ${page} does not exist` }); 59 | } 60 | 61 | try { 62 | const response = await Api.get(`/v2/posts?per_page=4&page=${page}&orderby=modified&_embed`); 63 | const { data, headers } = response; 64 | 65 | return !data || data.length < 1 66 | ? true 67 | : dispatch.articles.replace({ data, headers, page }); 68 | } catch (error) { 69 | throw HandleErrorMessage(error); 70 | } 71 | }, 72 | 73 | /** 74 | * Get a single item from the API 75 | * @param {number} id 76 | * @returns {Promise[obj]} 77 | */ 78 | async fetchSingle(id) { 79 | try { 80 | const response = await Api.get(`/v2/posts/${id}?_embed`); 81 | const { data } = response; 82 | 83 | if (!data) { 84 | throw new Error({ message: errorMessages.articles404 }); 85 | } 86 | 87 | return transform(data); 88 | } catch (error) { 89 | throw HandleErrorMessage(error); 90 | } 91 | }, 92 | 93 | /** 94 | * Save date to redux store 95 | * @param {obj} data 96 | * @returns {Promise[obj]} 97 | */ 98 | async save(data) { 99 | try { 100 | if (Object.keys(data).length < 1) { 101 | throw new Error({ message: errorMessages.missingData }); 102 | } 103 | 104 | dispatch.articles.replaceUserInput(data); 105 | return successMessages.defaultForm; // Message for the UI 106 | } catch (error) { 107 | throw HandleErrorMessage(error); 108 | } 109 | }, 110 | }), 111 | 112 | /** 113 | * Reducers 114 | */ 115 | reducers: { 116 | /** 117 | * Replace list in store 118 | * @param {obj} state 119 | * @param {obj} payload 120 | */ 121 | replace(state, payload) { 122 | let newList = null; 123 | const { data, headers, page } = payload; 124 | 125 | // Loop data array, saving items in a usable format 126 | if (data && typeof data === 'object') { 127 | newList = data.map((item) => transform(item)); 128 | } 129 | 130 | // Create our paginated and flat lists 131 | const listPaginated = page === 1 ? { [page]: newList } : { ...state.listPaginated, [page]: newList }; 132 | const listFlat = Object.keys(listPaginated).map((k) => listPaginated[k]).flat() || []; 133 | 134 | return newList 135 | ? { 136 | ...state, 137 | listPaginated, 138 | listFlat, 139 | lastSync: page === 1 140 | ? { [page]: moment().format() } 141 | : { ...state.lastSync, [page]: moment().format() }, 142 | meta: { 143 | page, 144 | lastPage: parseInt(headers['x-wp-totalpages'], 10) || null, 145 | total: parseInt(headers['x-wp-total'], 10) || null, 146 | }, 147 | pagination: pagination(headers['x-wp-totalpages'], '/articles/'), 148 | } 149 | : initialState; 150 | }, 151 | 152 | /** 153 | * Save form data 154 | * @param {obj} state 155 | * @param {obj} payload 156 | */ 157 | replaceUserInput(state, payload) { 158 | return { 159 | ...state, 160 | userInput: payload, 161 | }; 162 | }, 163 | }, 164 | }; 165 | -------------------------------------------------------------------------------- /src/tests/components/UI/__snapshots__/Error.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders with a button 1`] = ` 4 | 18 | 28 | 39 | 53 | hello boy 54 | 55 | 71 | An unexpected error came up 72 | 73 | 112 | 127 | Try Again 128 | 129 | 130 | 141 | 142 | 143 | `; 144 | 145 | exports[` renders with message 1`] = ` 146 | 160 | 170 | 181 | 195 | hello boy 196 | 197 | 213 | An unexpected error came up 214 | 215 | 226 | 227 | 228 | `; 229 | -------------------------------------------------------------------------------- /native-base-theme/components/CardItem.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { StyleSheet } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const platform = variables.platform; 10 | const transparentBtnCommon = { 11 | 'NativeBase.Text': { 12 | fontSize: variables.DefaultFontSize - 3, 13 | color: variables.sTabBarActiveTextColor 14 | }, 15 | 'NativeBase.Icon': { 16 | fontSize: variables.iconFontSize - 10, 17 | color: variables.sTabBarActiveTextColor, 18 | marginHorizontal: null 19 | }, 20 | 'NativeBase.IconNB': { 21 | fontSize: variables.iconFontSize - 10, 22 | color: variables.sTabBarActiveTextColor 23 | }, 24 | paddingVertical: null, 25 | paddingHorizontal: null 26 | }; 27 | 28 | const cardItemTheme = { 29 | 'NativeBase.Left': { 30 | 'NativeBase.Body': { 31 | 'NativeBase.Text': { 32 | '.note': { 33 | color: variables.listNoteColor, 34 | fontWeight: '400', 35 | marginRight: 20 36 | } 37 | }, 38 | flex: 1, 39 | marginLeft: 10, 40 | alignItems: null 41 | }, 42 | 'NativeBase.Icon': { 43 | fontSize: variables.iconFontSize 44 | }, 45 | 'NativeBase.IconNB': { 46 | fontSize: variables.iconFontSize 47 | }, 48 | 'NativeBase.Text': { 49 | marginLeft: 10, 50 | alignSelf: 'center' 51 | }, 52 | 'NativeBase.Button': { 53 | '.transparent': { 54 | ...transparentBtnCommon, 55 | paddingRight: variables.cardItemPadding + 5 56 | } 57 | }, 58 | flex: 1, 59 | flexDirection: 'row', 60 | alignItems: 'center' 61 | }, 62 | '.content': { 63 | 'NativeBase.Text': { 64 | color: platform === PLATFORM.IOS ? '#555' : '#222', 65 | fontSize: variables.DefaultFontSize - 2 66 | } 67 | }, 68 | '.cardBody': { 69 | padding: -5, 70 | 'NativeBase.Text': { 71 | marginTop: 5 72 | } 73 | }, 74 | 'NativeBase.Body': { 75 | 'NativeBase.Text': { 76 | '.note': { 77 | color: variables.listNoteColor, 78 | fontWeight: '200', 79 | marginRight: 20 80 | } 81 | }, 82 | 'NativeBase.Button': { 83 | '.transparent': { 84 | ...transparentBtnCommon, 85 | paddingRight: variables.cardItemPadding + 5, 86 | alignSelf: 'stretch' 87 | } 88 | }, 89 | flex: 1, 90 | alignSelf: 'stretch', 91 | alignItems: 'flex-start' 92 | }, 93 | 'NativeBase.Right': { 94 | 'NativeBase.Badge': { 95 | alignSelf: null 96 | }, 97 | 'NativeBase.Button': { 98 | '.transparent': { 99 | ...transparentBtnCommon 100 | }, 101 | alignSelf: null 102 | }, 103 | 'NativeBase.Icon': { 104 | alignSelf: null, 105 | fontSize: variables.iconFontSize - 8, 106 | color: variables.cardBorderColor 107 | }, 108 | 'NativeBase.IconNB': { 109 | alignSelf: null, 110 | fontSize: variables.iconFontSize - 8, 111 | color: variables.cardBorderColor 112 | }, 113 | 'NativeBase.Text': { 114 | fontSize: variables.DefaultFontSize - 1, 115 | alignSelf: null 116 | }, 117 | 'NativeBase.Thumbnail': { 118 | alignSelf: null 119 | }, 120 | 'NativeBase.Image': { 121 | alignSelf: null 122 | }, 123 | 'NativeBase.Radio': { 124 | alignSelf: null 125 | }, 126 | 'NativeBase.Checkbox': { 127 | alignSelf: null 128 | }, 129 | 'NativeBase.Switch': { 130 | alignSelf: null 131 | }, 132 | flex: 0.8 133 | }, 134 | '.header': { 135 | 'NativeBase.Text': { 136 | fontSize: 16, 137 | fontWeight: platform === PLATFORM.IOS ? '600' : '500' 138 | }, 139 | '.bordered': { 140 | 'NativeBase.Text': { 141 | color: variables.brandPrimary, 142 | fontWeight: platform === PLATFORM.IOS ? '600' : '500' 143 | }, 144 | borderBottomWidth: variables.borderWidth 145 | }, 146 | borderBottomWidth: null, 147 | paddingVertical: variables.cardItemPadding + 5 148 | }, 149 | '.footer': { 150 | 'NativeBase.Text': { 151 | fontSize: 16, 152 | fontWeight: platform === PLATFORM.IOS ? '600' : '500' 153 | }, 154 | '.bordered': { 155 | 'NativeBase.Text': { 156 | color: variables.brandPrimary, 157 | fontWeight: platform === PLATFORM.IOS ? '600' : '500' 158 | }, 159 | borderTopWidth: variables.borderWidth 160 | }, 161 | borderBottomWidth: null 162 | }, 163 | 'NativeBase.Text': { 164 | '.note': { 165 | color: variables.listNoteColor, 166 | fontWeight: '200' 167 | } 168 | }, 169 | 'NativeBase.Icon': { 170 | width: variables.iconFontSize + 5, 171 | fontSize: variables.iconFontSize - 2 172 | }, 173 | 'NativeBase.IconNB': { 174 | width: variables.iconFontSize + 5, 175 | fontSize: variables.iconFontSize - 2 176 | }, 177 | '.bordered': { 178 | borderBottomWidth: StyleSheet.hairlineWidth, 179 | borderColor: variables.cardBorderColor 180 | }, 181 | '.first': { 182 | borderTopLeftRadius: variables.cardBorderRadius, 183 | borderTopRightRadius: variables.cardBorderRadius 184 | }, 185 | '.last': { 186 | borderBottomLeftRadius: variables.cardBorderRadius, 187 | borderBottomRightRadius: variables.cardBorderRadius 188 | }, 189 | flexDirection: 'row', 190 | alignItems: 'center', 191 | borderRadius: variables.cardBorderRadius, 192 | padding: variables.cardItemPadding + 5, 193 | paddingVertical: variables.cardItemPadding, 194 | backgroundColor: variables.cardDefaultBg 195 | }; 196 | 197 | return cardItemTheme; 198 | }; 199 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /native-base-theme/components/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | // @flow 3 | 4 | import _ from 'lodash'; 5 | 6 | import bodyTheme from './Body'; 7 | import leftTheme from './Left'; 8 | import rightTheme from './Right'; 9 | import headerTheme from './Header'; 10 | import switchTheme from './Switch'; 11 | import thumbnailTheme from './Thumbnail'; 12 | import containerTheme from './Container'; 13 | import contentTheme from './Content'; 14 | import buttonTheme from './Button'; 15 | import titleTheme from './Title'; 16 | import subtitleTheme from './Subtitle'; 17 | import inputGroupTheme from './InputGroup'; 18 | import badgeTheme from './Badge'; 19 | import checkBoxTheme from './CheckBox'; 20 | import cardTheme from './Card'; 21 | import radioTheme from './Radio'; 22 | import h3Theme from './H3'; 23 | import h2Theme from './H2'; 24 | import h1Theme from './H1'; 25 | import footerTheme from './Footer'; 26 | import footerTabTheme from './FooterTab'; 27 | import fabTheme from './Fab'; 28 | import itemTheme from './Item'; 29 | import labelTheme from './Label'; 30 | import textAreaTheme from './Textarea'; 31 | import textTheme from './Text'; 32 | import toastTheme from './Toast'; 33 | import tabTheme from './Tab'; 34 | import tabBarTheme from './TabBar'; 35 | import tabContainerTheme from './TabContainer'; 36 | import viewTheme from './View'; 37 | import tabHeadingTheme from './TabHeading'; 38 | import iconTheme from './Icon'; 39 | import inputTheme from './Input'; 40 | import swipeRowTheme from './SwipeRow'; 41 | import segmentTheme from './Segment'; 42 | import spinnerTheme from './Spinner'; 43 | import cardItemTheme from './CardItem'; 44 | import listItemTheme from './ListItem'; 45 | import formTheme from './Form'; 46 | import separatorTheme from './Separator'; 47 | import pickerTheme from './Picker'; 48 | import variable from './../variables/platform'; 49 | 50 | export default (variables /* : * */ = variable) => { 51 | const theme = { 52 | variables, 53 | 'NativeBase.Left': { 54 | ...leftTheme(variables) 55 | }, 56 | 'NativeBase.Right': { 57 | ...rightTheme(variables) 58 | }, 59 | 'NativeBase.Body': { 60 | ...bodyTheme(variables) 61 | }, 62 | 63 | 'NativeBase.Header': { 64 | ...headerTheme(variables) 65 | }, 66 | 67 | 'NativeBase.Button': { 68 | ...buttonTheme(variables) 69 | }, 70 | 71 | 'NativeBase.Title': { 72 | ...titleTheme(variables) 73 | }, 74 | 'NativeBase.Subtitle': { 75 | ...subtitleTheme(variables) 76 | }, 77 | 78 | 'NativeBase.InputGroup': { 79 | ...inputGroupTheme(variables) 80 | }, 81 | 82 | 'NativeBase.Input': { 83 | ...inputTheme(variables) 84 | }, 85 | 86 | 'NativeBase.Badge': { 87 | ...badgeTheme(variables) 88 | }, 89 | 90 | 'NativeBase.CheckBox': { 91 | ...checkBoxTheme(variables) 92 | }, 93 | 94 | 'NativeBase.Radio': { 95 | ...radioTheme(variables) 96 | }, 97 | 98 | 'NativeBase.Card': { 99 | ...cardTheme(variables) 100 | }, 101 | 102 | 'NativeBase.CardItem': { 103 | ...cardItemTheme(variables) 104 | }, 105 | 106 | 'NativeBase.Toast': { 107 | ...toastTheme(variables) 108 | }, 109 | 110 | 'NativeBase.H1': { 111 | ...h1Theme(variables) 112 | }, 113 | 'NativeBase.H2': { 114 | ...h2Theme(variables) 115 | }, 116 | 'NativeBase.H3': { 117 | ...h3Theme(variables) 118 | }, 119 | 'NativeBase.Form': { 120 | ...formTheme(variables) 121 | }, 122 | 123 | 'NativeBase.Container': { 124 | ...containerTheme(variables) 125 | }, 126 | 'NativeBase.Content': { 127 | ...contentTheme(variables) 128 | }, 129 | 130 | 'NativeBase.Footer': { 131 | ...footerTheme(variables) 132 | }, 133 | 134 | 'NativeBase.Tabs': { 135 | flex: 1 136 | }, 137 | 138 | 'NativeBase.FooterTab': { 139 | ...footerTabTheme(variables) 140 | }, 141 | 142 | 'NativeBase.ListItem': { 143 | ...listItemTheme(variables) 144 | }, 145 | 146 | 'NativeBase.ListItem1': { 147 | ...listItemTheme(variables) 148 | }, 149 | 150 | 'NativeBase.Icon': { 151 | ...iconTheme(variables) 152 | }, 153 | 'NativeBase.IconNB': { 154 | ...iconTheme(variables) 155 | }, 156 | 'NativeBase.Text': { 157 | ...textTheme(variables) 158 | }, 159 | 'NativeBase.Spinner': { 160 | ...spinnerTheme(variables) 161 | }, 162 | 163 | 'NativeBase.Fab': { 164 | ...fabTheme(variables) 165 | }, 166 | 167 | 'NativeBase.Item': { 168 | ...itemTheme(variables) 169 | }, 170 | 171 | 'NativeBase.Label': { 172 | ...labelTheme(variables) 173 | }, 174 | 175 | 'NativeBase.Textarea': { 176 | ...textAreaTheme(variables) 177 | }, 178 | 179 | 'NativeBase.PickerNB': { 180 | ...pickerTheme(variables), 181 | 'NativeBase.Button': { 182 | 'NativeBase.Text': {} 183 | } 184 | }, 185 | 186 | 'NativeBase.Tab': { 187 | ...tabTheme(variables) 188 | }, 189 | 190 | 'NativeBase.Segment': { 191 | ...segmentTheme(variables) 192 | }, 193 | 194 | 'NativeBase.TabBar': { 195 | ...tabBarTheme(variables) 196 | }, 197 | 'NativeBase.ViewNB': { 198 | ...viewTheme(variables) 199 | }, 200 | 'NativeBase.TabHeading': { 201 | ...tabHeadingTheme(variables) 202 | }, 203 | 'NativeBase.TabContainer': { 204 | ...tabContainerTheme(variables) 205 | }, 206 | 'NativeBase.Switch': { 207 | ...switchTheme(variables) 208 | }, 209 | 'NativeBase.Separator': { 210 | ...separatorTheme(variables) 211 | }, 212 | 'NativeBase.SwipeRow': { 213 | ...swipeRowTheme(variables) 214 | }, 215 | 'NativeBase.Thumbnail': { 216 | ...thumbnailTheme(variables) 217 | } 218 | }; 219 | 220 | const cssifyTheme = (grandparent, parent, parentKey) => { 221 | _.forEach(parent, (style, styleName) => { 222 | if ( 223 | styleName.indexOf('.') === 0 && 224 | parentKey && 225 | parentKey.indexOf('.') === 0 226 | ) { 227 | if (grandparent) { 228 | if (!grandparent[styleName]) { 229 | grandparent[styleName] = {}; 230 | } else { 231 | grandparent[styleName][parentKey] = style; 232 | } 233 | } 234 | } 235 | if ( 236 | style && 237 | typeof style === 'object' && 238 | styleName !== 'fontVariant' && 239 | styleName !== 'transform' 240 | ) { 241 | cssifyTheme(parent, style, styleName); 242 | } 243 | }); 244 | }; 245 | 246 | cssifyTheme(null, theme, null); 247 | 248 | return theme; 249 | }; 250 | -------------------------------------------------------------------------------- /native-base-theme/components/Item.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { Platform } from 'react-native'; 4 | 5 | import variable from './../variables/platform'; 6 | import { PLATFORM } from './../variables/commonColor'; 7 | 8 | export default (variables /* : * */ = variable) => { 9 | const itemTheme = { 10 | '.floatingLabel': { 11 | 'NativeBase.Input': { 12 | height: 50, 13 | top: 8, 14 | paddingTop: 3, 15 | paddingBottom: 7, 16 | '.multiline': { 17 | minHeight: variables.inputHeightBase, 18 | paddingTop: Platform.OS === PLATFORM.IOS ? 10 : 3, 19 | paddingBottom: Platform.OS === PLATFORM.IOS ? 14 : 10 20 | } 21 | }, 22 | 'NativeBase.Label': { 23 | paddingTop: 5 24 | }, 25 | 'NativeBase.Icon': { 26 | top: 6, 27 | paddingTop: 8 28 | }, 29 | 'NativeBase.IconNB': { 30 | top: 6, 31 | paddingTop: 8 32 | } 33 | }, 34 | '.fixedLabel': { 35 | 'NativeBase.Label': { 36 | position: null, 37 | top: null, 38 | left: null, 39 | right: null, 40 | flex: 1, 41 | height: null, 42 | width: null, 43 | fontSize: variables.inputFontSize 44 | }, 45 | 'NativeBase.Input': { 46 | flex: 2, 47 | fontSize: variables.inputFontSize 48 | } 49 | }, 50 | '.stackedLabel': { 51 | 'NativeBase.Label': { 52 | position: null, 53 | top: null, 54 | left: null, 55 | right: null, 56 | paddingTop: 5, 57 | alignSelf: 'flex-start', 58 | fontSize: variables.inputFontSize - 2 59 | }, 60 | 'NativeBase.Icon': { 61 | marginTop: 36 62 | }, 63 | 'NativeBase.Input': { 64 | // alignSelf: Platform.OS === PLATFORM.IOS ? 'stretch' : 'flex-start', 65 | alignSelf: 'stretch', 66 | flex: 1, 67 | // width: Platform.OS === PLATFORM.IOS ? null : variables.deviceWidth - 25, 68 | fontSize: variables.inputFontSize, 69 | lineHeight: variables.inputLineHeight - 6, 70 | '.secureTextEntry': { 71 | fontSize: variables.inputFontSize 72 | }, 73 | '.multiline': { 74 | paddingTop: Platform.OS === PLATFORM.IOS ? 9 : undefined, 75 | paddingBottom: Platform.OS === PLATFORM.IOS ? 9 : undefined 76 | } 77 | }, 78 | flexDirection: null, 79 | minHeight: variables.inputHeightBase + 15 80 | }, 81 | '.inlineLabel': { 82 | 'NativeBase.Label': { 83 | position: null, 84 | top: null, 85 | left: null, 86 | right: null, 87 | paddingRight: 20, 88 | height: null, 89 | width: null, 90 | fontSize: variables.inputFontSize 91 | }, 92 | 'NativeBase.Input': { 93 | paddingLeft: 5, 94 | fontSize: variables.inputFontSize 95 | }, 96 | flexDirection: 'row' 97 | }, 98 | 'NativeBase.Label': { 99 | fontSize: variables.inputFontSize, 100 | color: variables.inputColorPlaceholder, 101 | paddingRight: 5 102 | }, 103 | 'NativeBase.Icon': { 104 | fontSize: 24, 105 | paddingRight: 8 106 | }, 107 | 'NativeBase.IconNB': { 108 | fontSize: 24, 109 | paddingRight: 8 110 | }, 111 | 'NativeBase.Input': { 112 | '.multiline': { 113 | height: null 114 | }, 115 | height: variables.inputHeightBase, 116 | color: variables.inputColor, 117 | flex: 1, 118 | top: Platform.OS === PLATFORM.IOS ? 1.5 : undefined, 119 | fontSize: variables.inputFontSize 120 | }, 121 | '.underline': { 122 | 'NativeBase.Input': { 123 | paddingLeft: 15 124 | }, 125 | '.success': { 126 | borderColor: variables.inputSuccessBorderColor 127 | }, 128 | '.error': { 129 | borderColor: variables.inputErrorBorderColor 130 | }, 131 | borderWidth: variables.borderWidth * 2, 132 | borderTopWidth: 0, 133 | borderRightWidth: 0, 134 | borderLeftWidth: 0, 135 | borderColor: variables.inputBorderColor 136 | }, 137 | '.regular': { 138 | 'NativeBase.Input': { 139 | paddingLeft: 8 140 | }, 141 | 'NativeBase.Icon': { 142 | paddingLeft: 10 143 | }, 144 | '.success': { 145 | borderColor: variables.inputSuccessBorderColor 146 | }, 147 | '.error': { 148 | borderColor: variables.inputErrorBorderColor 149 | }, 150 | borderWidth: variables.borderWidth * 2, 151 | borderColor: variables.inputBorderColor 152 | }, 153 | '.rounded': { 154 | 'NativeBase.Input': { 155 | paddingLeft: 8 156 | }, 157 | 'NativeBase.Icon': { 158 | paddingLeft: 10 159 | }, 160 | '.success': { 161 | borderColor: variables.inputSuccessBorderColor 162 | }, 163 | '.error': { 164 | borderColor: variables.inputErrorBorderColor 165 | }, 166 | borderWidth: variables.borderWidth * 2, 167 | borderRadius: 30, 168 | borderColor: variables.inputBorderColor 169 | }, 170 | 171 | '.success': { 172 | 'NativeBase.Icon': { 173 | color: variables.inputSuccessBorderColor 174 | }, 175 | 'NativeBase.IconNB': { 176 | color: variables.inputSuccessBorderColor 177 | }, 178 | '.rounded': { 179 | borderRadius: 30, 180 | borderColor: variables.inputSuccessBorderColor 181 | }, 182 | '.regular': { 183 | borderColor: variables.inputSuccessBorderColor 184 | }, 185 | '.underline': { 186 | borderWidth: variables.borderWidth * 2, 187 | borderTopWidth: 0, 188 | borderRightWidth: 0, 189 | borderLeftWidth: 0, 190 | borderColor: variables.inputSuccessBorderColor 191 | }, 192 | borderColor: variables.inputSuccessBorderColor 193 | }, 194 | 195 | '.error': { 196 | 'NativeBase.Icon': { 197 | color: variables.inputErrorBorderColor 198 | }, 199 | 'NativeBase.IconNB': { 200 | color: variables.inputErrorBorderColor 201 | }, 202 | '.rounded': { 203 | borderRadius: 30, 204 | borderColor: variables.inputErrorBorderColor 205 | }, 206 | '.regular': { 207 | borderColor: variables.inputErrorBorderColor 208 | }, 209 | '.underline': { 210 | borderWidth: variables.borderWidth * 2, 211 | borderTopWidth: 0, 212 | borderRightWidth: 0, 213 | borderLeftWidth: 0, 214 | borderColor: variables.inputErrorBorderColor 215 | }, 216 | borderColor: variables.inputErrorBorderColor 217 | }, 218 | '.disabled': { 219 | 'NativeBase.Icon': { 220 | color: '#384850' 221 | }, 222 | 'NativeBase.IconNB': { 223 | color: '#384850' 224 | } 225 | }, 226 | '.picker': { 227 | marginLeft: 0 228 | }, 229 | 230 | borderWidth: variables.borderWidth * 2, 231 | borderTopWidth: 0, 232 | borderRightWidth: 0, 233 | borderLeftWidth: 0, 234 | borderColor: variables.inputBorderColor, 235 | backgroundColor: 'transparent', 236 | flexDirection: 'row', 237 | alignItems: 'center', 238 | marginLeft: 2 239 | }; 240 | 241 | return itemTheme; 242 | }; 243 | --------------------------------------------------------------------------------