Full Code of infinitered/ignite-andross for AI

master c0836e755a6b cached
127 files
157.8 KB
43.7k tokens
33 symbols
1 requests
Download .txt
Repository: infinitered/ignite-andross
Branch: master
Commit: c0836e755a6b
Files: 127
Total size: 157.8 KB

Directory structure:
gitextract_lzwitwv2/

├── .circleci/
│   └── config.yml
├── .gitignore
├── LICENSE
├── boilerplate/
│   ├── .babelrc
│   ├── .editorconfig
│   ├── App/
│   │   ├── Components/
│   │   │   ├── AlertMessage.js
│   │   │   ├── AlertMessage.story.js
│   │   │   ├── DrawerButton.js
│   │   │   ├── DrawerButton.story.js
│   │   │   ├── FullButton.js
│   │   │   ├── FullButton.story.js
│   │   │   ├── README.md
│   │   │   ├── RoundedButton.js
│   │   │   ├── RoundedButton.story.js
│   │   │   ├── Stories.js
│   │   │   └── Styles/
│   │   │       ├── AlertMessageStyles.js
│   │   │       ├── DrawerButtonStyles.js
│   │   │       ├── FullButtonStyles.js
│   │   │       ├── README.md
│   │   │       └── RoundedButtonStyles.js
│   │   ├── Config/
│   │   │   ├── AppConfig.js
│   │   │   ├── DebugConfig.js
│   │   │   ├── README.md
│   │   │   ├── ReactotronConfig.js
│   │   │   └── index.js
│   │   ├── Containers/
│   │   │   ├── App.js
│   │   │   ├── LaunchScreen.js
│   │   │   ├── README.md
│   │   │   ├── RootContainer.js
│   │   │   └── Styles/
│   │   │       ├── LaunchScreenStyles.js
│   │   │       ├── README.md
│   │   │       └── RootContainerStyles.js
│   │   ├── Fixtures/
│   │   │   ├── README.md
│   │   │   ├── gantman.json
│   │   │   ├── rateLimit.json
│   │   │   ├── root.json
│   │   │   └── skellock.json
│   │   ├── Images/
│   │   │   └── README.md
│   │   ├── Lib/
│   │   │   └── README.md
│   │   ├── Navigation/
│   │   │   ├── AppNavigation.js
│   │   │   ├── ReduxNavigation.js
│   │   │   └── Styles/
│   │   │       └── NavigationStyles.js
│   │   ├── Redux/
│   │   │   ├── CreateStore.js
│   │   │   ├── GithubRedux.js
│   │   │   ├── NavigationRedux.js
│   │   │   ├── ScreenTrackingMiddleware.js
│   │   │   ├── SearchRedux.js
│   │   │   ├── StartupRedux.js
│   │   │   └── index.js
│   │   ├── Sagas/
│   │   │   ├── GithubSagas.js
│   │   │   ├── StartupSagas.js
│   │   │   └── index.js
│   │   ├── Services/
│   │   │   ├── Api.js
│   │   │   ├── ExamplesRegistry.js
│   │   │   └── FixtureApi.js
│   │   ├── Themes/
│   │   │   ├── ApplicationStyles.js
│   │   │   ├── Colors.js
│   │   │   ├── Fonts.js
│   │   │   ├── Images.js
│   │   │   ├── Metrics.js
│   │   │   ├── README.md
│   │   │   └── index.js
│   │   └── Transforms/
│   │       ├── ConvertFromKelvin.js
│   │       └── README.md
│   ├── README.md
│   ├── Tests/
│   │   ├── Components/
│   │   │   ├── AlertMessageTest.js
│   │   │   ├── DrawerButtonTest.js
│   │   │   ├── FullButtonTest.js
│   │   │   └── RoundedButtonTest.js
│   │   ├── Redux/
│   │   │   └── GithubReduxTest.js
│   │   ├── Sagas/
│   │   │   ├── GithubSagaTest.js
│   │   │   └── StartupSagaTest.js
│   │   ├── Services/
│   │   │   └── FixtureAPITest.js
│   │   ├── Setup.js.ejs
│   │   └── StoriesTest.js
│   ├── ignite.json.ejs
│   ├── index.js.ejs
│   ├── package.json.ejs
│   └── storybook/
│       ├── addons.js
│       ├── index.js
│       └── storybook.ejs
├── boilerplate.js
├── commands/
│   └── generate/
│       ├── component.js
│       ├── container.js
│       ├── generate.js
│       ├── list.js
│       ├── redux.js
│       ├── saga.js
│       └── screen.js
├── ignite.json
├── lib/
│   ├── patterns.js
│   └── react-native-version.js
├── options.js
├── package.json
├── plugin.js
├── readme.md
├── screenExamples.js
├── templates/
│   ├── component-style.ejs
│   ├── component-test.ejs
│   ├── component.ejs
│   ├── container-style.ejs
│   ├── container.ejs
│   ├── examples/
│   │   ├── GridExample.js.ejs
│   │   ├── RowExample.js.ejs
│   │   ├── SectionExample.js.ejs
│   │   └── Styles/
│   │       ├── GridExampleStyle.js.ejs
│   │       ├── RowExampleStyle.js.ejs
│   │       └── SectionExampleStyle.js.ejs
│   ├── flatlist-grid-style.ejs
│   ├── flatlist-grid.ejs
│   ├── flatlist-sections.ejs
│   ├── flatlist.ejs
│   ├── listview-grid-style.ejs
│   ├── listview-sections.ejs
│   ├── listview-style.ejs
│   ├── listview.ejs
│   ├── redux-test-ava.ejs
│   ├── redux-test-jest.ejs
│   ├── redux.ejs
│   ├── saga-test-ava.ejs
│   ├── saga-test-jest.ejs
│   ├── saga.ejs
│   ├── screen-style.ejs
│   └── screen.ejs
└── test/
    ├── generators-integration.test.js
    ├── interface.test.js
    └── react-native-version.test.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .circleci/config.yml
================================================
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#

defaults: &defaults
  docker:
    # Choose the version of Node you want here
    - image: circleci/node:12.16
  working_directory: ~/repo

version: 2
jobs:
  setup:
    <<: *defaults
    steps:
      - checkout
      - restore_cache:
          name: Restore node modules
          keys:
            - v1-dependencies-{{ checksum "package.json" }}
            # fallback to using the latest cache if no exact match is found
            - v1-dependencies-
      - run:
          name: Install dependencies
          command: |
            yarn install
      - save_cache:
          name: Save node modules
          paths:
            - node_modules
          key: v1-dependencies-{{ checksum "package.json" }}

  tests:
    <<: *defaults
    steps:
      - checkout
      - restore_cache:
          name: Restore node modules
          keys:
            - v1-dependencies-{{ checksum "package.json" }}
            # fallback to using the latest cache if no exact match is found
            - v1-dependencies-
      - run:
          name: Run tests
          command: yarn ci:test # this command will be added to/found in your package.json scripts

  publish:
    <<: *defaults
    steps:
      - checkout
      - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
      - restore_cache:
          name: Restore node modules
          keys:
            - v1-dependencies-{{ checksum "package.json" }}
            # fallback to using the latest cache if no exact match is found
            - v1-dependencies-
      # Run semantic-release after all the above is set.
      - run:
          name: Publish to NPM
          command: yarn ci:publish # this will be added to your package.json scripts

workflows:
  version: 2
  test_and_release:
    jobs:
      - setup
      - tests:
          requires:
            - setup
      - publish:
          requires:
            - tests
          filters:
            branches:
              only: master


================================================
FILE: .gitignore
================================================
.DS_Store
npm-debug.log
npm-debug.log*
coverage
.nyc_output
lerna-debug.log
node_modules
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
testgrounds
IntegrationTest
integration_test
.idea
yarn-error.log
.vscode
package-lock.json


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2016 Infinite Red, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: boilerplate/.babelrc
================================================
{
  "presets": ["module:metro-react-native-babel-preset"],
  "env": {
    "production": {
      "plugins": ["ignite-ignore-reactotron"]
    }
  }
}


================================================
FILE: boilerplate/.editorconfig
================================================
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true


[*.gradle]
indent_size = 4

================================================
FILE: boilerplate/App/Components/AlertMessage.js
================================================
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { View, Text } from 'react-native'
import styles from './Styles/AlertMessageStyles'

export default class AlertMessage extends Component {
  static defaultProps = { show: true }

  static propTypes = {
    title: PropTypes.string,
    icon: PropTypes.string,
    style: PropTypes.object,
    show: PropTypes.bool
  }

  render () {
    let messageComponent = null
    if (this.props.show) {
      const { title } = this.props
      return (
        <View
          style={[styles.container, this.props.style]}
        >
          <View style={styles.contentContainer}>
            <Text allowFontScaling={false} style={styles.message}>{title && title.toUpperCase()}</Text>
          </View>
        </View>
      )
    }

    return messageComponent
  }
}


================================================
FILE: boilerplate/App/Components/AlertMessage.story.js
================================================
import React from 'react'
import { storiesOf } from '@storybook/react-native'

import AlertMessage from './AlertMessage'

storiesOf('AlertMessage')
  .add('Default', () => (
    <AlertMessage
      title='ALERT ALERT'
    />
  ))
  .add('Hidden', () => (
    <AlertMessage
      title='ALERT ALERT'
      show={false}
    />
  ))
  .add('Custom Style', () => (
    <AlertMessage
      title='ALERT ALERT'
      style={{ backgroundColor: 'red' }}
    />
  ))


================================================
FILE: boilerplate/App/Components/DrawerButton.js
================================================
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Text, TouchableOpacity } from 'react-native'
import styles from './Styles/DrawerButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'

// Note that this file (App/Components/DrawerButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.

// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Drawer Button', () =>
  <DrawerButton
    text='Example left drawer button'
    onPress={() => window.alert('Your drawers are showing')}
  />
)

class DrawerButton extends Component {
  static propTypes = {
    text: PropTypes.string,
    onPress: PropTypes.func
  }

  render () {
    return (
      <TouchableOpacity onPress={this.props.onPress}>
        <Text style={styles.text}>{this.props.text}</Text>
      </TouchableOpacity>
    )
  }
}

export default DrawerButton


================================================
FILE: boilerplate/App/Components/DrawerButton.story.js
================================================
import React from 'react'
import { View } from 'react-native'
import { storiesOf } from '@storybook/react-native'

import DrawerButton from './DrawerButton'

storiesOf('DrawerButton')
  .add('Default', () => (
    <View style={{ backgroundColor: 'black' }}>
      <DrawerButton
        text='Drawer Button'
        onPress={() => { }}
      />
    </View>
  ))


================================================
FILE: boilerplate/App/Components/FullButton.js
================================================
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity, Text } from 'react-native'
import styles from './Styles/FullButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'

// Note that this file (App/Components/FullButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.

// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Full Button', () =>
  <FullButton
    text='Hey there'
    onPress={() => window.alert('Full Button Pressed!')}
  />
)

export default class FullButton extends Component {
  static propTypes = {
    text: PropTypes.string,
    onPress: PropTypes.func,
    styles: PropTypes.object
  }

  render () {
    return (
      <TouchableOpacity style={[styles.button, this.props.styles]} onPress={this.props.onPress}>
        <Text style={styles.buttonText}>{this.props.text && this.props.text.toUpperCase()}</Text>
      </TouchableOpacity>
    )
  }
}


================================================
FILE: boilerplate/App/Components/FullButton.story.js
================================================
import React from 'react'
import { storiesOf } from '@storybook/react-native'

import FullButton from './FullButton'

storiesOf('FullButton')
  .add('Default', () => (
    <FullButton
      text='A simple button'
    />
  ))
  .add('Custom Style', () => (
    <FullButton
      text='Style Me Up!'
      styles={{ backgroundColor: 'blue' }}
    />
  ))


================================================
FILE: boilerplate/App/Components/README.md
================================================
### Components Folder
All components are stored and organized here


================================================
FILE: boilerplate/App/Components/RoundedButton.js
================================================
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity, Text } from 'react-native'
import styles from './Styles/RoundedButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'

// Note that this file (App/Components/RoundedButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.

// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Rounded Button', () =>
  <RoundedButton
    text='real buttons have curves'
    onPress={() => window.alert('Rounded Button Pressed!')}
  />
)

export default class RoundedButton extends Component {
  static propTypes = {
    onPress: PropTypes.func,
    text: PropTypes.string,
    children: PropTypes.string,
    navigator: PropTypes.object
  }

  getText () {
    const buttonText = this.props.text || this.props.children || ''
    return buttonText.toUpperCase()
  }

  render () {
    return (
      <TouchableOpacity style={styles.button} onPress={this.props.onPress}>
        <Text style={styles.buttonText}>{this.getText()}</Text>
      </TouchableOpacity>
    )
  }
}


================================================
FILE: boilerplate/App/Components/RoundedButton.story.js
================================================
import React from 'react'
import { storiesOf } from '@storybook/react-native'

import RoundedButton from './RoundedButton'

storiesOf('RoundedButton')
  .add('Default', () => (
    <RoundedButton
      text='A simple rounded button'
    />
  ))
  .add('Text as children', () => (
    <RoundedButton>
        Hello from the children!
    </RoundedButton>
  ))


================================================
FILE: boilerplate/App/Components/Stories.js
================================================
import './AlertMessage.story'
import './DrawerButton.story'
import './FullButton.story'
import './RoundedButton.story'


================================================
FILE: boilerplate/App/Components/Styles/AlertMessageStyles.js
================================================
import { StyleSheet } from 'react-native'
import { Colors, Metrics, Fonts } from '../../Themes/'

export default StyleSheet.create({
  container: {
    justifyContent: 'center',
    marginVertical: Metrics.section
  },
  contentContainer: {
    alignSelf: 'center',
    alignItems: 'center'
  },
  message: {
    marginTop: Metrics.baseMargin,
    marginHorizontal: Metrics.baseMargin,
    textAlign: 'center',
    fontFamily: Fonts.type.base,
    fontSize: Fonts.size.regular,
    fontWeight: 'bold',
    color: Colors.steel
  },
  icon: {
    color: Colors.steel
  }
})


================================================
FILE: boilerplate/App/Components/Styles/DrawerButtonStyles.js
================================================
import { Metrics, Colors, Fonts } from '../../Themes'

export default {
  text: {
    ...Fonts.style.h5,
    color: Colors.snow,
    marginVertical: Metrics.baseMargin
  }
}


================================================
FILE: boilerplate/App/Components/Styles/FullButtonStyles.js
================================================
import { StyleSheet } from 'react-native'
import { Fonts, Colors } from '../../Themes/'

export default StyleSheet.create({
  button: {
    marginVertical: 5,
    borderTopColor: Colors.fire,
    borderBottomColor: Colors.bloodOrange,
    borderTopWidth: 1,
    borderBottomWidth: 1,
    backgroundColor: Colors.ember
  },
  buttonText: {
    margin: 18,
    textAlign: 'center',
    color: Colors.snow,
    fontSize: Fonts.size.medium,
    fontFamily: Fonts.type.bold
  }
})


================================================
FILE: boilerplate/App/Components/Styles/README.md
================================================
### Styles Folder
Component styles are separated from functionality.


================================================
FILE: boilerplate/App/Components/Styles/RoundedButtonStyles.js
================================================
import { StyleSheet } from 'react-native'
import { Fonts, Colors, Metrics } from '../../Themes/'

export default StyleSheet.create({
  button: {
    height: 45,
    borderRadius: 5,
    marginHorizontal: Metrics.section,
    marginVertical: Metrics.baseMargin,
    backgroundColor: Colors.fire,
    justifyContent: 'center'
  },
  buttonText: {
    color: Colors.snow,
    textAlign: 'center',
    fontWeight: 'bold',
    fontSize: Fonts.size.medium,
    marginVertical: Metrics.baseMargin
  }
})


================================================
FILE: boilerplate/App/Config/AppConfig.js
================================================
// Simple React Native specific changes

export default {
  // font scaling override - RN default is on
  allowTextFontScaling: true
}


================================================
FILE: boilerplate/App/Config/DebugConfig.js
================================================
export default {
  useFixtures: false,
  ezLogin: false,
  yellowBox: __DEV__,
  reduxLogging: __DEV__,
  includeExamples: __DEV__,
  useReactotron: __DEV__
}


================================================
FILE: boilerplate/App/Config/README.md
================================================
### Config Folder
All application specific configuration falls in this folder.

`AppConfig.js` - production values.
`DebugConfig.js` - development-wide globals.
`ReactotronConfig.js` - Reactotron client settings.
`ReduxPersist.js` - rehydrate Redux state.


================================================
FILE: boilerplate/App/Config/ReactotronConfig.js
================================================
import Config from '../Config/DebugConfig'
import Immutable from 'seamless-immutable'
import Reactotron from 'reactotron-react-native'
import { reactotronRedux as reduxPlugin } from 'reactotron-redux'
import sagaPlugin from 'reactotron-redux-saga'

const reactotron = Reactotron
    .configure({ name: 'Ignite App' })
    .useReactNative()
    .use(reduxPlugin({ onRestore: Immutable }))
    .use(sagaPlugin())

if (Config.useReactotron) {
  // https://github.com/infinitered/reactotron for more options!

  reactotron.connect()

  // Let's clear Reactotron on every time we load the app
  reactotron.clear()

  // Totally hacky, but this allows you to not both importing reactotron-react-native
  // on every file.  This is just DEV mode, so no big deal.
}
export default reactotron
console.tron = reactotron


================================================
FILE: boilerplate/App/Config/index.js
================================================
import DebugConfig from './DebugConfig'
import AppConfig from './AppConfig' // eslint-disable-line no-unused-vars

if (__DEV__) {
  // If ReactNative's yellow box warnings are too much, it is possible to turn
  // it off, but the healthier approach is to fix the warnings.  =)
  console.disableYellowBox = !DebugConfig.yellowBox
}


================================================
FILE: boilerplate/App/Containers/App.js
================================================
import '../Config'
import DebugConfig from '../Config/DebugConfig'
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import RootContainer from './RootContainer'
import createStore from '../Redux'

// create our store
const store = createStore()

/**
 * Provides an entry point into our application.  Both index.ios.js and index.android.js
 * call this component first.
 *
 * We create our Redux store here, put it into a provider and then bring in our
 * RootContainer.
 *
 * We separate like this to play nice with React Native's hot reloading.
 */
class App extends Component {
  render () {
    return (
      <Provider store={store}>
        <RootContainer />
      </Provider>
    )
  }
}

// allow reactotron overlay for fast design in dev mode
export default DebugConfig.useReactotron
  ? console.tron.overlay(App)
  : App


================================================
FILE: boilerplate/App/Containers/LaunchScreen.js
================================================
import React, { Component } from 'react'
import { ScrollView, Text, Image, View } from 'react-native'
import { Images } from '../Themes'

// Styles
import styles from './Styles/LaunchScreenStyles'

export default class LaunchScreen extends Component {
  render () {
    return (
      <View style={styles.mainContainer}>
        <Image source={Images.background} style={styles.backgroundImage} resizeMode='stretch' />
        <ScrollView style={styles.container}>
          <View style={styles.centered}>
            <Image source={Images.launch} style={styles.logo} />
          </View>

          <View style={styles.section} >
            <Image source={Images.ready} />
            <Text style={styles.sectionText}>
              This probably isn't what your app is going to look like. Unless your designer handed you this screen and, in that case, congrats! You're ready to ship. For everyone else, this is where you'll see a live preview of your fully functioning app using Ignite.
            </Text>
          </View>

        </ScrollView>
      </View>
    )
  }
}


================================================
FILE: boilerplate/App/Containers/README.md
================================================
### Containers Folder
A container is what they call a "Smart Component" in Redux.  It is a component
which knows about Redux.  They are usually used as "Screens".

Also located in here are 2 special containers: `App.js` and `RootContainer.js`.

`App.js` is first component loaded after `index.ios.js` or `index.android.js`.  The purpose of this file is to setup Redux or any other non-visual "global" modules.  Having Redux setup here helps with the hot-reloading process in React Native during development as it won't try to reload your sagas and reducers should your colors change (for example).

`RootContainer.js` is the first visual component in the app.  It is the ancestor of all other screens and components.

You'll probably find you'll have great mileage in Ignite apps without even touching these 2 files.  They, of course, belong to you, so when you're ready to add something non-visual like Firebase or something visual like an overlay, you have spots to place these additions.


================================================
FILE: boilerplate/App/Containers/RootContainer.js
================================================
import React, { Component } from 'react'
import { View, StatusBar } from 'react-native'
import ReduxNavigation from '../Navigation/ReduxNavigation'
import { connect } from 'react-redux'
import StartupActions from '../Redux/StartupRedux'

// Styles
import styles from './Styles/RootContainerStyles'

class RootContainer extends Component {
  componentDidMount () {
    this.props.startup()
  }

  render () {
    return (
      <View style={styles.applicationView}>
        <StatusBar barStyle='light-content' />
        <ReduxNavigation />
      </View>
    )
  }
}

// wraps dispatch to create nicer functions to call within our component
const mapDispatchToProps = (dispatch) => ({
  startup: () => dispatch(StartupActions.startup())
})

export default connect(null, mapDispatchToProps)(RootContainer)


================================================
FILE: boilerplate/App/Containers/Styles/LaunchScreenStyles.js
================================================
import { StyleSheet } from 'react-native'
import { Metrics, ApplicationStyles } from '../../Themes/'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    paddingBottom: Metrics.baseMargin
  },
  logo: {
    marginTop: Metrics.doubleSection,
    height: Metrics.images.logo,
    width: Metrics.images.logo,
    resizeMode: 'contain'
  },
  centered: {
    alignItems: 'center'
  }
})


================================================
FILE: boilerplate/App/Containers/Styles/README.md
================================================
### Styles Folder
Container styles are separated from functionality.


================================================
FILE: boilerplate/App/Containers/Styles/RootContainerStyles.js
================================================
import {StyleSheet} from 'react-native'
import {Fonts, Metrics, Colors} from '../../Themes/'

export default StyleSheet.create({
  applicationView: {
    flex: 1
  },
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: Colors.background
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    fontFamily: Fonts.type.base,
    margin: Metrics.baseMargin
  },
  myImage: {
    width: 200,
    height: 200,
    alignSelf: 'center'
  }
})


================================================
FILE: boilerplate/App/Fixtures/README.md
================================================
### Fixtures folder
All key API responses are housed here.

These API responses can be used for several reasons.  _E.G._:
* To bypass logins when building any screen of the application
* To quickly test API parsing in unit tests
* To separate Network from Data concerns while coding


================================================
FILE: boilerplate/App/Fixtures/gantman.json
================================================
{
  "total_count": 7,
  "incomplete_results": false,
  "items": [
    {
      "login": "GantMan",
      "id": 997157,
      "avatar_url": "https://avatars.githubusercontent.com/u/997157?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/GantMan",
      "html_url": "https://github.com/GantMan",
      "followers_url": "https://api.github.com/users/GantMan/followers",
      "following_url": "https://api.github.com/users/GantMan/following{/other_user}",
      "gists_url": "https://api.github.com/users/GantMan/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/GantMan/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/GantMan/subscriptions",
      "organizations_url": "https://api.github.com/users/GantMan/orgs",
      "repos_url": "https://api.github.com/users/GantMan/repos",
      "events_url": "https://api.github.com/users/GantMan/events{/privacy}",
      "received_events_url": "https://api.github.com/users/GantMan/received_events",
      "type": "User",
      "site_admin": false,
      "score": 122.12115
    },
    {
      "login": "vlad-G",
      "id": 13520880,
      "avatar_url": "https://avatars.githubusercontent.com/u/13520880?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/vlad-G",
      "html_url": "https://github.com/vlad-G",
      "followers_url": "https://api.github.com/users/vlad-G/followers",
      "following_url": "https://api.github.com/users/vlad-G/following{/other_user}",
      "gists_url": "https://api.github.com/users/vlad-G/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/vlad-G/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/vlad-G/subscriptions",
      "organizations_url": "https://api.github.com/users/vlad-G/orgs",
      "repos_url": "https://api.github.com/users/vlad-G/repos",
      "events_url": "https://api.github.com/users/vlad-G/events{/privacy}",
      "received_events_url": "https://api.github.com/users/vlad-G/received_events",
      "type": "User",
      "site_admin": false,
      "score": 12.69848
    },
    {
      "login": "gantmani",
      "id": 3034094,
      "avatar_url": "https://avatars.githubusercontent.com/u/3034094?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/gantmani",
      "html_url": "https://github.com/gantmani",
      "followers_url": "https://api.github.com/users/gantmani/followers",
      "following_url": "https://api.github.com/users/gantmani/following{/other_user}",
      "gists_url": "https://api.github.com/users/gantmani/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/gantmani/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/gantmani/subscriptions",
      "organizations_url": "https://api.github.com/users/gantmani/orgs",
      "repos_url": "https://api.github.com/users/gantmani/repos",
      "events_url": "https://api.github.com/users/gantmani/events{/privacy}",
      "received_events_url": "https://api.github.com/users/gantmani/received_events",
      "type": "User",
      "site_admin": false,
      "score": 11.641713
    },
    {
      "login": "sgantman",
      "id": 5911526,
      "avatar_url": "https://avatars.githubusercontent.com/u/5911526?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/sgantman",
      "html_url": "https://github.com/sgantman",
      "followers_url": "https://api.github.com/users/sgantman/followers",
      "following_url": "https://api.github.com/users/sgantman/following{/other_user}",
      "gists_url": "https://api.github.com/users/sgantman/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/sgantman/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/sgantman/subscriptions",
      "organizations_url": "https://api.github.com/users/sgantman/orgs",
      "repos_url": "https://api.github.com/users/sgantman/repos",
      "events_url": "https://api.github.com/users/sgantman/events{/privacy}",
      "received_events_url": "https://api.github.com/users/sgantman/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.926345
    },
    {
      "login": "michaelgantman",
      "id": 16693070,
      "avatar_url": "https://avatars.githubusercontent.com/u/16693070?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/michaelgantman",
      "html_url": "https://github.com/michaelgantman",
      "followers_url": "https://api.github.com/users/michaelgantman/followers",
      "following_url": "https://api.github.com/users/michaelgantman/following{/other_user}",
      "gists_url": "https://api.github.com/users/michaelgantman/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/michaelgantman/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/michaelgantman/subscriptions",
      "organizations_url": "https://api.github.com/users/michaelgantman/orgs",
      "repos_url": "https://api.github.com/users/michaelgantman/repos",
      "events_url": "https://api.github.com/users/michaelgantman/events{/privacy}",
      "received_events_url": "https://api.github.com/users/michaelgantman/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.926345
    },
    {
      "login": "gantmanis",
      "id": 19141249,
      "avatar_url": "https://avatars.githubusercontent.com/u/19141249?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/gantmanis",
      "html_url": "https://github.com/gantmanis",
      "followers_url": "https://api.github.com/users/gantmanis/followers",
      "following_url": "https://api.github.com/users/gantmanis/following{/other_user}",
      "gists_url": "https://api.github.com/users/gantmanis/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/gantmanis/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/gantmanis/subscriptions",
      "organizations_url": "https://api.github.com/users/gantmanis/orgs",
      "repos_url": "https://api.github.com/users/gantmanis/repos",
      "events_url": "https://api.github.com/users/gantmanis/events{/privacy}",
      "received_events_url": "https://api.github.com/users/gantmanis/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.8813524
    },
    {
      "login": "Gantman2014",
      "id": 7669410,
      "avatar_url": "https://avatars.githubusercontent.com/u/7669410?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/Gantman2014",
      "html_url": "https://github.com/Gantman2014",
      "followers_url": "https://api.github.com/users/Gantman2014/followers",
      "following_url": "https://api.github.com/users/Gantman2014/following{/other_user}",
      "gists_url": "https://api.github.com/users/Gantman2014/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/Gantman2014/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/Gantman2014/subscriptions",
      "organizations_url": "https://api.github.com/users/Gantman2014/orgs",
      "repos_url": "https://api.github.com/users/Gantman2014/repos",
      "events_url": "https://api.github.com/users/Gantman2014/events{/privacy}",
      "received_events_url": "https://api.github.com/users/Gantman2014/received_events",
      "type": "User",
      "site_admin": false,
      "score": 7.8813524
    }
  ]
}

================================================
FILE: boilerplate/App/Fixtures/rateLimit.json
================================================
{
  "resources": {
    "core": {
      "limit": 60,
      "remaining": 42,
      "reset": 1488126913
    },
    "search": {
      "limit": 10,
      "remaining": 9,
      "reset": 1488126003
    }
  },
  "rate": {
    "limit": 60,
    "remaining": 42,
    "reset": 1488126913
  }
}

================================================
FILE: boilerplate/App/Fixtures/root.json
================================================
{
  "current_user_url": "https://api.github.com/user",
  "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
  "authorizations_url": "https://api.github.com/authorizations",
  "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
  "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
  "emails_url": "https://api.github.com/user/emails",
  "emojis_url": "https://api.github.com/emojis",
  "events_url": "https://api.github.com/events",
  "feeds_url": "https://api.github.com/feeds",
  "followers_url": "https://api.github.com/user/followers",
  "following_url": "https://api.github.com/user/following{/target}",
  "gists_url": "https://api.github.com/gists{/gist_id}",
  "hub_url": "https://api.github.com/hub",
  "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
  "issues_url": "https://api.github.com/issues",
  "keys_url": "https://api.github.com/user/keys",
  "notifications_url": "https://api.github.com/notifications",
  "organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
  "organization_url": "https://api.github.com/orgs/{org}",
  "public_gists_url": "https://api.github.com/gists/public",
  "rate_limit_url": "https://api.github.com/rate_limit",
  "repository_url": "https://api.github.com/repos/{owner}/{repo}",
  "repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
  "current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}",
  "starred_url": "https://api.github.com/user/starred{/owner}{/repo}",
  "starred_gists_url": "https://api.github.com/gists/starred",
  "team_url": "https://api.github.com/teams",
  "user_url": "https://api.github.com/users/{user}",
  "user_organizations_url": "https://api.github.com/user/orgs",
  "user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
  "user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
}

================================================
FILE: boilerplate/App/Fixtures/skellock.json
================================================
{
  "total_count": 1,
  "incomplete_results": false,
  "items": [
    {
      "login": "skellock",
      "id": 68273,
      "avatar_url": "https://avatars.githubusercontent.com/u/68273?v=3",
      "gravatar_id": "",
      "url": "https://api.github.com/users/skellock",
      "html_url": "https://github.com/skellock",
      "followers_url": "https://api.github.com/users/skellock/followers",
      "following_url": "https://api.github.com/users/skellock/following{/other_user}",
      "gists_url": "https://api.github.com/users/skellock/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/skellock/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/skellock/subscriptions",
      "organizations_url": "https://api.github.com/users/skellock/orgs",
      "repos_url": "https://api.github.com/users/skellock/repos",
      "events_url": "https://api.github.com/users/skellock/events{/privacy}",
      "received_events_url": "https://api.github.com/users/skellock/received_events",
      "type": "User",
      "site_admin": false,
      "score": 107.22611
    }
  ]
}

================================================
FILE: boilerplate/App/Images/README.md
================================================
### Images folder
Holds all images for the applications.

================================================
FILE: boilerplate/App/Lib/README.md
================================================
# Lib

At first glance, this could appear to be a "miscellaneous" folder, but we recommend that you treat this as proving ground for components that could be reusable outside your project.

Maybe you're writing a set of utilities that you could use outside your project, but they're not quite ready or battle tested.  This folder would be a great place to put them.  They ideally be pure functions have no dependencies on other things in your App folder.


================================================
FILE: boilerplate/App/Navigation/AppNavigation.js
================================================
import { createAppContainer } from 'react-navigation'
import { createStackNavigator } from 'react-navigation-stack';
import LaunchScreen from '../Containers/LaunchScreen'

import styles from './Styles/NavigationStyles'

// Manifest of possible screens
const PrimaryNav = createStackNavigator({
  LaunchScreen: { screen: LaunchScreen }
}, {
  // Default config for all screens
  headerMode: 'none',
  initialRouteName: 'LaunchScreen',
  navigationOptions: {
    headerStyle: styles.header
  }
})

export default createAppContainer(PrimaryNav)


================================================
FILE: boilerplate/App/Navigation/ReduxNavigation.js
================================================
import * as React from 'react'
import { BackHandler, Platform } from 'react-native'
import {
  createReactNavigationReduxMiddleware,
  createReduxContainer
} from 'react-navigation-redux-helpers'
import { connect } from 'react-redux'
import AppNavigation from './AppNavigation'

export const appNavigatorMiddleware = createReactNavigationReduxMiddleware(
  (state) => state.nav,
  'root'
)

const ReduxAppNavigator = createReduxContainer(AppNavigation, 'root')

class ReduxNavigation extends React.Component {
  componentDidMount () {
    if (Platform.OS === 'ios') return
    BackHandler.addEventListener('hardwareBackPress', () => {
      const { dispatch, nav } = this.props
      // change to whatever is your first screen, otherwise unpredictable results may occur
      if (nav.routes.length === 1 && (nav.routes[0].routeName === 'LaunchScreen')) {
        return false
      }
      // if (shouldCloseApp(nav)) return false
      dispatch({ type: 'Navigation/BACK' })
      return true
    })
  }

  componentWillUnmount () {
    if (Platform.OS === 'ios') return
    BackHandler.removeEventListener('hardwareBackPress', undefined)
  }

  render () {
    return <ReduxAppNavigator dispatch={this.props.dispatch} state={this.props.nav} />
  }
}

const mapStateToProps = state => ({
  nav: state.nav
})
export default connect(mapStateToProps)(ReduxNavigation)


================================================
FILE: boilerplate/App/Navigation/Styles/NavigationStyles.js
================================================
import { StyleSheet } from 'react-native'
import { Colors } from '../../Themes/'

export default StyleSheet.create({
  header: {
    backgroundColor: Colors.backgroundColor
  }
})


================================================
FILE: boilerplate/App/Redux/CreateStore.js
================================================
import { createStore, applyMiddleware, compose } from 'redux'
import Config from '../Config/DebugConfig'
import createSagaMiddleware from 'redux-saga'
import ScreenTracking from './ScreenTrackingMiddleware'
import { appNavigatorMiddleware } from '../Navigation/ReduxNavigation'
import Reactotron from '../Config/ReactotronConfig'

// creates the store
export default (rootReducer, rootSaga) => {
  /* ------------- Redux Configuration ------------- */

  const middleware = []
  const enhancers = []

  /* ------------- Navigation Middleware ------------ */
  middleware.push(appNavigatorMiddleware)

  /* ------------- Analytics Middleware ------------- */
  middleware.push(ScreenTracking)

  /* ------------- Saga Middleware ------------- */

  const sagaMonitor = Config.useReactotron ? console.tron.createSagaMonitor() : null
  const sagaMiddleware = createSagaMiddleware({ sagaMonitor })
  middleware.push(sagaMiddleware)

  /* ------------- Assemble Middleware ------------- */

  enhancers.push(applyMiddleware(...middleware))

  // if Reactotron is enabled (default for __DEV__), we'll create the store through Reactotron
  const createAppropriateStore = createStore
  if (Config.useReactotron) {
    enhancers.push(Reactotron.createEnhancer())
  }
  const store = createAppropriateStore(rootReducer, compose(...enhancers))

  // kick off root saga
  let sagasManager = sagaMiddleware.run(rootSaga)

  return {
    store,
    sagasManager,
    sagaMiddleware
  }
}


================================================
FILE: boilerplate/App/Redux/GithubRedux.js
================================================
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  userRequest: ['username'],
  userSuccess: ['avatar'],
  userFailure: null
})

export const GithubTypes = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  avatar: null,
  fetching: null,
  error: null,
  username: null
})

/* ------------- Selectors ------------- */

export const GithubSelectors = {
  selectAvatar: state => state.github.avatar
}

/* ------------- Reducers ------------- */

// request the avatar for a user
export const request = (state, { username }) =>
  state.merge({ fetching: true, username, avatar: null })

// successful avatar lookup
export const success = (state, action) => {
  const { avatar } = action
  return state.merge({ fetching: false, error: null, avatar })
}

// failed to get the avatar
export const failure = (state) =>
  state.merge({ fetching: false, error: true, avatar: null })

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.USER_REQUEST]: request,
  [Types.USER_SUCCESS]: success,
  [Types.USER_FAILURE]: failure
})


================================================
FILE: boilerplate/App/Redux/NavigationRedux.js
================================================
import AppNavigation from '../Navigation/AppNavigation'

export const reducer = (state, action) => {
  const newState = AppNavigation.router.getStateForAction(action, state)
  return newState || state
}


================================================
FILE: boilerplate/App/Redux/ScreenTrackingMiddleware.js
================================================
import { NavigationActions } from 'react-navigation'

// gets the current screen from navigation state
const getCurrentRouteName = (navigationState) => {
  if (!navigationState) {
    return null
  }
  const route = navigationState.routes[navigationState.index]
  // dive into nested navigators
  if (route.routes) {
    return getCurrentRouteName(route)
  }
  return route.routeName
}

const screenTracking = ({ getState }) => next => (action) => {
  if (
    action.type !== NavigationActions.NAVIGATE &&
    action.type !== NavigationActions.BACK
  ) {
    return next(action)
  }

  const currentScreen = getCurrentRouteName(getState().nav)
  const result = next(action)
  const nextScreen = getCurrentRouteName(getState().nav)
  if (nextScreen !== currentScreen) {
    try {
      __DEV__ && console.tron.log(`NAVIGATING ${currentScreen} to ${nextScreen}`)
      // Example: Analytics.trackEvent('user_navigation', {currentScreen, nextScreen})
    } catch (e) {
      __DEV__ && console.tron.log(e)
    }
  }
  return result
}

export default screenTracking


================================================
FILE: boilerplate/App/Redux/SearchRedux.js
================================================
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
import { filter } from 'ramda'
import { startsWith } from 'ramdasauce'

const LIST_DATA = ['sausage', 'blubber', 'pencil', 'cloud', 'moon', 'water', 'computer', 'school',
  'network', 'hammer', 'walking', 'violently', 'mediocre', 'literature', 'chair', 'two', 'window',
  'cords', 'musical', 'zebra', 'xylophone', 'penguin', 'home', 'dog', 'final', 'ink', 'teacher', 'fun',
  'website', 'banana', 'uncle', 'softly', 'mega', 'ten', 'awesome', 'attatch', 'blue', 'internet', 'bottle',
  'tight', 'zone', 'tomato', 'prison', 'hydro', 'cleaning', 'telivision', 'send', 'frog', 'cup', 'book',
  'zooming', 'falling', 'evily', 'gamer', 'lid', 'juice', 'moniter', 'captain', 'bonding', 'loudly', 'thudding',
  'guitar', 'shaving', 'hair', 'soccer', 'water', 'racket', 'table', 'late', 'media', 'desktop', 'flipper',
  'club', 'flying', 'smooth', 'monster', 'purple', 'guardian', 'bold', 'hyperlink', 'presentation', 'world', 'national',
  'comment', 'element', 'magic', 'lion', 'sand', 'crust', 'toast', 'jam', 'hunter', 'forest', 'foraging',
  'silently', 'tawesomated', 'joshing', 'pong', 'RANDOM', 'WORD'
]

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  search: ['searchTerm'],
  cancelSearch: null
})

export const TemperatureTypes = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  searchTerm: '',
  searching: false,
  results: LIST_DATA
})

/* ------------- Reducers ------------- */

export const performSearch = (state, { searchTerm }) => {
  const results = filter(startsWith(searchTerm), LIST_DATA)
  return state.merge({ searching: true, searchTerm, results })
}

export const cancelSearch = (state) => INITIAL_STATE

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.SEARCH]: performSearch,
  [Types.CANCEL_SEARCH]: cancelSearch
})


================================================
FILE: boilerplate/App/Redux/StartupRedux.js
================================================
import { createActions } from 'reduxsauce'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  startup: null
})

export const StartupTypes = Types
export default Creators


================================================
FILE: boilerplate/App/Redux/index.js
================================================
import { combineReducers } from 'redux'
import configureStore from './CreateStore'
import rootSaga from '../Sagas/'

/* ------------- Assemble The Reducers ------------- */
export const reducers = combineReducers({
  nav: require('./NavigationRedux').reducer,
  github: require('./GithubRedux').reducer,
  search: require('./SearchRedux').reducer
})

export default () => {
  let { store, sagasManager, sagaMiddleware } = configureStore(reducers, rootSaga)

  if (module.hot) {
    module.hot.accept(() => {
      const nextRootReducer = require('./').reducers
      store.replaceReducer(nextRootReducer)

      const newYieldedSagas = require('../Sagas').default
      sagasManager.cancel()
      sagasManager.done.then(() => {
        sagasManager = sagaMiddleware(newYieldedSagas)
      })
    })
  }

  return store
}


================================================
FILE: boilerplate/App/Sagas/GithubSagas.js
================================================
import { call, put } from 'redux-saga/effects'
import { path } from 'ramda'
import GithubActions from '../Redux/GithubRedux'

export function * getUserAvatar (api, action) {
  const { username } = action
  // make the call to the api
  const response = yield call(api.getUser, username)

  if (response.ok) {
    const firstUser = path(['data', 'items'], response)[0]
    const avatar = firstUser.avatar_url
    // do data conversion here if needed
    yield put(GithubActions.userSuccess(avatar))
  } else {
    yield put(GithubActions.userFailure())
  }
}


================================================
FILE: boilerplate/App/Sagas/StartupSagas.js
================================================
import { put, select } from 'redux-saga/effects'
import GithubActions, { GithubSelectors } from '../Redux/GithubRedux'
import { is } from 'ramda'

// exported to make available for tests
export const selectAvatar = GithubSelectors.selectAvatar

// process STARTUP actions
export function * startup (action) {
  if (__DEV__ && console.tron) {
    // straight-up string logging
    console.tron.log('Hello, I\'m an example of how to log via Reactotron.')

    // logging an object for better clarity
    console.tron.log({
      message: 'pass objects for better logging',
      someGeneratorFunction: selectAvatar
    })

    // fully customized!
    const subObject = { a: 1, b: [1, 2, 3], c: true }
    subObject.circularDependency = subObject // osnap!
    console.tron.display({
      name: '🔥 IGNITE 🔥',
      preview: 'You should totally expand this',
      value: {
        '💃': 'Welcome to the future!',
        subObject,
        someInlineFunction: () => true,
        someGeneratorFunction: startup,
        someNormalFunction: selectAvatar
      }
    })
  }
  const avatar = yield select(selectAvatar)
  // only get if we don't have it yet
  if (!is(String, avatar)) {
    yield put(GithubActions.userRequest('GantMan'))
  }
}


================================================
FILE: boilerplate/App/Sagas/index.js
================================================
import { takeLatest, all } from 'redux-saga/effects'
import API from '../Services/Api'
import FixtureAPI from '../Services/FixtureApi'
import DebugConfig from '../Config/DebugConfig'

/* ------------- Types ------------- */

import { StartupTypes } from '../Redux/StartupRedux'
import { GithubTypes } from '../Redux/GithubRedux'

/* ------------- Sagas ------------- */

import { startup } from './StartupSagas'
import { getUserAvatar } from './GithubSagas'

/* ------------- API ------------- */

// The API we use is only used from Sagas, so we create it here and pass along
// to the sagas which need it.
const api = DebugConfig.useFixtures ? FixtureAPI : API.create()

/* ------------- Connect Types To Sagas ------------- */

export default function * root () {
  yield all([
    // some sagas only receive an action
    takeLatest(StartupTypes.STARTUP, startup),

    // some sagas receive extra parameters in addition to an action
    takeLatest(GithubTypes.USER_REQUEST, getUserAvatar, api)
  ])
}


================================================
FILE: boilerplate/App/Services/Api.js
================================================
// a library to wrap and simplify api calls
import apisauce from 'apisauce'

// our "constructor"
const create = (baseURL = 'https://api.github.com/') => {
  // ------
  // STEP 1
  // ------
  //
  // Create and configure an apisauce-based api object.
  //
  const api = apisauce.create({
    // base URL is read from the "constructor"
    baseURL,
    // here are some default headers
    headers: {
      'Cache-Control': 'no-cache'
    },
    // 10 second timeout...
    timeout: 10000
  })

  // ------
  // STEP 2
  // ------
  //
  // Define some functions that call the api.  The goal is to provide
  // a thin wrapper of the api layer providing nicer feeling functions
  // rather than "get", "post" and friends.
  //
  // I generally don't like wrapping the output at this level because
  // sometimes specific actions need to be take on `403` or `401`, etc.
  //
  // Since we can't hide from that, we embrace it by getting out of the
  // way at this level.
  //
  const getRoot = () => api.get('')
  const getRate = () => api.get('rate_limit')
  const getUser = (username) => api.get('search/users', {q: username})

  // ------
  // STEP 3
  // ------
  //
  // Return back a collection of functions that we would consider our
  // interface.  Most of the time it'll be just the list of all the
  // methods in step 2.
  //
  // Notice we're not returning back the `api` created in step 1?  That's
  // because it is scoped privately.  This is one way to create truly
  // private scoped goodies in JavaScript.
  //
  return {
    // a list of the API functions from step 2
    getRoot,
    getRate,
    getUser
  }
}

// let's return back our create method as the default.
export default {
  create
}


================================================
FILE: boilerplate/App/Services/ExamplesRegistry.js
================================================
import React from 'react'
import { Text, View } from 'react-native'
import R from 'ramda'
import { ApplicationStyles } from '../Themes'
import DebugConfig from '../Config/DebugConfig'
let globalComponentExamplesRegistry = []
let globalPluginExamplesRegistry = []

export const addComponentExample = (title, usage = () => {}) => { if (DebugConfig.includeExamples) globalComponentExamplesRegistry.push({title, usage}) } // eslint-disable-line

export const addPluginExample = (title, usage = () => {}) => { if (DebugConfig.includeExamples) globalPluginExamplesRegistry.push({title, usage}) } // eslint-disable-line

const renderComponentExample = (example) => {
  return (
    <View key={example.title}>
      <View style={ApplicationStyles.darkLabelContainer}>
        <Text style={ApplicationStyles.darkLabel}>{example.title}</Text>
      </View>
      {example.usage.call()}
    </View>
  )
}

const renderPluginExample = (example) => {
  return (
    <View key={example.title}>
      <View style={ApplicationStyles.darkLabelContainer}>
        <Text style={ApplicationStyles.darkLabel}>{example.title}</Text>
      </View>
      {example.usage.call()}
    </View>
  )
}

export const renderComponentExamples = () => R.map(renderComponentExample, globalComponentExamplesRegistry)

export const renderPluginExamples = () => R.map(renderPluginExample, globalPluginExamplesRegistry)

// Default for readability
export default {
  renderComponentExamples,
  addComponentExample,
  renderPluginExamples,
  addPluginExample
}


================================================
FILE: boilerplate/App/Services/FixtureApi.js
================================================
export default {
  // Functions return fixtures
  getRoot: () => {
    return {
      ok: true,
      data: require('../Fixtures/root.json')
    }
  },
  getRate: () => {
    return {
      ok: true,
      data: require('../Fixtures/rateLimit.json')
    }
  },
  getUser: (username) => {
    // This fixture only supports gantman or else returns skellock
    const gantmanData = require('../Fixtures/gantman.json')
    const skellockData = require('../Fixtures/skellock.json')
    return {
      ok: true,
      data: username.toLowerCase() === 'gantman' ? gantmanData : skellockData
    }
  }
}


================================================
FILE: boilerplate/App/Themes/ApplicationStyles.js
================================================
import Fonts from './Fonts'
import Metrics from './Metrics'
import Colors from './Colors'

// This file is for a reusable grouping of Theme items.
// Similar to an XML fragment layout in Android

const ApplicationStyles = {
  screen: {
    mainContainer: {
      flex: 1,
      backgroundColor: Colors.transparent
    },
    backgroundImage: {
      position: 'absolute',
      top: 0,
      left: 0,
      bottom: 0,
      right: 0
    },
    container: {
      flex: 1,
      paddingTop: Metrics.baseMargin,
      backgroundColor: Colors.transparent
    },
    section: {
      margin: Metrics.section,
      padding: Metrics.baseMargin
    },
    sectionText: {
      ...Fonts.style.normal,
      paddingVertical: Metrics.doubleBaseMargin,
      color: Colors.snow,
      marginVertical: Metrics.smallMargin,
      textAlign: 'center'
    },
    subtitle: {
      color: Colors.snow,
      padding: Metrics.smallMargin,
      marginBottom: Metrics.smallMargin,
      marginHorizontal: Metrics.smallMargin
    },
    titleText: {
      ...Fonts.style.h2,
      fontSize: 14,
      color: Colors.text
    }
  },
  darkLabelContainer: {
    padding: Metrics.smallMargin,
    paddingBottom: Metrics.doubleBaseMargin,
    borderBottomColor: Colors.border,
    borderBottomWidth: 1,
    marginBottom: Metrics.baseMargin
  },
  darkLabel: {
    fontFamily: Fonts.type.bold,
    color: Colors.snow
  },
  groupContainer: {
    margin: Metrics.smallMargin,
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center'
  },
  sectionTitle: {
    ...Fonts.style.h4,
    color: Colors.coal,
    backgroundColor: Colors.ricePaper,
    padding: Metrics.smallMargin,
    marginTop: Metrics.smallMargin,
    marginHorizontal: Metrics.baseMargin,
    borderWidth: 1,
    borderColor: Colors.ember,
    alignItems: 'center',
    textAlign: 'center'
  }
}

export default ApplicationStyles


================================================
FILE: boilerplate/App/Themes/Colors.js
================================================
const colors = {
  background: '#1F0808',
  clear: 'rgba(0,0,0,0)',
  facebook: '#3b5998',
  transparent: 'rgba(0,0,0,0)',
  silver: '#F7F7F7',
  steel: '#CCCCCC',
  error: 'rgba(200, 0, 0, 0.8)',
  ricePaper: 'rgba(255,255,255, 0.75)',
  frost: '#D8D8D8',
  cloud: 'rgba(200,200,200, 0.35)',
  windowTint: 'rgba(0, 0, 0, 0.4)',
  panther: '#161616',
  charcoal: '#595959',
  coal: '#2d2d2d',
  bloodOrange: '#fb5f26',
  snow: 'white',
  ember: 'rgba(164, 0, 48, 0.5)',
  fire: '#e73536',
  drawer: 'rgba(30, 30, 29, 0.95)',
  eggplant: '#251a34',
  border: '#483F53',
  banner: '#5F3E63',
  text: '#E0D7E5'
}

export default colors


================================================
FILE: boilerplate/App/Themes/Fonts.js
================================================
const type = {
  base: 'Avenir-Book',
  bold: 'Avenir-Black',
  emphasis: 'HelveticaNeue-Italic'
}

const size = {
  h1: 38,
  h2: 34,
  h3: 30,
  h4: 26,
  h5: 20,
  h6: 19,
  input: 18,
  regular: 17,
  medium: 14,
  small: 12,
  tiny: 8.5
}

const style = {
  h1: {
    fontFamily: type.base,
    fontSize: size.h1
  },
  h2: {
    fontWeight: 'bold',
    fontSize: size.h2
  },
  h3: {
    fontFamily: type.emphasis,
    fontSize: size.h3
  },
  h4: {
    fontFamily: type.base,
    fontSize: size.h4
  },
  h5: {
    fontFamily: type.base,
    fontSize: size.h5
  },
  h6: {
    fontFamily: type.emphasis,
    fontSize: size.h6
  },
  normal: {
    fontFamily: type.base,
    fontSize: size.regular
  },
  description: {
    fontFamily: type.base,
    fontSize: size.medium
  }
}

export default {
  type,
  size,
  style
}


================================================
FILE: boilerplate/App/Themes/Images.js
================================================
// leave off @2x/@3x
const images = {
  logo: require('../Images/ir.png'),
  clearLogo: require('../Images/top_logo.png'),
  launch: require('../Images/launch-icon.png'),
  ready: require('../Images/your-app.png'),
  ignite: require('../Images/ignite_logo.png'),
  igniteClear: require('../Images/ignite-logo-transparent.png'),
  tileBg: require('../Images/tile_bg.png'),
  background: require('../Images/BG.png'),
  buttonBackground: require('../Images/button-bg.png'),
  api: require('../Images/Icons/icon-api-testing.png'),
  components: require('../Images/Icons/icon-components.png'),
  deviceInfo: require('../Images/Icons/icon-device-information.png'),
  faq: require('../Images/Icons/faq-icon.png'),
  home: require('../Images/Icons/icon-home.png'),
  theme: require('../Images/Icons/icon-theme.png'),
  usageExamples: require('../Images/Icons/icon-usage-examples.png'),
  chevronRight: require('../Images/Icons/chevron-right.png'),
  hamburger: require('../Images/Icons/hamburger.png'),
  backButton: require('../Images/Icons/back-button.png'),
  closeButton: require('../Images/Icons/close-button.png')
}

export default images


================================================
FILE: boilerplate/App/Themes/Metrics.js
================================================
import {Dimensions, Platform} from 'react-native'

const { width, height } = Dimensions.get('window')

// Used via Metrics.baseMargin
const metrics = {
  marginHorizontal: 10,
  marginVertical: 10,
  section: 25,
  baseMargin: 10,
  doubleBaseMargin: 20,
  smallMargin: 5,
  doubleSection: 50,
  horizontalLineHeight: 1,
  screenWidth: width < height ? width : height,
  screenHeight: width < height ? height : width,
  navBarHeight: (Platform.OS === 'ios') ? 64 : 54,
  buttonRadius: 4,
  icons: {
    tiny: 15,
    small: 20,
    medium: 30,
    large: 45,
    xl: 50
  },
  images: {
    small: 20,
    medium: 40,
    large: 60,
    logo: 200
  }
}

export default metrics


================================================
FILE: boilerplate/App/Themes/README.md
================================================
### Themes Folder
Application specific themes
* Base Styles
* Fonts
* Metrics
* Colors

etc.


================================================
FILE: boilerplate/App/Themes/index.js
================================================
import Colors from './Colors'
import Fonts from './Fonts'
import Metrics from './Metrics'
import Images from './Images'
import ApplicationStyles from './ApplicationStyles'

export { Colors, Fonts, Images, Metrics, ApplicationStyles }


================================================
FILE: boilerplate/App/Transforms/ConvertFromKelvin.js
================================================
export default (kelvin: number) => {
  const celsius = kelvin - 273.15
  const fahrenheit = (celsius * 1.8000) + 32

  return Math.round(fahrenheit)
}


================================================
FILE: boilerplate/App/Transforms/README.md
================================================
# Transforms

A common pattern when working with APIs is to change data to play nice between your app & the API.  

We've found this to be the case in every project we've worked on.  So much so that we're recommending that you create a folder dedicated to these transformations.

Transforms are not necessarily a bad thing (although an API might have you transforming more than you'd like).

For example, you may:

* turn appropriate strings to date objects
* convert snake case to camel case
* normalize or denormalize things
* create lookup tables


================================================
FILE: boilerplate/README.md
================================================
#  <%= props.name %>
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/)

* Standard compliant React Native App Utilizing [Ignite](https://github.com/infinitered/ignite)

## :arrow_up: How to Setup

**Step 1:** git clone this repo:

**Step 2:** cd to the cloned repo:

**Step 3:** Install the Application with `yarn` or `npm i`


## :arrow_forward: How to Run App

1. cd to the repo
2. Run Build for either OS
  * for iOS
    * run `npx react-native run-ios`
  * for Android
    * Run Genymotion
    * run `npx react-native run-android`

## :no_entry_sign: Standard Compliant

[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
This project adheres to Standard.  Our CI enforces this, so we suggest you enable linting to keep your project compliant during development.

**To Lint on Commit**

This is implemented using [husky](https://github.com/typicode/husky). There is no additional setup needed.

**Bypass Lint**

If you have to bypass lint for a special commit that you will come back and clean (pushing something to a branch etc.) then you can bypass git hooks with adding `--no-verify` to your commit command.

**Understanding Linting Errors**

The linting rules are from JS Standard and React-Standard.  [Regular JS errors can be found with descriptions here](http://eslint.org/docs/rules/), while [React errors and descriptions can be found here](https://github.com/yannickcr/eslint-plugin-react).

## :closed_lock_with_key: Secrets

This project uses [react-native-config](https://github.com/luggit/react-native-config) to expose config variables to your javascript code in React Native. You can store API keys
and other sensitive information in a `.env` file:

```
API_URL=https://myapi.com
GOOGLE_MAPS_API_KEY=abcdefgh
```

and access them from React Native like so:

```
import Secrets from 'react-native-config'

Secrets.API_URL  // 'https://myapi.com'
Secrets.GOOGLE_MAPS_API_KEY  // 'abcdefgh'
```

The `.env` file is ignored by git keeping those secrets out of your repo.

### Get started:
1. Copy .env.example to .env
2. Add your config variables
3. Follow instructions at [https://github.com/luggit/react-native-config#setup](https://github.com/luggit/react-native-config#setup)
4. Done!


================================================
FILE: boilerplate/Tests/Components/AlertMessageTest.js
================================================
import 'react-native'
import React from 'react'
import AlertMessage from '../../App/Components/AlertMessage'
import renderer from 'react-test-renderer'

test('AlertMessage component renders correctly if show is true', () => {
  const tree = renderer.create(<AlertMessage title='howdy' />).toJSON()
  expect(tree).toMatchSnapshot()
})

test('AlertMessage component does not render if show is false', () => {
  const tree = renderer.create(<AlertMessage title='howdy' show={false} />).toJSON()
  expect(tree).toMatchSnapshot()
})

test('AlertMessage component renders correctly if backgroundColor prop is set', () => {
  const tree = renderer.create(<AlertMessage title='howdy' style={{backgroundColor: 'red'}} />).toJSON()
  expect(tree).toMatchSnapshot()
})


================================================
FILE: boilerplate/Tests/Components/DrawerButtonTest.js
================================================
import 'react-native'
import React from 'react'
import DrawerButton from '../../App/Components/DrawerButton'
import { shallow } from 'enzyme'
import renderer from 'react-test-renderer'

test('DrawerButton component renders correctly', () => {
  const tree = renderer.create(<DrawerButton onPress={() => {}} text='hi' />).toJSON()
  expect(tree).toMatchSnapshot()
})

test('onPress', () => {
  let i = 0
  const onPress = () => i++
  const wrapperPress = shallow(<DrawerButton onPress={onPress} text='hi' />)

  expect(wrapperPress.prop('onPress')).toBe(onPress) // uses the right handler
  expect(i).toBe(0)
  wrapperPress.simulate('press')
  expect(i).toBe(1)
})


================================================
FILE: boilerplate/Tests/Components/FullButtonTest.js
================================================
import 'react-native'
import React from 'react'
import FullButton from '../../App/Components/FullButton'
import { shallow } from 'enzyme'
import renderer from 'react-test-renderer'

test('FullButton component renders correctly', () => {
  const tree = renderer.create(<FullButton onPress={() => {}} text='hi' />).toJSON()
  expect(tree).toMatchSnapshot()
})

test('onPress', () => {
  let i = 0 // i guess i could have used sinon here too... less is more i guess
  const onPress = () => i++
  const wrapperPress = shallow(<FullButton onPress={onPress} text='hi' />)

  expect(wrapperPress.prop('onPress')).toBe(onPress) // uses the right handler
  expect(i).toBe(0)
  wrapperPress.simulate('press')
  expect(i).toBe(1)
})


================================================
FILE: boilerplate/Tests/Components/RoundedButtonTest.js
================================================
import 'react-native'
import React from 'react'
import RoundedButton from '../../App/Components/RoundedButton'
import { shallow } from 'enzyme'
import renderer from 'react-test-renderer'

test('RoundedButton component renders correctly', () => {
  const tree = renderer.create(<RoundedButton onPress={() => {}} text='howdy' />).toJSON()
  expect(tree).toMatchSnapshot()
})

test('RoundedButton component with children renders correctly', () => {
  const tree = renderer.create(<RoundedButton onPress={() => {}}>Howdy</RoundedButton>).toJSON()
  expect(tree).toMatchSnapshot()
})

test('onPress', () => {
  let i = 0 // i guess i could have used sinon here too... less is more i guess
  const onPress = () => i++
  const wrapperPress = shallow(<RoundedButton onPress={onPress} text='hi' />)

  expect(wrapperPress.prop('onPress')).toBe(onPress) // uses the right handler
  expect(i).toBe(0)
  wrapperPress.simulate('press')
  expect(i).toBe(1)
})


================================================
FILE: boilerplate/Tests/Redux/GithubReduxTest.js
================================================
import Actions, { reducer, INITIAL_STATE } from '../../App/Redux/GithubRedux'

test('request', () => {
  const username = 'taco'
  const state = reducer(INITIAL_STATE, Actions.userRequest(username))

  expect(state.fetching).toBe(true)
  expect(state.username).toBe(username)
  expect(state.avatar).toBeNull()
})

test('success', () => {
  const avatar = 'http://placekitten.com/200/300'
  const state = reducer(INITIAL_STATE, Actions.userSuccess(avatar))

  expect(state.fetching).toBe(false)
  expect(state.avatar).toBe(avatar)
  expect(state.error).toBeNull()
})

test('failure', () => {
  const state = reducer(INITIAL_STATE, Actions.userFailure())

  expect(state.fetching).toBe(false)
  expect(state.error).toBe(true)
  expect(state.avatar).toBeNull()
})


================================================
FILE: boilerplate/Tests/Sagas/GithubSagaTest.js
================================================
import FixtureAPI from '../../App/Services/FixtureApi'
import { put, call } from 'redux-saga/effects'
import { getUserAvatar } from '../../App/Sagas/GithubSagas'
import GithubActions from '../../App/Redux/GithubRedux'
import { path } from 'ramda'

const stepper = (fn) => (mock) => fn.next(mock).value

test('first calls API', () => {
  const step = stepper(getUserAvatar(FixtureAPI, {username: 'taco'}))
  // first yield is API
  expect(step()).toEqual(call(FixtureAPI.getUser, 'taco'))
})

test('success path', () => {
  const response = FixtureAPI.getUser('taco')
  const step = stepper(getUserAvatar(FixtureAPI, {username: 'taco'}))
  // first step API
  step()
  // Second step successful return
  const stepResponse = step(response)
  // Get the avatar Url from the response
  const firstUser = path(['data', 'items'], response)[0]
  const avatar = firstUser.avatar_url
  expect(stepResponse).toEqual(put(GithubActions.userSuccess(avatar)))
})

test('failure path', () => {
  const response = {ok: false}
  const step = stepper(getUserAvatar(FixtureAPI, {username: 'taco'}))
  // first step API
  step()
  // Second step failed response
  expect(step(response)).toEqual(put(GithubActions.userFailure()))
})


================================================
FILE: boilerplate/Tests/Sagas/StartupSagaTest.js
================================================
import { select, put } from 'redux-saga/effects'
import { selectAvatar, startup } from '../../App/Sagas/StartupSagas'
import GithubActions from '../../App/Redux/GithubRedux'

const stepper = (fn) => (mock) => fn.next(mock).value

test('watches for the right action', () => {
  const step = stepper(startup())
  GithubActions.userRequest('GantMan')
  expect(step()).toEqual(select(selectAvatar))
  expect(step()).toEqual(put(GithubActions.userRequest('GantMan')))
})


================================================
FILE: boilerplate/Tests/Services/FixtureAPITest.js
================================================
import API from '../../App/Services/Api'
import FixtureAPI from '../../App/Services/FixtureApi'
import R from 'ramda'

test('All fixtures map to actual API', () => {
  const fixtureKeys = R.keys(FixtureAPI).sort()
  const apiKeys = R.keys(API.create())

  const intersection = R.intersection(fixtureKeys, apiKeys).sort()

  // There is no difference between the intersection and all fixtures
  expect(R.equals(fixtureKeys, intersection)).toBe(true)
})

test('FixtureAPI getRate returns the right file', () => {
  const expectedFile = require('../../App/Fixtures/rateLimit.json')

  expect(FixtureAPI.getRate()).toEqual({
    ok: true,
    data: expectedFile
  })
})

test('FixtureAPI getUser returns the right file for gantman', () => {
  const expectedFile = require('../../App/Fixtures/gantman.json')

  expect(FixtureAPI.getUser('GantMan')).toEqual({
    ok: true,
    data: expectedFile
  })
})

test('FixtureAPI getUser returns the right file for skellock as default', () => {
  const expectedFile = require('../../App/Fixtures/skellock.json')

  expect(FixtureAPI.getUser('Whatever')).toEqual({
    ok: true,
    data: expectedFile
  })
})


================================================
FILE: boilerplate/Tests/Setup.js.ejs
================================================
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

configure({ adapter: new Adapter() })

// Mock your external modules here if needed
<%_ if (props.i18n === 'react-native-i18n') { _%>
jest
.mock('react-native-i18n', () => {
  const english = require('../App/I18n/languages/english.json')
  const keys = require('ramda')
  const replace = require('ramda')
  const forEach = require('ramda')

  return {
    t: (key, replacements) => {
      let value = english[key]
      if (!value) return key
      if (!replacements) return value

      forEach((r) => {
        value = replace(`{{${r}}}`, replacements[r], value)
      }, keys(replacements))
      return value
    }
  }
})
<%_ } else { _%>
// jest
// .mock('react-native-device-info', () => {
//   return { isTablet: jest.fn(() => { return false }) }
// })
<%_ } _%>


================================================
FILE: boilerplate/Tests/StoriesTest.js
================================================
import initStoryshots from '@storybook/addon-storyshots'

initStoryshots()


================================================
FILE: boilerplate/ignite.json.ejs
================================================
{
  "createdWith": "<%= props.igniteVersion %>",
  "boilerplate": "ignite-andross",
  "examples": "classic",
  "navigation": "react-navigation",
  "askToOverwrite": true
}


================================================
FILE: boilerplate/index.js.ejs
================================================
import './App/Config/ReactotronConfig'
import { AppRegistry } from 'react-native'
import App from './App/Containers/App'

AppRegistry.registerComponent('<%= props.name %>', () => App)


================================================
FILE: boilerplate/package.json.ejs
================================================
{
  "version": "0.0.1",
  "scripts": {
    "clean": "rm -rf $TMPDIR/react-* && watchman watch-del-all && npm cache clean --force",
    "clean:android": "cd android/ && ./gradlew clean && cd .. && npx react-native run-android",
    "newclear": "rm -rf $TMPDIR/react-* && watchman watch-del-all && rm -rf ios/build && rm -rf node_modules/ && npm cache clean --force && npm i",
    "test:watch": "jest --watch",
    "updateSnapshot": "jest --updateSnapshot",
    "coverage": "jest --coverage && open coverage/lcov-report/index.html || xdg-open coverage/lcov-report/index.html",
    "android:build": "cd android && ./gradlew assembleRelease",
    "android:install": "cd android && ./gradlew assembleRelease && ./gradlew installRelease",
    "android:hockeyapp": "cd android && ./gradlew assembleRelease && puck -submit=auto app/build/outputs/apk/app-release.apk",
    "android:devices": "$ANDROID_HOME/platform-tools/adb devices",
    "android:logcat": "$ANDROID_HOME/platform-tools/adb logcat *:S ReactNative:V ReactNativeJS:V",
    "android:shake": "$ANDROID_HOME/platform-tools/adb devices | grep '\\t' | awk '{print $1}' | sed 's/\\s//g' | xargs -I {} $ANDROID_HOME/platform-tools/adb -s {} shell input keyevent 82",
    "storybook": "storybook start -p 7007"
  },
  "dependencies": {
    "@react-native-community/async-storage": "^1.11.0",
    "@react-native-community/masked-view": "^0.1.10",
    "apisauce": "^1.1.1",
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.2",
    "format-json": "^1.0.3",
    "identity-obj-proxy": "^3.0.0",
    "lodash": "^4.17.17",
    "prop-types": "^15.7.2",
    "querystringify": "^2.1.1",
    "ramda": "^0.27.0",
    "ramdasauce": "^2.1.3",
    "react": "16.13.1",
    "react-native": "0.63.0",
    "react-native-config": "^0.12.0",
    "react-native-device-info": "^5.3.0",
    "react-native-gesture-handler": "1.6.1",
    "react-native-safe-area-context": "^3.0.7",
    "react-native-safe-area-view": "^1.1.1",
    "react-native-screens": "^2.9.0",
    "react-navigation": "4.0.0",
    "react-navigation-redux-helpers": "^4.0.1",
    "react-navigation-stack": "^1.10.3",
    "react-navigation-tabs": "^2.5.6",
    "react-redux": "^7.1.3",
    "redux": "^4.0.4",
    "redux-saga": "^1.1.3",
    "reduxsauce": "^1.1.1",
    "seamless-immutable": "^7.1.4"
  },
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/runtime": "^7.8.4",
    "@react-native-community/eslint-config": "^1.1.0",
    "@storybook/addon-actions": "5.0.11",
    "@storybook/addon-links": "5.0.11",
    "@storybook/addon-storyshots": "^4.1.11",
    "@storybook/addons": "^4.1.11",
    "@storybook/channels": "^4.1.11",
    "@storybook/react-native": "^4.1.11",
    "babel-eslint": "7.1.1",
    "babel-jest": "^25.1.0",
    "babel-plugin-ignite-ignore-reactotron": "^0.3.0",
    "eslint": "^6.6.0",
    "jest": "^25.1.0",
    "metro-react-native-babel-preset": "^0.59.0",
    "react-test-renderer": "16.13.1",
    "reactotron-react-native": "^4.0.2",
    "reactotron-redux": "^3.1.2",
    "reactotron-redux-saga": "^4.2.2",
    "snazzy": "^8.0.0",
    "standard": "10.0.2"
  },
  "jest": {
    "testMatch": [
      "<rootDir>/Tests/**/*.js",
      "**/?(*.)(spec|test).js?(x)"
    ],
    "testPathIgnorePatterns":[
      "/node_modules/",
      "<rootDir>/Tests/Setup.js"
    ],
    "moduleNameMapper": {
      "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "identity-obj-proxy"
    },
    "transform": {
       "^.+\\.(js)$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
    },
    "setupFiles": [
      "<rootDir>/Tests/Setup"
    ],
    "preset": "react-native"
  },
  "config": {}
}


================================================
FILE: boilerplate/storybook/addons.js
================================================
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'


================================================
FILE: boilerplate/storybook/index.js
================================================
import StorybookUI from './storybook'

export default StorybookUI


================================================
FILE: boilerplate/storybook/storybook.ejs
================================================
import { AppRegistry } from 'react-native'
import { getStorybookUI, configure } from '@storybook/react-native'

// import stories
configure(() => {
  require('../App/Components/Stories')
}, module)

// This assumes that storybook is running on the same host as your RN packager,
// to set manually use, e.g. host: 'localhost' option
const StorybookUI = getStorybookUI({ port: 7007, onDeviceUI: true })
AppRegistry.registerComponent('<%= props.name %>', () => StorybookUI)
export default StorybookUI


================================================
FILE: boilerplate.js
================================================
const options = require('./options')
const { mergeDeepRight, pipe, assoc, omit, __ } = require('ramda')
const { getReactNativeVersion } = require('./lib/react-native-version')

/**
 * Is Android installed?
 *
 * $ANDROID_HOME/tools folder has to exist.
 *
 * @param {*} context - The gluegun context.
 * @returns {boolean}
 */
const isAndroidInstalled = function(context) {
  const androidHome = process.env['ANDROID_HOME']
  const hasAndroidEnv = !context.strings.isBlank(androidHome)
  const hasAndroid = hasAndroidEnv && context.filesystem.exists(`${androidHome}/tools`) === 'dir'

  return Boolean(hasAndroid)
}

/**
 * Let's install.
 *
 * @param {any} context - The gluegun context.
 */
async function install(context) {
  const { filesystem, parameters, ignite, reactNative, print, system, prompt, template } = context
  const { colors } = print
  const { red, yellow, bold, gray, blue } = colors

  const perfStart = new Date().getTime()

  const name = parameters.first
  const spinner = print.spin(`using the ${red('Infinite Red')} boilerplate v2 (code name 'Andross')`).succeed()

  // attempt to install React Native or die trying
  const useNpm = !ignite.useYarn
  const rnInstall = await reactNative.install({
    name,
    version: getReactNativeVersion(context),
    useNpm,
  })
  if (rnInstall.exitCode > 0) process.exit(rnInstall.exitCode)

  // remove the __tests__ directory and App.js that come with React Native
  filesystem.remove('__tests__')
  filesystem.remove('App.js')

  // copy our App, Tests & storybook directories
  spinner.text = '▸ copying files'
  spinner.start()
  filesystem.copy(`${__dirname}/boilerplate/App`, `${process.cwd()}/App`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  filesystem.copy(`${__dirname}/boilerplate/Tests`, `${process.cwd()}/Tests`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  filesystem.copy(`${__dirname}/boilerplate/storybook`, `${process.cwd()}/storybook`, {
    overwrite: true,
    matching: '!*.ejs'
  })
  filesystem.dir(`${process.cwd()}/ignite`)
  spinner.stop()

  // --max, --min, interactive
  let answers
  if (parameters.options.max) {
    answers = options.answers.max
  } else if (parameters.options.min) {
    answers = options.answers.min
  } else {
    answers = await prompt.ask(options.questions)
  }

  // generate some templates
  spinner.text = '▸ generating files'
  const templates = [
    { template: 'index.js.ejs', target: 'index.js' },
    { template: 'README.md', target: 'README.md' },
    { template: 'ignite.json.ejs', target: 'ignite/ignite.json' },
    { template: '.editorconfig', target: '.editorconfig' },
    { template: '.babelrc', target: '.babelrc' },
    { template: 'Tests/Setup.js.ejs', target: 'Tests/Setup.js' },
    { template: 'storybook/storybook.ejs', target: 'storybook/storybook.js' },
    { template: '.env.example', target: '.env.example' }
  ]
  const templateProps = {
    name,
    igniteVersion: ignite.version,
    reactNativeVersion: rnInstall.version,
    vectorIcons: answers['vector-icons'],
    animatable: answers['animatable'],
    i18n: answers['i18n']
  }
  await ignite.copyBatch(context, templates, templateProps, {
    quiet: !parameters.options.debug,
    directory: `${ignite.ignitePluginPath()}/boilerplate`
  })

  /**
   * Append to files
   */
  // https://github.com/facebook/react-native/issues/12724
  await filesystem.appendAsync('.gitattributes', '*.bat text eol=crlf')
  filesystem.append('.gitignore', '\n# Misc\n#')
  filesystem.append('.gitignore', '\n.env\n')

  // transform our package.json in case we need to replace variables
  const rawJson = await template.generate({
    directory: `${ignite.ignitePluginPath()}/boilerplate`,
    template: 'package.json.ejs',
    props: templateProps
  })
  const newPackageJson = JSON.parse(rawJson)

  // read in the react-native created package.json
  const currentPackage = filesystem.read('package.json', 'json')

  // deep merge, lol
  const newPackage = pipe(
    assoc('dependencies', mergeDeepRight(currentPackage.dependencies, newPackageJson.dependencies)),
    assoc('devDependencies', mergeDeepRight(currentPackage.devDependencies, newPackageJson.devDependencies)),
    assoc('scripts', mergeDeepRight(currentPackage.scripts, newPackageJson.scripts)),
    mergeDeepRight(__, omit(['dependencies', 'devDependencies', 'scripts'], newPackageJson))
  )(currentPackage)

  // write this out
  filesystem.write('package.json', newPackage, { jsonIndent: 2 })

  spinner.stop()

  // react native link -- must use spawn & stdio: ignore or it hangs!! :(
  // spinner.text = `▸ linking native libraries`
  // spinner.start()
  // await system.spawn('npx react-native link', { stdio: 'ignore' })
  // spinner.stop()

  // pass long the debug flag if we're running in that mode
  const debugFlag = parameters.options.debug ? '--debug' : ''

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // NOTE(steve): I'm re-adding this here because boilerplates now hold permanent files
  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  try {
    // boilerplate adds itself to get plugin.js/generators etc
    // Could be directory, npm@version, or just npm name.  Default to passed in values
    const boilerplate = parameters.options.b || parameters.options.boilerplate || 'ignite-andross'

    await system.spawn(`npx ignite-cli add ${boilerplate} ${debugFlag}`, { stdio: 'inherit' })

    // now run install of Ignite Plugins
    await ignite.addModule('react-navigation', { version: '4.0.0' })
    await ignite.addModule('react-native-gesture-handler', { version: '1.6.1', link: true })

    ignite.patchInFile(`${process.cwd()}/android/app/src/main/java/com/${name.toLowerCase()}/MainActivity.java`, {
      after: 'import com.facebook.react.ReactActivity;',
      insert: `
      import com.facebook.react.ReactActivityDelegate;
      import com.facebook.react.ReactRootView;
      import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;`
    })

    ignite.patchInFile(`${process.cwd()}/android/app/src/main/java/com/${name.toLowerCase()}/MainActivity.java`, {
      after: `public class MainActivity extends ReactActivity {`,
      insert:
        '\n  @Override\n' +
        '  protected ReactActivityDelegate createReactActivityDelegate() {\n' +
        '    return new ReactActivityDelegate(this, getMainComponentName()) {\n' +
        '      @Override\n' +
        '      protected ReactRootView createRootView() {\n' +
        '       return new RNGestureHandlerEnabledRootView(MainActivity.this);\n' +
        '      }\n' +
        '    };\n' +
        '  }'
    })
    if (answers['vector-icons'] === 'react-native-vector-icons') {
      await system.spawn(`npx ignite-cli add vector-icons@1.1.1 ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

    if (answers['i18n'] === 'react-native-i18n') {
      await system.spawn(`npx ignite-cli add i18n@1.2.0 ${debugFlag}`, { stdio: 'inherit' })
    }

    if (answers['animatable'] === 'react-native-animatable') {
      await system.spawn(`npx ignite-cli add animatable@1.0.2 ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

    // dev-screens be installed after vector-icons and animatable so that it can
    // conditionally patch its PluginExamplesScreen
    if (answers['dev-screens'] === 'Yes') {
      await system.spawn(`npx ignite-cli add dev-screens@"2.4.5" ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

    if (answers['redux-persist'] === 'Yes') {
      await system.spawn(`npx ignite-cli add redux-persist@2.0.0 ${debugFlag}`, {
        stdio: 'inherit'
      })
    }

    if (parameters.options.lint !== false) {
      await system.spawn(`npx ignite-cli add standard@1.0.0 ${debugFlag}`, {
        stdio: 'inherit'
      })
    }
  } catch (e) {
    ignite.log(e)
    throw e
  }

  // git configuration
  const gitExists = await filesystem.exists('./.git')
  if (!gitExists && !parameters.options['skip-git'] && system.which('git')) {
    // initial git
    const spinner = print.spin('configuring git')

    // TODO: Make husky hooks optional
    const huskyCmd = '' // `&& node node_modules/husky/bin/install .`
    await system.run(`git init . && git add . && git commit -m "Initial commit." ${huskyCmd}`)

    spinner.succeed(`configured git`)
  }

  const perfDuration = parseInt((new Date().getTime() - perfStart) / 10) / 100
  spinner.succeed(`ignited ${yellow(name)} in ${perfDuration}s`)

  const androidInfo = isAndroidInstalled(context)
    ? ''
    : `\n\nTo run in Android, make sure you've followed the latest react-native setup instructions at https://facebook.github.io/react-native/docs/getting-started.html before using ignite.\nYou won't be able to run ${bold(
        'react-native run-android'
      )} successfully until you have.`

  const successMessage = `
    ${red('Ignite CLI')} ignited ${yellow(name)} in ${gray(`${perfDuration}s`)}

    To get started:

      cd ${name}
      npx react-native run-ios
      npx react-native run-android${androidInfo}
      npx ignite-cli --help

    ${gray(
      'Read the walkthrough at https://github.com/infinitered/ignite-andross/blob/master/readme.md#boilerplate-walkthrough'
    )}

    ${blue('Need additional help? Join our Slack community at http://community.infinite.red.')}

    ${bold('Now get cooking! 🍽')}
  `

  print.info(successMessage)
}

module.exports = {
  install
}


================================================
FILE: commands/generate/component.js
================================================
module.exports = {
  description: 'Generates a component, styles, and an optional test.',
  run: async function(toolbox) {
    // grab some features
    const { parameters, strings, print, ignite } = toolbox
    const { pascalCase, isBlank } = strings
    const config = ignite.loadIgniteConfig()
    const { tests } = config

    const options = parameters.options || {}

    const hasFolder = !isBlank(options.folder)

    // validation
    if (isBlank(parameters.first) && !hasFolder) {
      print.info(`${toolbox.runtime.brand} generate component <name>\n`)
      print.info('A name is required.')
      return
    }

    let componentPath = hasFolder
      ? `${options.folder}/${parameters.first || 'index'}`
      : parameters.first

    let pathComponents = componentPath.split('/').map(pascalCase)
    let name = pathComponents.pop()
    if (name === 'Index') {
      name = 'index'
    }
    const relativePath = pathComponents.length ? pathComponents.join('/') + '/' : ''

    const props = { name }
    const jobs = [
      {
        template: 'component.ejs',
        target: `App/Components/${relativePath}${name}.js`
      },
      {
        template: 'component-style.ejs',
        target: `App/Components/${relativePath}Styles/${name}Style.js`
      },
      tests === 'ava' && {
        template: 'component-test.ejs',
        target: `Test/Components/${relativePath}${name}Test.js`
      }
    ]

    await ignite.copyBatch(toolbox, jobs, props)
  }
}


================================================
FILE: commands/generate/container.js
================================================
const patterns = require('../../lib/patterns')

module.exports = {
  description: 'Generates a redux smart component.',
  run: async function(toolbox) {
    // grab some features
    const { parameters, strings, print, ignite, filesystem } = toolbox
    const { pascalCase, isBlank } = strings
    const config = ignite.loadIgniteConfig()

    // validation
    if (isBlank(parameters.first)) {
      print.info(`${toolbox.runtime.brand} generate container <name>\n`)
      print.info('A name is required.')
      return
    }

    const name = pascalCase(parameters.first)
    const props = { name }

    const jobs = [
      {
        template: 'container.ejs',
        target: `App/Containers/${name}.js`
      },
      {
        template: 'container-style.ejs',
        target: `App/Containers/Styles/${name}Style.js`
      }
    ]

    await ignite.copyBatch(toolbox, jobs, props)

    // if using `react-navigation` go the extra step
    // and insert the container into the nav router
    if (config.navigation === 'react-navigation') {
      const containerName = name
      const appNavFilePath = `${process.cwd()}/App/Navigation/AppNavigation.js`
      const importToAdd = `import ${containerName} from '../Containers/${containerName}'`
      const routeToAdd = `  ${containerName}: { screen: ${containerName} },`

      if (!filesystem.exists(appNavFilePath)) {
        const msg = `No '${appNavFilePath}' file found.  Can't insert container.`
        print.error(msg)
        process.exit(1)
      }

      // insert container import
      ignite.patchInFile(appNavFilePath, {
        after: patterns[patterns.constants.PATTERN_IMPORTS],
        insert: importToAdd
      })

      // insert container route
      ignite.patchInFile(appNavFilePath, {
        after: patterns[patterns.constants.PATTERN_ROUTES],
        insert: routeToAdd
      })
    } else {
      print.info('Container created, manually add it to your navigation')
    }
  }
}


================================================
FILE: commands/generate/generate.js
================================================
module.exports = {
  name: 'generate',
  alias: ['g'],
  run: async toolbox => {
    toolbox.print.printHelp(toolbox)
  }
}


================================================
FILE: commands/generate/list.js
================================================
module.exports = {
  alias: ['ls', 'listview'],
  description: 'Generates a screen with a ListView/Flatlist/SectionList + walkthrough.',
  run: async function(toolbox) {
    const patterns = require('../../lib/patterns')
    // grab some features
    const { print, parameters, strings, ignite, filesystem } = toolbox
    const { pascalCase, isBlank } = strings
    const config = ignite.loadIgniteConfig()

    // validation
    if (isBlank(parameters.first)) {
      print.info(`${toolbox.runtime.brand} generate list <name>\n`)
      print.info('A name is required.')
      return
    }

    const name = pascalCase(parameters.first)
    const props = { name }

    // which type of list in code
    const typeCodeMessage = 'What coding style do you want for your list?'
    const typeCodeChoices = ['Flatlist (new)', 'Listview (classic)']

    // which type of layout?
    const typeMessage = 'What kind of List would you like to generate?'
    const typeChoices = ['Row', 'Grid']

    // Sections or no?
    const typeDataMessage = 'How will your data be presented on this list?'
    const typeDataChoices = ['Single', 'Sectioned']

    // Check for parameters to bypass questions
    let typeCode = parameters.options.codeType
    let type = parameters.options.type
    let dataType = parameters.options.dataType

    // only prompt if type is not defined
    if (!typeCode) {
      // as question 1
      const codeAnswers = await toolbox.prompt.ask({
        name: 'type',
        type: 'list',
        message: typeCodeMessage,
        choices: typeCodeChoices
      })
      typeCode = codeAnswers.type[0] === typeCodeChoices[0] ? 'flatlist' : 'listview'
      
    }

    if (!type) {
      // ask question 2
      const answers = await toolbox.prompt.ask({
        name: 'type',
        type: 'list',
        message: typeMessage,
        choices: typeChoices
      })
     
      type = answers.type[0] === typeDataChoices[0] ? 'row' : 'grid'
     
    }

    if (!dataType) {
      // ask question 3
      const dataAnswers = await toolbox.prompt.ask({
        name: 'type',
        type: 'list',
        message: typeDataMessage,
        choices: typeDataChoices
      })
      dataType = dataAnswers.type[0] === typeDataChoices[0] ? 'single' : 'sectioned'
    }

    // Sorry the following is so confusing, but so are React Native lists
    // There are 3 options and therefore 8 possible combinations
    let componentTemplate =
      dataType.toLowerCase() === 'sectioned' ? typeCode + '-sections' : typeCode
    let styleTemplate = ''
    // Different logic depending on code types
    if (typeCode === 'flatlist') {
      /*
       * The following mess is because FlatList supports numColumns
       * where SectionList does not.
       */
      if (type.toLowerCase() === 'grid' && dataType.toLowerCase() === 'sectioned') {
        // grid + section means we need wrap
        styleTemplate = 'listview-grid-style'
      } else if (type.toLowerCase() === 'grid') {
        componentTemplate = componentTemplate + '-grid'
        // grid + single = no wrap, use columns
        styleTemplate = 'flatlist-grid-style'
      } else {
        // no grids, flatlist basic
        styleTemplate = 'listview-style'
      }
    } else {
      // listview builder
      styleTemplate = type.toLowerCase() === 'grid' ? 'listview-grid-style' : 'listview-style'
    }

    const jobs = [
      {
        template: `${componentTemplate}.ejs`,
        target: `App/Containers/${name}.js`
      },
      {
        template: `${styleTemplate}.ejs`,
        target: `App/Containers/Styles/${name}Style.js`
      }
    ]

    await ignite.copyBatch(toolbox, jobs, props)

    // if using `react-navigation` go the extra step
    // and insert the screen into the nav router
    if (config.navigation === 'react-navigation') {
      const screenName = `${name}`
      const appNavFilePath = `${process.cwd()}/App/Navigation/AppNavigation.js`
      const importToAdd = `import ${screenName} from '../Containers/${screenName}'`
      const routeToAdd = `  ${screenName}: { screen: ${screenName} },`

      if (!filesystem.exists(appNavFilePath)) {
        const msg = `No '${appNavFilePath}' file found.  Can't insert list screen.`
        print.error(msg)
        process.exit(1)
      }

      // insert list screen import
      ignite.patchInFile(appNavFilePath, {
        after: patterns[patterns.constants.PATTERN_IMPORTS],
        insert: importToAdd
      })

      // insert list screen route
      ignite.patchInFile(appNavFilePath, {
        after: patterns[patterns.constants.PATTERN_ROUTES],
        insert: routeToAdd
      })
    } else {
      print.info('List screen created, manually add it to your navigation')
    }
  }
}


================================================
FILE: commands/generate/redux.js
================================================
module.exports = {
  description: ' Generates a action/creator/reducer set for Redux.',
  run: async function(toolbox) {
    // grab some features
    const { parameters, ignite, strings, print } = toolbox
    const { isBlank, pascalCase } = strings
    const config = ignite.loadIgniteConfig()

    // validation
    if (isBlank(parameters.first)) {
      print.info(`${toolbox.runtime.brand} generate redux <name>\n`)
      print.info('A name is required.')
      return
    }

    const name = pascalCase(parameters.first)
    const props = { name }

    const jobs = [{ template: `redux.ejs`, target: `App/Redux/${name}Redux.js` }]
    if (config.tests) {
      jobs.push({
        template: `redux-test-${config.tests}.ejs`,
        target: `Tests/Redux/${name}ReduxTest.js`
      })
    }

    await ignite.copyBatch(toolbox, jobs, props)
  }
}


================================================
FILE: commands/generate/saga.js
================================================
module.exports = {
  description: 'Generates a saga with an optional test.',
  run: async function(toolbox) {
    // grab some features
    const { parameters, ignite, print, strings } = toolbox
    const { pascalCase, isBlank } = strings
    const config = ignite.loadIgniteConfig()
    const { tests } = config

    // validation
    if (isBlank(parameters.first)) {
      print.info(`${toolbox.runtime.brand} generate saga <name>\n`)
      print.info('A name is required.')
      return
    }

    const name = pascalCase(parameters.first)
    const props = { name }

    const jobs = [{ template: `saga.ejs`, target: `App/Sagas/${name}Sagas.js` }]
    if (tests) {
      jobs.push({
        template: `saga-test-${tests}.ejs`,
        target: `Tests/Sagas/${name}SagaTest.js`
      })
    }

    // make the templates
    await ignite.copyBatch(toolbox, jobs, props)
  }
}


================================================
FILE: commands/generate/screen.js
================================================
const patterns = require('../../lib/patterns')

module.exports = {
  description: 'Generates an opinionated container.',
  run: async function(toolbox) {
    // grab some features
    const { parameters, print, strings, ignite, filesystem } = toolbox
    const { pascalCase, isBlank } = strings
    const config = ignite.loadIgniteConfig()

    // validation
    if (isBlank(parameters.first)) {
      print.info(`${toolbox.runtime.brand} generate screen <name>\n`)
      print.info('A name is required.')
      return
    }

    const name = pascalCase(parameters.first)
    const screenName = name.endsWith('Screen') ? name : `${name}Screen`
    const props = { name: screenName }

    const jobs = [
      {
        template: `screen.ejs`,
        target: `App/Containers/${screenName}.js`
      },
      {
        template: `screen-style.ejs`,
        target: `App/Containers/Styles/${screenName}Style.js`
      }
    ]

    // make the templates
    await ignite.copyBatch(toolbox, jobs, props)

    // if using `react-navigation` go the extra step
    // and insert the screen into the nav router
    if (config.navigation === 'react-navigation') {
      const appNavFilePath = `${process.cwd()}/App/Navigation/AppNavigation.js`
      const importToAdd = `import ${screenName} from '../Containers/${screenName}'`
      const routeToAdd = `  ${screenName}: { screen: ${screenName} },`

      if (!filesystem.exists(appNavFilePath)) {
        const msg = `No '${appNavFilePath}' file found.  Can't insert screen.`
        print.error(msg)
        process.exit(1)
      }

      // insert screen import
      ignite.patchInFile(appNavFilePath, {
        after: patterns[patterns.constants.PATTERN_IMPORTS],
        insert: importToAdd
      })

      // insert screen route
      ignite.patchInFile(appNavFilePath, {
        after: patterns[patterns.constants.PATTERN_ROUTES],
        insert: routeToAdd
      })
    } else {
      print.info(`Screen ${screenName} created, manually add it to your navigation`)
    }
  }
}


================================================
FILE: ignite.json
================================================
{
  "generators": [
    "component",
    "container",
    "listview",
    "list",
    "redux",
    "saga",
    "screen"
  ]
}


================================================
FILE: lib/patterns.js
================================================
const constants = {
  PATTERN_IMPORTS: 'imports',
  PATTERN_ROUTES: 'routes'
}
//  [constants.PATTERN_IMPORTS]: `import[\\s\\S]*from\\s+'react-navigation';?`,

module.exports = {
  constants,

  [constants.PATTERN_IMPORTS]: `import { createAppContainer } from 'react-navigation'`,
  [constants.PATTERN_ROUTES]: 'const PrimaryNav'
}


================================================
FILE: lib/react-native-version.js
================================================
const { pathOr, is } = require('ramda')

// the default React Native version for this boilerplate
const REACT_NATIVE_VERSION = '0.63.0'

// where the version lives under gluegun
const pathToVersion = ['parameters', 'options', 'react-native-version']

// accepts the context and returns back the version
const getVersionFromContext = pathOr(REACT_NATIVE_VERSION, pathToVersion)

/**
 * Gets the React Native version to use.
 *
 * Attempts to read it from the command line, and if not there, falls back
 * to the version we want for this boilerplate.  For example:
 *
 *   $ npx ignite-cli new Custom --react-native-version 0.61.1
 *
 * @param {*} context - The gluegun context.
 */
const getReactNativeVersion = (context = {}) => {
  const version = getVersionFromContext(context)
  return is(String, version) ? version : REACT_NATIVE_VERSION
}

module.exports = {
  REACT_NATIVE_VERSION,
  getReactNativeVersion
}


================================================
FILE: options.js
================================================
/**
 * The questions to ask during the install process.
 */
const questions = [
  {
    name: 'dev-screens',
    message: 'Would you like Ignite Development Screens?',
    type: 'list',
    choices: ['No', 'Yes']
  },
  {
    name: 'vector-icons',
    message: 'What vector icon library will you use?',
    type: 'list',
    choices: ['none', 'react-native-vector-icons']
  },
  {
    name: 'i18n',
    message: 'What internationalization library will you use?',
    type: 'list',
    choices: ['none', 'react-native-i18n']
  },
  {
    name: 'animatable',
    message: 'What animation library will you use?',
    type: 'list',
    choices: ['none', 'react-native-animatable']
  },
  {
    name: 'redux-persist',
    message: 'Would you like to include redux-persist?',
    type: 'list',
    choices: ['No', 'Yes']
  }
]

/**
 * The max preset.
 */
const max = {
  'dev-screens': 'Yes',
  'vector-icons': 'react-native-vector-icons',
  i18n: 'react-native-i18n',
  animatable: 'react-native-animatable',
  'redux-persist': 'Yes'
}

/**
 * The min preset.
 */
const min = {
  'dev-screens': 'No',
  'vector-icons': 'none',
  i18n: 'none',
  animatable: 'none',
  'redux-persist': 'No'
}

module.exports = {
  questions,
  answers: { min, max }
}


================================================
FILE: package.json
================================================
{
  "name": "ignite-andross",
  "description": "Infinite Red's hot boilerplate for React Native.",
  "license": "MIT",
  "repository": "infinitered/ignite-andross",
  "homepage": "https://github.com/infinitered/ignite-andross",
  "version": "4.3.0",
  "files": [
    "boilerplate",
    "commands",
    "lib",
    "templates",
    "boilerplate.js",
    "ignite.json",
    "options.js",
    "plugin.js",
    "readme.md",
    "screenExamples.js"
  ],
  "author": {
    "name": "Infinite Red",
    "email": "npm@infinite.red",
    "url": "https://github.com/infinitered/ignite-andross"
  },
  "scripts": {
    "lint": "standard",
    "test": "jest",
    "watch": "jest --runInBand --watch",
    "coverage": "jest --runInBand --coverage",
    "ci:test": "yarn test",
    "ci:publish": "yarn semantic-release",
    "semantic-release": "semantic-release"
  },
  "standard": {
    "parser": "babel-eslint",
    "globals": [
      "__DEV__",
      "jasmine",
      "beforeAll",
      "afterAll",
      "test",
      "expect",
      "describe"
    ]
  },
  "prettier": {
    "semi": false,
    "singleQuote": true,
    "printWidth": 120
  },
  "release": {
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/npm",
      "@semantic-release/github",
      [
        "@semantic-release/git",
        {
          "assets": "package.json",
          "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
        }
      ]
    ]
  },
  "devDependencies": {
    "@semantic-release/git": "^7.0.5",
    "babel-eslint": "^7.1.1",
    "fs-jetpack": "^1.0.0",
    "jest": "^20.0.4",
    "np": "^2.15.0",
    "semantic-release": "^15.12.2",
    "sinon": "^2.3.1",
    "socks": "^2.1.6",
    "standard": "^10.0.2",
    "tempy": "^0.1.0"
  },
  "dependencies": {
    "ramda": "^0.26.1"
  }
}


================================================
FILE: plugin.js
================================================
const screenExamples = require('./screenExamples')

/**
 * Add the plugin.
 *
 * @param {any} context - The gluegun context.
 */
async function add (context) {
  await screenExamples.add(context)
}

/**
 * Remove the plugin.
 *
 * @param {any} context - The gluegun context.
 */
async function remove (context) {
  await screenExamples.remove(context)
}

module.exports = { add, remove }


================================================
FILE: readme.md
================================================
<p align="center"><img src="http://ir_public.s3.amazonaws.com/projects/ignite/ignite-andross-launch-screen.png" alt="logo" width="414px"></p>

# Ignite "Andross" Boilerplate

## Why is this archived?

We really appreciate all the community support in the years since we first released ignite-andross. Our focus has shifted to the latest version of [Ignite](https://github.com/infinitered/ignite), which includes the latest version of our boilerplate. Feel free to fork this library and continue on its legacy if you want. 

NOTE: This repo has been renamed from ignite-ir-boilerplate-andross to ignite-andross. Although web traffic and git operations for the previous name will be redirected, we recommend you update any links and git urls for this repo.


## The original tried and true boilerplate for [Infinite Red](https://infinite.red)'s React Native apps

Currently includes:

* React Native 0.63
* React Navigation 4.0.0
* Redux
* Redux Sagas
* And more!

## Quick Start

When you've installed the [Ignite CLI](https://github.com/infinitered/ignite), you can get started with this boilerplate like this:

```sh
npx ignite-cli new MyLatestCreation
```

By default we'll ask you to choose which boilerplate you'd like. If you just want to use this one you can specify it with `--boilerplate` or `-b`:

```sh
npx ignite-cli new MyLatestCreation --boilerplate andross
```

You can also change the React Native version; just keep in mind, we may not have tested this just yet.

```sh
npx ignite-cli new MyLatestCreation --react-native-version 0.99.0-rc.2
```

By default we'll ask you some questions during install as to which features you'd like.  If you just want them all, you can skip the questions:

```sh
npx ignite-cli new MyLatestCreation --max
```

If you want very few of these extras:

```sh
npx ignite-cli new MyLatestCreation --min
```

## Boilerplate walkthrough

Your `App` folder is where most of the goodies are found in an Ignite Next app. Let's walk through them in more detail. Start with `Containers/App.js` (described below) and work your way down the walkthrough in order.

### Containers

Containers are (mostly) full screens, although they can be sections of screens or application containers.

* `App.js` - your main application. We create a Redux store and configure it here
* `RootContainer.js` - main view of your application. Contains your status bar and navigation component
* `LaunchScreen.js` - this is the first screen shown in your application. It's loaded into the Navigation component
* `Styles` - styling for each of the above containers and screens

To generate a new Container or Screen you can use the following generator commands:

* `npx ignite-cli g container New` - Will create a `New.js` and also a `Styles/NewStyle.js`.
* `npx ignite-cli g list New` - The same as the `container` command, but it will give you a walkthrough to generate a ListView screen. Allowing you to even pick `FlatList` or not, grid, and some other options.
* `npx ignite-cli g screen New` - Will create a `NewScreen.js` and also a `Styles/NewScreenStyle.js`. Important to mention that the `screen` generator will add the `Screen` on the file/class name to make easier to identify.

Those commands will also add the new container to the navigations file.

### Navigation

Your primary and other navigation components reside here.

* `AppNavigation.js` - loads in your initial screen and creates your menu(s) in a StackNavigation
* `Styles` - styling for the navigation
* `ReduxNavigation.js` - This file contains the core navigation of your application. If you ever change your launch screen, make sure to change it also at `if (nav.routes.length === 1 && (nav.routes[0].routeName === 'LaunchScreen')) {`, otherwise you may encounter navigation problems with the Android back button!

### Components

React components go here...pretty self-explanatory. We won't go through each in detail -- open each file to read the comments and view the code.

To generate a new Component you can use the following generator commands:

* `npx ignite-cli g component New` - Will create a `New.js` and also a `Styles/NewStyle.js`.
* `npx ignite-cli g component path/New` - The same as above, but will use a relative path
* `npx ignite-cli g component --folder path` - An alternative to `npx ignite-cli g component path/index`
* `npx ignite-cli g component --folder path new ` - An alternative to `npx ignite-cli g component relativePath/New`

### Storybook

[Storybook](https://storybook.js.org/) has been setup to show off components in the different states. Storybook is a great way to develop and test components outside of use in your app. Simply run `npm run storybook` to get started. All stores are contained in the `*.story.js` files along side the components.

### Themes

Styling themes used throughout your app styles.

* `ApplicationStyles.js` - app-wide styles
* `Colors.js` - defined colors for your app
* `Fonts.js` - defined fonts for your app
* `Images.js` - loads and caches images used in your app
* `Metrics.js` - useful measurements of things like navBarHeight

### Config

Initialize and configure things here.

* `AppConfig.js` - simple React Native configuration here
* `DebugConfig.js` - define how you want your debug environment to act
* `ReactotronConfig.js` - configures [Reactotron](https://github.com/infinitered/reactotron) in your project (Note: this [will be extracted](https://github.com/infinitered/ignite/issues/779) into a plugin in the future)
* `ReduxPersist.js` - configures Redux Persist (Note: this [will be extracted](https://github.com/infinitered/ignite/issues/780) into a plugin in the future)

### Fixtures

Contains json files that mimic API responses for quicker development. These are used by the `Services/FixtureApi.js` object to mock API responses.

### Redux, Sagas

Contains a preconfigured Redux and Redux-Sagas setup. Review each file carefully to see how Redux interacts with your application.

Here again we have generators to help you out. You just have to use one of the following:

* `npx ignite-cli g redux Amazing` - Will generate and link the redux for `Amazing`.
* `npx ignite-cli g saga Amazing` - The same as above, but for the Sagas

You can read more about Redux and Redux Sagas in these blog posts:

* [Using redux-saga To Simplify Your Growing React Native Codebase](https://shift.infinite.red/using-redux-saga-to-simplify-your-growing-react-native-codebase-2b8036f650de)
* [A Tour of React Native — Part 2: Redux & Friends](https://shift.infinite.red/a-tour-of-react-native-part-2-redux-friends-4fed022aaa1e)

### Services

Contains your API service and other important utilities for your application.

* `Api.js` - main API service, giving you an interface to communicate with your back end
* `ExamplesRegistry.js` - lets you view component and Ignite plugin examples in your app
* `FixtureApi.js` - mocks your API service, making it faster to develop early on in your app
* `ImmutablePersistenceTransform.js` - part of the redux-persist implementation (will be removed)
* `RehydrationServices.js` - part of the redux-persist implementation (will be removed)

### Lib

We recommend using this folder for modules that can be extracted into their own NPM packages at some point.

### Images

Contains actual images (usually png) used in your application.

### Transforms

Helpers for transforming data between API and your application and vice versa. An example is provided that you can look at to see how it works.

### Tests

This folder (located as a sibling to `App`) contains sample Jest snapshot and unit tests for your application.

If you would like to have the `npx ignite-cli generate` command include the generation of tests when available, add
`"tests": "jest"` or `"tests": "ava"` to `./ignite/ignite.json`, depending on the test runner you are using.

**Previous Boilerplates**

* [2016 aka Ignite 1.0](https://github.com/infinitered/ignite-ir-boilerplate-2016)


## Premium Support

[Ignite CLI](https://infinite.red/ignite) and [Ignite Andross](https://github.com/infinitered/ignite-andross), as open source projects, are free to use and always will be. [Infinite Red](https://infinite.red/) offers premium Ignite CLI support and general mobile app design/development services. Email us at [hello@infinite.red](mailto:hello@infinite.red) to get in touch with us for more details.


================================================
FILE: screenExamples.js
================================================
const screenExamples = [
  {
    title: 'Row Example',
    screen: 'examples/RowExample.js.ejs',
    ancillary: ['examples/Styles/RowExampleStyle.js.ejs']
  },
  {
    title: 'Grid Example',
    screen: 'examples/GridExample.js.ejs',
    ancillary: ['examples/Styles/GridExampleStyle.js.ejs']
  },
  {
    title: 'Sections Example',
    screen: 'examples/SectionExample.js.ejs',
    ancillary: ['examples/Styles/SectionExampleStyle.js.ejs']
  }
]

/**
 * Adds the screen examples.
 *
 * @param {any} context The gluegun context.
 */
async function add (context) {
  // examples of generated screens
  await context.ignite.addPluginScreenExamples(screenExamples)
}

/**
 * Removes the screen examples.
 *
 * @param {any} context The gluegun context.
 */
async function remove (context) {
  // remove screens
  await context.ignite.removePluginScreenExamples(screenExamples)
}

module.exports = {
  add, remove
}


================================================
FILE: templates/component-style.ejs
================================================
import { StyleSheet } from 'react-native'

export default StyleSheet.create({
  container: {
    flex: 1
  }
})


================================================
FILE: templates/component-test.ejs
================================================
import test from 'ava'
import React from 'react'
import { shallow } from 'enzyme'
import <%= props.name %> from '../../App/Components/<%= props.name %>'

const wrapper = shallow(<<%= props.name %> />)

test('component exists', t => {
  t.is(wrapper.length, 1) // exists
})

test('component structure', t => {
  t.is(wrapper.name(), 'View') // the right root component
  t.is(wrapper.children().length, 1) // has 1 child
  t.is(wrapper.children().first().name(), 'Text') // that child is Text
  t.true(wrapper.children().first().containsMatchingElement('<%= props.name %> Component')) // That the Component Text is included
})

// test('some other things here', t => {
// const wrapper = shallow(<YourComponentNameHere // SomeProps > )
   // You can add in props as shown above, or use the constant wrapper declared
   // at the top of the file.
// })


================================================
FILE: templates/component.ejs
================================================
import React from 'react'
// import PropTypes from 'prop-types';
import { View, Text } from 'react-native'
import styles from './Styles/<%= props.name %>Style'

const <%= props.name %> = () => {
  return (
    <View style={styles.container}>
      <Text><%= props.name %> Component</Text>
    </View>
  )
}

// // Prop type warnings
// <%= props.name %>.propTypes = {
//   someProperty: PropTypes.object,
//   someSetting: PropTypes.bool.isRequired,
// }
//
// // Defaults for props
// <%= props.name %>.defaultProps = {
//   someSetting: false
// }

export default <%= props.name %>


================================================
FILE: templates/container-style.ejs
================================================
import { StyleSheet } from 'react-native'
import { Colors, Metrics } from '../../Themes/'

export default StyleSheet.create({
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background
  }
})


================================================
FILE: templates/container.ejs
================================================
import React from 'react'
import { ScrollView, Text } from 'react-native'
import { connect } from 'react-redux'
// Add Actions - replace 'Your' with whatever your reducer is called :)
// import YourActions from '../Redux/YourRedux'

// Styles
import styles from './Styles/<%= props.name %>Style'

const <%= props.name %> = () => {
  return (
    <ScrollView style={styles.container}>
      <Text><%= props.name %> Container</Text>
    </ScrollView>
  )
}

const mapStateToProps = (state) => {
  return {
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: templates/examples/GridExample.js.ejs
================================================
import React, { Component } from 'react'
import { View, Text, ListView } from 'react-native'
import { connect } from 'react-redux'

// For empty lists
// import AlertMessage from '../Components/AlertMessage'

// Styles
import styles from './Styles/GridExampleStyle'

class GridExample extends Component {
  constructor (props) {
    super(props)
    // If you need scroll to bottom, consider http://bit.ly/2bMQ2BZ

    /* ***********************************************************
    * STEP 1
    * This is an array of objects with the properties you desire
    * Usually this should come from Redux mapStateToProps
    *************************************************************/
    const dataObjects = [
      {title: 'First Title', description: 'First Description'},
      {title: 'Second Title', description: 'Second Description'},
      {title: 'Third Title', description: 'Third Description'},
      {title: 'Fourth Title', description: 'Fourth Description'},
      {title: 'Fifth Title', description: 'Fifth Description'},
      {title: 'Sixth Title', description: 'Sixth Description'},
      {title: 'Seventh Title', description: 'Seventh Description'}
    ]

    /* ***********************************************************
    * STEP 2
    * Teach datasource how to detect if rows are different
    * Make this function fast!  Perhaps something like:
    *   (r1, r2) => r1.id !== r2.id}
    *************************************************************/
    const rowHasChanged = (r1, r2) => r1 !== r2

    // DataSource configured
    const ds = new ListView.DataSource({rowHasChanged})

    // Datasource is always in state
    this.state = {
      dataSource: ds.cloneWithRows(dataObjects)
    }
  }

  /* ***********************************************************
  * STEP 3
  * `_renderRow` function -How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={rowData.title} description={rowData.description} />
  *************************************************************/
  _renderRow (rowData) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{rowData.title}</Text>
        <Text style={styles.label}>{rowData.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 4
  * If your datasource is driven by Redux, you'll need to
  * reset it when new data arrives.
  * DO NOT! place `cloneWithRows` inside of render, since render
  * is called very often, and should remain fast!  Just replace
  * state's datasource on newProps.
  *
  * e.g.
    componentWillReceiveProps (newProps) {
      if (newProps.someData) {
        this.setState(prevState => ({
          dataSource: prevState.dataSource.cloneWithRows(newProps.someData)
        }))
      }
    }
  *************************************************************/

  // Used for friendly AlertMessage
  // returns true if the dataSource is empty
  _noRowData () {
    return this.state.dataSource.getRowCount() === 0
  }

  // Render a footer.
  _renderFooter = () => {
    return (
      <Text> - Footer - </Text>
    )
  }

  render () {
    return (
      <View style={styles.container}>
        <ListView
          contentContainerStyle={styles.listContent}
          dataSource={this.state.dataSource}
          renderRow={this._renderRow}
          renderFooter={this._renderFooter}
          enableEmptySections
          pageSize={15}
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(GridExample)


================================================
FILE: templates/examples/RowExample.js.ejs
================================================
import React, { Component } from 'react'
import { View, Text, ListView } from 'react-native'
import { connect } from 'react-redux'

// Styles
import styles from './Styles/RowExampleStyle'

class RowExample extends Component {
  constructor (props) {
    super(props)
    // If you need scroll to bottom, consider http://bit.ly/2bMQ2BZ

    /* ***********************************************************
    * STEP 1
    * This is an array of objects with the properties you desire
    * Usually this should come from Redux mapStateToProps
    *************************************************************/
    const dataObjects = [
      {title: 'First Title', description: 'First Description'},
      {title: 'Second Title', description: 'Second Description'},
      {title: 'Third Title', description: 'Third Description'},
      {title: 'Fourth Title', description: 'Fourth Description'},
      {title: 'Fifth Title', description: 'Fifth Description'},
      {title: 'Sixth Title', description: 'Sixth Description'},
      {title: 'Seventh Title', description: 'Seventh Description'}
    ]

    /* ***********************************************************
    * STEP 2
    * Teach datasource how to detect if rows are different
    * Make this function fast!  Perhaps something like:
    *   (r1, r2) => r1.id !== r2.id}
    *************************************************************/
    const rowHasChanged = (r1, r2) => r1 !== r2

    // DataSource configured
    const ds = new ListView.DataSource({rowHasChanged})

    // Datasource is always in state
    this.state = {
      dataSource: ds.cloneWithRows(dataObjects)
    }
  }

  /* ***********************************************************
  * STEP 3
  * `_renderRow` function -How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={rowData.title} description={rowData.description} />
  *************************************************************/
  _renderRow (rowData) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{rowData.title}</Text>
        <Text style={styles.label}>{rowData.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 4
  * If your datasource is driven by Redux, you'll need to
  * reset it when new data arrives.
  * DO NOT! place `cloneWithRows` inside of render, since render
  * is called very often, and should remain fast!  Just replace
  * state's datasource on newProps.
  *
  * e.g.
    componentWillReceiveProps (newProps) {
      if (newProps.someData) {
        this.setState(prevState => ({
          dataSource: prevState.dataSource.cloneWithRows(newProps.someData)
        }))
      }
    }
  *************************************************************/

  // Used for friendly AlertMessage
  // returns true if the dataSource is empty
  _noRowData () {
    return this.state.dataSource.getRowCount() === 0
  }

  // Render a footer.
  _renderFooter = () => {
    return (
      <Text> - Footer - </Text>
    )
  }

  render () {
    return (
      <View style={styles.container}>
        <ListView
          contentContainerStyle={styles.listContent}
          dataSource={this.state.dataSource}
          renderRow={this._renderRow}
          renderFooter={this._renderFooter}
          enableEmptySections
          pageSize={15}
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(RowExample)


================================================
FILE: templates/examples/SectionExample.js.ejs
================================================
import React, { Component } from 'react'
import { View, ListView, Text } from 'react-native'
import { connect } from 'react-redux'

// Styles
import styles from './Styles/SectionExampleStyle'

class ListviewSectionsExample extends Component {
  constructor (props) {
    super(props)

    /* ***********************************************************
    * STEP 1
    * This is an array of objects with the properties you desire
    * Usually this should come from Redux mapStateToProps
    *************************************************************/
    const dataObjects = {
      first: [
        {title: 'First Title', description: 'First Description'},
        {title: 'Second Title', description: 'Second Description'},
        {title: 'Third Title', description: 'Third Description'},
        {title: 'Fourth Title', description: 'Fourth Description'},
        {title: 'Fifth Title', description: 'Fifth Description'},
        {title: 'Sixth Title', description: 'Sixth Description'},
        {title: 'Seventh Title', description: 'Seventh Description'},
        {title: 'Eighth Title', description: 'Eighth Description'},
        {title: 'Ninth Title', description: 'Ninth Description'},
        {title: 'Tenth Title', description: 'Tenth Description'}
      ],
      second: [
        {title: 'Eleventh Title', description: 'Eleventh Description'},
        {title: '12th Title', description: '12th Description'},
        {title: '13th Title', description: '13th Description'},
        {title: '14th Title', description: '14th Description'},
        {title: '15th Title', description: '15th Description'},
        {title: '16th Title', description: '16th Description'},
        {title: '17th Title', description: '17th Description'},
        {title: '18th Title', description: '18th Description'},
        {title: '19th Title', description: '19th Description'},
        {title: '20th Title', description: '20th Description'},
        {title: 'BLACKJACK!', description: 'BLACKJACK! Description'}
      ]
    }
    /* ***********************************************************
    * STEP 2
    * Teach datasource how to detect if rows are different
    * Make this function fast!  Perhaps something like:
    *   (r1, r2) => r1.id !== r2.id}
    *   The same goes for sectionHeaderHasChanged
    *************************************************************/
    const rowHasChanged = (r1, r2) => r1 !== r2
    const sectionHeaderHasChanged = (s1, s2) => s1 !== s2

    // DataSource configured
    const ds = new ListView.DataSource({rowHasChanged, sectionHeaderHasChanged})

    // Datasource is always in state
    this.state = {
      dataSource: ds.cloneWithRowsAndSections(dataObjects)
    }
  }

  /* ***********************************************************
  * STEP 3
  * `_renderRow` function -How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={rowData.title} description={rowData.description} />
  *************************************************************/
  _renderRow (rowData, sectionID) {
    // You can condition on sectionID (key as string), for different cells
    // in different sections
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>Section {sectionID} - {rowData.title}</Text>
        <Text style={styles.label}>{rowData.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 4
  * If your datasource is driven by Redux, you'll need to
  * reset it when new data arrives.
  * DO NOT! place `cloneWithRowsAndSections` inside of render, since render
  * is called very often, and should remain fast!  Just replace
  * state's datasource on newProps.
  *
  * e.g.
    componentWillReceiveProps (newProps) {
      if (newProps.someData) {
        this.setState(prevState => ({
          dataSource: prevState.dataSource.cloneWithRowsAndSections(newProps.someData)
        }))
      }
    }
  *************************************************************/

  // Used for friendly AlertMessage
  // returns true if the dataSource is empty
  _noRowData () {
    return this.state.dataSource.getRowCount() === 0
  }

  _renderHeader (data, sectionID) {
    switch (sectionID) {
      case 'first':
        return <Text style={styles.boldLabel}>First Section</Text>
      default:
        return <Text style={styles.boldLabel}>Second Section</Text>
    }
  }

  render () {
    return (
      <View style={styles.container}>
        <ListView
          renderSectionHeader={this._renderHeader}
          contentContainerStyle={styles.listContent}
          dataSource={this.state.dataSource}
          onLayout={this.onLayout}
          renderRow={this._renderRow}
          enableEmptySections
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ListviewSectionsExample)


================================================
FILE: templates/examples/Styles/GridExampleStyle.js.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../../../../DevScreens/DevTheme/'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background
  },
  row: {
    width: 100,
    height: 100,
    justifyContent: 'center',
    alignItems: 'center',
    margin: Metrics.baseMargin,
    backgroundColor: Colors.fire,
    borderRadius: Metrics.smallMargin
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center'
  },
  listContent: {
    justifyContent: 'space-around',
    flexDirection: 'row',
    flexWrap: 'wrap'
  }
})


================================================
FILE: templates/examples/Styles/RowExampleStyle.js.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../../../../DevScreens/DevTheme/'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center'
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})


================================================
FILE: templates/examples/Styles/SectionExampleStyle.js.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../../../../DevScreens/DevTheme/'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center'
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})


================================================
FILE: templates/flatlist-grid-style.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../Themes'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center',
    margin: 10,
    padding: 5,
    paddingVertical: 10,
    borderRadius: Metrics.smallMargin
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})


================================================
FILE: templates/flatlist-grid.ejs
================================================
import React from 'react'
import { View, Text, FlatList } from 'react-native'
import { connect } from 'react-redux'

// More info here: https://facebook.github.io/react-native/docs/flatlist.html

// Styles
import styles from './Styles/<%= props.name %>Style'

class <%= props.name %> extends React.PureComponent {
  /* ***********************************************************
  * STEP 1
  * This is an array of objects with the properties you desire
  * Usually this should come from Redux mapStateToProps
  *************************************************************/
  state = {
    dataObjects: [
      {title: 'First Title', description: 'First Description'},
      {title: 'Second Title', description: 'Second Description'},
      {title: 'Third Title', description: 'Third Description'},
      {title: 'Fourth Title', description: 'Fourth Description'},
      {title: 'Fifth Title', description: 'Fifth Description'},
      {title: 'Sixth Title', description: 'Sixth Description'},
      {title: 'Seventh Title', description: 'Seventh Description'}
    ]
  }

  /* ***********************************************************
  * STEP 2
  * `renderRow` function. How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={item.title} description={item.description} />
  *************************************************************/
  renderRow ({item}) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{item.title}</Text>
        <Text style={styles.label}>{item.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 3
  * Consider the configurations we've set below.  Customize them
  * to your liking!  Each with some friendly advice.
  *************************************************************/
  // Render a header?
  renderHeader = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Header - </Text>

  // Render a footer?
  renderFooter = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Footer - </Text>

  // Show this when data is empty
  renderEmpty = () =>
    <Text style={styles.label}> - Nothing to See Here - </Text>

  renderSeparator = () =>
    <Text style={styles.label}> - ~~~~~ - </Text>

  // The default function if no Key is provided is index
  // an identifiable key is important if you plan on
  // item reordering.  Otherwise index is fine
  keyExtractor = (item, index) => `${index}`

  // How many items should be kept im memory as we scroll?
  oneScreensWorth = 20

  // extraData is for anything that is not indicated in data
  // for instance, if you kept "favorites" in `this.state.favs`
  // pass that in, so changes in favorites will cause a re-render
  // and your renderItem will have access to change depending on state
  // e.g. `extraData`={this.state.favs}

  // Optimize your list if the height of each item can be calculated
  // by supplying a constant height, there is no need to measure each
  // item after it renders.  This can save significant time for lists
  // of a size 100+
  // e.g. itemLayout={(data, index) => (
  //   {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  // )}

  render () {
    return (
      <View style={styles.container}>
        <FlatList
          contentContainerStyle={styles.listContent}
          data={this.state.dataObjects}
          renderItem={this.renderRow}
          numColumns={2}
          keyExtractor={this.keyExtractor}
          initialNumToRender={this.oneScreensWorth}
          ListHeaderComponent={this.renderHeader}
          ListFooterComponent={this.renderFooter}
          ListEmptyComponent={this.renderEmpty}
          ItemSeparatorComponent={this.renderSeparator}
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: templates/flatlist-sections.ejs
================================================
import React from 'react'
import { View, SectionList, Text } from 'react-native'
import { connect } from 'react-redux'

// More info here: https://facebook.github.io/react-native/docs/sectionlist.html

// Styles
import styles from './Styles/<%= props.name %>Style'

class <%= props.name %> extends React.PureComponent {
  /* ***********************************************************
  * STEP 1
  * This is an array of objects with the properties you desire
  * Usually this should come from Redux mapStateToProps
  *************************************************************/
  state = {
    data: [
      {
        key: 'First',
        data: [
          {title: 'First Title', description: 'First Description'},
          {title: 'Second Title', description: 'Second Description'},
          {title: 'Third Title', description: 'Third Description'},
          {title: 'Fourth Title', description: 'Fourth Description'},
          {title: 'Fifth Title', description: 'Fifth Description'},
          {title: 'Sixth Title', description: 'Sixth Description'},
          {title: 'Seventh Title', description: 'Seventh Description'},
          {title: 'Eighth Title', description: 'Eighth Description'},
          {title: 'Ninth Title', description: 'Ninth Description'},
          {title: 'Tenth Title', description: 'Tenth Description'}
        ]
      }, {
        key: 'Second',
        data: [
          {title: 'Eleventh Title', description: 'Eleventh Description'},
          {title: '12th Title', description: '12th Description'},
          {title: '13th Title', description: '13th Description'},
          {title: '14th Title', description: '14th Description'},
          {title: '15th Title', description: '15th Description'},
          {title: '16th Title', description: '16th Description'},
          {title: '17th Title', description: '17th Description'},
          {title: '18th Title', description: '18th Description'},
          {title: '19th Title', description: '19th Description'},
          {title: '20th Title', description: '20th Description'},
          {title: 'BLACKJACK!', description: 'BLACKJACK! Description'}
        ]
      }
    ]
  }

  /* ***********************************************************
  * STEP 3
  * `renderItem` function - How each cell should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
  *   return <MyCustomCell title={item.title} description={item.description} />
  *
  * For sections with different cells (heterogeneous lists), you can do branch
  * logic here based on section.key OR at the data level, you can provide
  * `renderItem` functions in each section.
  *
  * Note: You can remove section/separator functions and jam them in here
  *************************************************************/
  renderItem ({section, item}) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>Section {section.key} - {item.title}</Text>
        <Text style={styles.label}>{item.description}</Text>
      </View>
    )
  }

  // Conditional branching for section headers, also see step 3
  renderSectionHeader ({section}) {
    switch (section.key) {
      case 'First':
        return <View style={styles.sectionHeader}><Text style={styles.boldLabel}>First Section</Text></View>
      default:
        return <View style={styles.sectionHeader}><Text style={styles.boldLabel}>Second Section</Text></View>
    }
  }

  /* ***********************************************************
  * STEP 2
  * Consider the configurations we've set below.  Customize them
  * to your liking!  Each with some friendly advice.
  *
  * Removing a function here will make SectionList use default
  *************************************************************/
  // Render a header?
  renderHeader = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Full List Header - </Text>

  // Render a footer?
  renderFooter = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Full List Footer - </Text>

  // Does each section need a footer?
  renderSectionFooter = () =>
    <Text style={styles.label}> END SECTION!!!! </Text>

  // Show this when data is empty
  renderEmpty = () =>
    <Text style={styles.label}> - Nothing to See Here - </Text>

  renderSeparator = () =>
    <Text style={styles.label}> - ~~~~~ - </Text>

  renderSectionSeparator = () =>
    <Text style={styles.label}> \/\/\/\/\/\/\/\/ </Text>

  // The default function if no Key is provided is index
  // an identifiable key is important if you plan on
  // item reordering.  Otherwise index is fine
  keyExtractor = (item, index) => `${index}`

  // How many items should be kept im memory as we scroll?
  oneScreensWorth = 20

  // extraData is for anything that is not indicated in data
  // for instance, if you kept "favorites" in `this.state.favs`
  // pass that in, so changes in favorites will cause a re-render
  // and your renderItem will have access to change depending on state
  // e.g. `extraData`={this.state.favs}

  // Optimize your list if the height of each item can be calculated
  // by supplying a constant height, there is no need to measure each
  // item after it renders.  This can save significant time for lists
  // of a size 100+
  // e.g. itemLayout={(data, index) => (
  //   {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  // )}

  render () {
    return (
      <View style={styles.container}>
        <SectionList
          renderSectionHeader={this.renderSectionHeader}
          sections={this.state.data}
          contentContainerStyle={styles.listContent}
          renderItem={this.renderItem}
          keyExtractor={this.keyExtractor}
          initialNumToRender={this.oneScreensWorth}
          ListHeaderComponent={this.renderHeader}
          SectionSeparatorComponent={this.renderSectionSeparator}
          ListFooterComponent={this.renderFooter}
          ListEmptyComponent={this.renderEmpty}
          ItemSeparatorComponent={this.renderSeparator}
          renderSectionFooter={this.renderSectionFooter}
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: templates/flatlist.ejs
================================================
import React from 'react'
import { View, Text, FlatList } from 'react-native'
import { connect } from 'react-redux'

// More info here: https://facebook.github.io/react-native/docs/flatlist.html

// Styles
import styles from './Styles/<%= props.name %>Style'

class <%= props.name %> extends React.PureComponent {
  /* ***********************************************************
  * STEP 1
  * This is an array of objects with the properties you desire
  * Usually this should come from Redux mapStateToProps
  *************************************************************/
  state = {
    dataObjects: [
      {title: 'First Title', description: 'First Description'},
      {title: 'Second Title', description: 'Second Description'},
      {title: 'Third Title', description: 'Third Description'},
      {title: 'Fourth Title', description: 'Fourth Description'},
      {title: 'Fifth Title', description: 'Fifth Description'},
      {title: 'Sixth Title', description: 'Sixth Description'},
      {title: 'Seventh Title', description: 'Seventh Description'}
    ]
  }

  /* ***********************************************************
  * STEP 2
  * `renderRow` function. How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={item.title} description={item.description} />
  *************************************************************/
  renderRow ({item}) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{item.title}</Text>
        <Text style={styles.label}>{item.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 3
  * Consider the configurations we've set below.  Customize them
  * to your liking!  Each with some friendly advice.
  *************************************************************/
  // Render a header?
  renderHeader = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Header - </Text>

  // Render a footer?
  renderFooter = () =>
    <Text style={[styles.label, styles.sectionHeader]}> - Footer - </Text>

  // Show this when data is empty
  renderEmpty = () =>
    <Text style={styles.label}> - Nothing to See Here - </Text>

  renderSeparator = () =>
    <Text style={styles.label}> - ~~~~~ - </Text>

  // The default function if no Key is provided is index
  // an identifiable key is important if you plan on
  // item reordering.  Otherwise index is fine
  keyExtractor = (item, index) => `${index}`

  // How many items should be kept im memory as we scroll?
  oneScreensWorth = 20

  // extraData is for anything that is not indicated in data
  // for instance, if you kept "favorites" in `this.state.favs`
  // pass that in, so changes in favorites will cause a re-render
  // and your renderItem will have access to change depending on state
  // e.g. `extraData`={this.state.favs}

  // Optimize your list if the height of each item can be calculated
  // by supplying a constant height, there is no need to measure each
  // item after it renders.  This can save significant time for lists
  // of a size 100+
  // e.g. itemLayout={(data, index) => (
  //   {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  // )}

  render () {
    return (
      <View style={styles.container}>
        <FlatList
          contentContainerStyle={styles.listContent}
          data={this.state.dataObjects}
          renderItem={this.renderRow}
          keyExtractor={this.keyExtractor}
          initialNumToRender={this.oneScreensWorth}
          ListHeaderComponent={this.renderHeader}
          ListFooterComponent={this.renderFooter}
          ListEmptyComponent={this.renderEmpty}
          ItemSeparatorComponent={this.renderSeparator}
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: templates/listview-grid-style.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../Themes'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    backgroundColor: Colors.background
  },
  row: {
    width: 100,
    height: 100,
    justifyContent: 'center',
    alignItems: 'center',
    margin: Metrics.baseMargin,
    backgroundColor: Colors.fire,
    borderRadius: Metrics.smallMargin
  },
  sectionHeader: {
    paddingTop: Metrics.doubleBaseMargin,
    width: Metrics.screenWidth,
    alignSelf: 'center',
    margin: Metrics.baseMargin,
    backgroundColor: Colors.background
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center'
  },
  listContent: {
    justifyContent: 'space-around',
    flexDirection: 'row',
    flexWrap: 'wrap'
  }
})


================================================
FILE: templates/listview-sections.ejs
================================================
import React, { Component } from 'react'
import { View, ListView, Text } from 'react-native'
import { connect } from 'react-redux'

// For empty lists
// import AlertMessage from '../Components/AlertMessage'

// Styles
import styles from './Styles/<%= props.name %>Style'

class <%= props.name %> extends Component {
  constructor (props) {
    super(props)

    /* ***********************************************************
    * STEP 1
    * This is an array of objects with the properties you desire
    * Usually this should come from Redux mapStateToProps
    *************************************************************/
    const dataObjects = {
      first: [
        {title: 'First Title', description: 'First Description'},
        {title: 'Second Title', description: 'Second Description'},
        {title: 'Third Title', description: 'Third Description'},
        {title: 'Fourth Title', description: 'Fourth Description'},
        {title: 'Fifth Title', description: 'Fifth Description'},
        {title: 'Sixth Title', description: 'Sixth Description'},
        {title: 'Seventh Title', description: 'Seventh Description'},
        {title: 'Eighth Title', description: 'Eighth Description'},
        {title: 'Ninth Title', description: 'Ninth Description'},
        {title: 'Tenth Title', description: 'Tenth Description'}
      ],
      second: [
        {title: 'Eleventh Title', description: 'Eleventh Description'},
        {title: '12th Title', description: '12th Description'},
        {title: '13th Title', description: '13th Description'},
        {title: '14th Title', description: '14th Description'},
        {title: '15th Title', description: '15th Description'},
        {title: '16th Title', description: '16th Description'},
        {title: '17th Title', description: '17th Description'},
        {title: '18th Title', description: '18th Description'},
        {title: '19th Title', description: '19th Description'},
        {title: '20th Title', description: '20th Description'},
        {title: 'BLACKJACK!', description: 'BLACKJACK! Description'}
      ]
    }
    /* ***********************************************************
    * STEP 2
    * Teach datasource how to detect if rows are different
    * Make this function fast!  Perhaps something like:
    *   (r1, r2) => r1.id !== r2.id}
    *   The same goes for sectionHeaderHasChanged
    *************************************************************/
    const rowHasChanged = (r1, r2) => r1 !== r2
    const sectionHeaderHasChanged = (s1, s2) => s1 !== s2

    // DataSource configured
    const ds = new ListView.DataSource({rowHasChanged, sectionHeaderHasChanged})

    // Datasource is always in state
    this.state = {
      dataSource: ds.cloneWithRowsAndSections(dataObjects)
    }
  }

  /* ***********************************************************
  * STEP 3
  * `renderRow` function -How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={rowData.title} description={rowData.description} />
  *************************************************************/
  renderRow (rowData, sectionID) {
    // You can condition on sectionID (key as string), for different cells
    // in different sections
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>Section {sectionID} - {rowData.title}</Text>
        <Text style={styles.label}>{rowData.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 4
  * If your datasource is driven by Redux, you'll need to
  * reset it when new data arrives.
  * DO NOT! place `cloneWithRowsAndSections` inside of render, since render
  * is called very often, and should remain fast!  Just replace
  * state's datasource on newProps.
  *
  * e.g.
    componentWillReceiveProps (newProps) {
      if (newProps.someData) {
        this.setState(prevState => ({
          dataSource: prevState.dataSource.cloneWithRowsAndSections(newProps.someData)
        }))
      }
    }
  *************************************************************/

  // Used for friendly AlertMessage
  // returns true if the dataSource is empty
  noRowData () {
    return this.state.dataSource.getRowCount() === 0
  }

  renderHeader (data, sectionID) {
    switch (sectionID) {
      case 'first':
        return <View style={styles.sectionHeader}><Text style={styles.boldLabel}>First Section</Text></View>
      default:
        return <View style={styles.sectionHeader}><Text style={styles.boldLabel}>Second Section</Text></View>
    }
  }

  render () {
    return (
      <View style={styles.container}>
        <ListView
          renderSectionHeader={this.renderHeader}
          contentContainerStyle={styles.listContent}
          dataSource={this.state.dataSource}
          onLayout={this.onLayout}
          renderRow={this.renderRow}
          enableEmptySections
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: templates/listview-style.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../Themes'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center'
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})


================================================
FILE: templates/listview.ejs
================================================
import React, { Component } from 'react'
import { View, Text, ListView } from 'react-native'
import { connect } from 'react-redux'

// For empty lists
// import AlertMessage from '../Components/AlertMessage'

// Styles
import styles from './Styles/<%= props.name %>Style'

class <%= props.name %> extends Component {
  state: {
    dataSource: Object
  }

  constructor (props) {
    super(props)
    /* ***********************************************************
    * STEP 1
    * This is an array of objects with the properties you desire
    * Usually this should come from Redux mapStateToProps
    *************************************************************/
    const dataObjects = [
      {title: 'First Title', description: 'First Description'},
      {title: 'Second Title', description: 'Second Description'},
      {title: 'Third Title', description: 'Third Description'},
      {title: 'Fourth Title', description: 'Fourth Description'},
      {title: 'Fifth Title', description: 'Fifth Description'},
      {title: 'Sixth Title', description: 'Sixth Description'},
      {title: 'Seventh Title', description: 'Seventh Description'}
    ]

    /* ***********************************************************
    * STEP 2
    * Teach datasource how to detect if rows are different
    * Make this function fast!  Perhaps something like:
    *   (r1, r2) => r1.id !== r2.id}
    *************************************************************/
    const rowHasChanged = (r1, r2) => r1 !== r2

    // DataSource configured
    const ds = new ListView.DataSource({rowHasChanged})

    // Datasource is always in state
    this.state = {
      dataSource: ds.cloneWithRows(dataObjects)
    }
  }

  /* ***********************************************************
  * STEP 3
  * `renderRow` function -How each cell/row should be rendered
  * It's our best practice to place a single component here:
  *
  * e.g.
    return <MyCustomCell title={rowData.title} description={rowData.description} />
  *************************************************************/
  renderRow (rowData) {
    return (
      <View style={styles.row}>
        <Text style={styles.boldLabel}>{rowData.title}</Text>
        <Text style={styles.label}>{rowData.description}</Text>
      </View>
    )
  }

  /* ***********************************************************
  * STEP 4
  * If your datasource is driven by Redux, you'll need to
  * reset it when new data arrives.
  * DO NOT! place `cloneWithRows` inside of render, since render
  * is called very often, and should remain fast!  Just replace
  * state's datasource on newProps.
  *
  * e.g.
    componentWillReceiveProps (newProps) {
      if (newProps.someData) {
        this.setState(prevState => ({
          dataSource: prevState.dataSource.cloneWithRows(newProps.someData)
        }))
      }
    }
  *************************************************************/

  // Used for friendly AlertMessage
  // returns true if the dataSource is empty
  noRowData () {
    return this.state.dataSource.getRowCount() === 0
  }

  // Render a footer.
  renderFooter = () => {
    return (
      <Text> - Footer - </Text>
    )
  }

  render () {
    return (
      <View style={styles.container}>
        <ListView
          contentContainerStyle={styles.listContent}
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          renderFooter={this.renderFooter}
          enableEmptySections
          pageSize={15}
        />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: templates/redux-test-ava.ejs
================================================
import test from 'ava'
import Actions, { reducer, INITIAL_STATE } from '../../App/Redux/<%= props.name %>Redux'

test('attempt', t => {
  const state = reducer(INITIAL_STATE, Actions.<%= camelCase(props.name) %>Request('data'))

  t.true(state.fetching)
})

test('success', t => {
  const state = reducer(INITIAL_STATE, Actions.<%= camelCase(props.name) %>Success('hi'))

  t.is(state.payload, 'hi')
})

test('failure', t => {
  const state = reducer(INITIAL_STATE, Actions.<%= camelCase(props.name) %>Failure(99))

  t.false(state.fetching)
  t.true(state.error)
})


================================================
FILE: templates/redux-test-jest.ejs
================================================
import Actions, { reducer, INITIAL_STATE } from '../../App/Redux/<%= props.name %>Redux'

it('attempt', () => {
  const state = reducer(INITIAL_STATE, Actions.<%= camelCase(props.name) %>Request('data'))

  expect(state.fetching).toBe(true)
})

it('success', () => {
  const state = reducer(INITIAL_STATE, Actions.<%= camelCase(props.name) %>Success('hi'))

  expect(state.payload).toBe('hi')
})

it('failure', () => {
  const state = reducer(INITIAL_STATE, Actions.<%= camelCase(props.name) %>Failure())

  expect(state.fetching).toBe(false)
  expect(state.error).toBe(true)
})


================================================
FILE: templates/redux.ejs
================================================
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  <%= camelCase(props.name) %>Request: ['data'],
  <%= camelCase(props.name) %>Success: ['payload'],
  <%= camelCase(props.name) %>Failure: null
})

export const <%= props.name %>Types = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  data: null,
  fetching: null,
  payload: null,
  error: null
})

/* ------------- Selectors ------------- */

export const <%= props.name %>Selectors = {
  getData: state => state.data
}

/* ------------- Reducers ------------- */

// request the data from an api
export const request = (state, { data }) =>
  state.merge({ fetching: true, data, payload: null })

// successful api lookup
export const success = (state, action) => {
  const { payload } = action
  return state.merge({ fetching: false, error: null, payload })
}

// Something went wrong somewhere.
export const failure = state =>
  state.merge({ fetching: false, error: true, payload: null })

/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.<%= snakeCase(props.name).toUpperCase() %>_REQUEST]: request,
  [Types.<%= snakeCase(props.name).toUpperCase() %>_SUCCESS]: success,
  [Types.<%= snakeCase(props.name).toUpperCase() %>_FAILURE]: failure
})


================================================
FILE: templates/saga-test-ava.ejs
================================================
/* ***********************************************************
* Wiring Instructions
* To make this test work, you'll need to:
*  - Add a Fixture named get<%= props.name %> to the
*    ./App/Services/FixtureApi file. You can just keep adding
*    functions to that file.
*************************************************************/

import test from 'ava'
import FixtureAPI from '../../App/Services/FixtureApi'
import { call, put } from 'redux-saga/effects'
import { get<%= props.name %> } from '../../App/Sagas/<%= props.name %>Sagas'
import <%= props.name %>Actions from '../../App/Redux/<%= props.name %>Redux'

const stepper = (fn) => (mock) => fn.next(mock).value

test('first calls API', t => {
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // first yield is the API
  t.deepEqual(step(), call(FixtureAPI.get<%= props.name %>, 'taco'))
})

test('success path', t => {
  const response = FixtureAPI.get<%= props.name %>('taco')
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // Step 1: hit the api
  step()
  // Second step successful return and data!
  t.deepEqual(step(response), put(<%= pascalCase(props.name) %>Actions.<%= camelCase(props.name) %>Success(21)))
})

test('failure path', t => {
  const response = {ok: false}
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // Step 1: hit the api
  step()
  // Second step failed response
  t.deepEqual(step(response), put(<%= pascalCase(props.name) %>Actions.<%= camelCase(props.name) %>Failure()))
})


================================================
FILE: templates/saga-test-jest.ejs
================================================
/* ***********************************************************
* Wiring Instructions
* To make this test work, you'll need to:
*  - Add a Fixture named get<%= props.name %> to the
*    ./App/Services/FixtureApi file. You can just keep adding
*    functions to that file.
*************************************************************/

import FixtureAPI from '../../App/Services/FixtureApi'
import { call, put } from 'redux-saga/effects'
import { get<%= props.name %> } from '../../App/Sagas/<%= props.name %>Sagas'
import <%= props.name %>Actions from '../../App/Redux/<%= props.name %>Redux'

const stepper = (fn) => (mock) => fn.next(mock).value

it('first calls API', () => {
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // first yield is the API
  expect(step()).toEqual(call(FixtureAPI.get<%= props.name %>, 'taco'))
})

it('success path', () => {
  const response = FixtureAPI.get<%= props.name %>('taco')
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // Step 1: Hit the api
  step()
  // Step 2: Successful return and data!
  expect(step(response)).toEqual(put(<%= pascalCase(props.name) %>Actions.<%= camelCase(props.name) %>Success(21)))
})

it('failure path', () => {
  const response = {ok: false}
  const step = stepper(get<%= props.name %>(FixtureAPI, {data: 'taco'}))
  // Step 1: Hit the api
  step()
  // Step 2: Failed response.
  expect(step(response)).toEqual(put(<%= pascalCase(props.name) %>Actions.<%= camelCase(props.name) %>Failure()))
})


================================================
FILE: templates/saga.ejs
================================================
/* ***********************************************************
* A short word on how to use this automagically generated file.
* We're often asked in the Infinite Red Slack channel how to connect
* to a to a third party api, so we thought we'd demonstrate - but
* you should know you can use sagas for other flow control too.
*
* Other points:
*  - You'll need to add this saga to sagas/index.js
*  - This template uses the api declared in sagas/index.js, so
*    you'll need to define a constant in that file.
*************************************************************/

import { call, put } from 'redux-saga/effects'
import <%= props.name %>Actions from '../Redux/<%= props.name %>Redux'
// import { <%= props.name %>Selectors } from '../Redux/<%= props.name %>Redux'

export function * get<%= props.name %> (api, action) {
  const { data } = action
  // get current data from Store
  // const currentData = yield select(<%= props.name %>Selectors.getData)
  // make the call to the api
  const response = yield call(api.get<%= camelCase(props.name) %>, data)

  // success?
  if (response.ok) {
    // You might need to change the response here - do this with a 'transform',
    // located in ../Transforms/. Otherwise, just pass the data back from the api.
    yield put(<%= props.name %>Actions.<%= camelCase(props.name) %>Success(response.data))
  } else {
    yield put(<%= props.name %>Actions.<%= camelCase(props.name) %>Failure())
  }
}


================================================
FILE: templates/screen-style.ejs
================================================
import { StyleSheet } from 'react-native'
import { ApplicationStyles } from '../../Themes/'

export default StyleSheet.create({
  ...ApplicationStyles.screen
})


================================================
FILE: templates/screen.ejs
================================================
import React, { Component } from 'react'
import { ScrollView, Text, KeyboardAvoidingView } from 'react-native'
import { connect } from 'react-redux'
// Add Actions - replace 'Your' with whatever your reducer is called :)
// import YourActions from '../Redux/YourRedux'

// Styles
import styles from './Styles/<%= props.name %>Style'

class <%= props.name %> extends Component {
  render () {
    return (
      <ScrollView style={styles.container}>
        <KeyboardAvoidingView behavior='position'>
          <Text><%= props.name %></Text>
        </KeyboardAvoidingView>
      </ScrollView>
    )
  }
}

const mapStateToProps = (state) => {
  return {
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(<%= props.name %>)


================================================
FILE: test/generators-integration.test.js
================================================
const execa = require('execa')
const jetpack = require('fs-jetpack')
const tempy = require('tempy')

const IGNITE = 'npx ignite-cli'
const APP = 'IntegrationTest'
const BOILERPLATE = jetpack.path(__dirname, '..')

// calling the ignite cli takes a while
jasmine.DEFAULT_TIMEOUT_INTERVAL = 600000

const exopts = {
  preferLocal: false,
  shell: true
}

describe('generators', () => {
  beforeAll(async () => {
    // creates a new temp directory
    process.chdir(tempy.directory())
    await execa(IGNITE, ['new', APP, '--min', '--skip-git', '--boilerplate', BOILERPLATE], exopts)
    process.chdir(APP)
    await execa('npm', ['run', 'fixcode'], exopts)
  })

  test('generates a component', async () => {
    const simpleComponent = 'Simple'
    await execa(IGNITE, ['g', 'component', simpleComponent], exopts)
    expect(jetpack.exists(`App/Components/${simpleComponent}.js`)).toBe('file')
    expect(jetpack.exists(`App/Components/Styles/${simpleComponent}Style.js`)).toBe('file')
    const lint = await execa('npm', ['-s', 'run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generates a folder component', async () => {
    const folderComponent = 'Folder'
    await execa(IGNITE, ['g', 'component', '--folder', folderComponent], exopts)
    expect(jetpack.exists(`App/Components/${folderComponent}/index.js`)).toBe('file')
    expect(jetpack.exists(`App/Components/${folderComponent}/Styles/indexStyle.js`)).toBe('file')
    const lint = await execa('npm', ['-s', 'run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generates a component inside a folder', async () => {
    const componentName = 'InFolder'
    const folderName = 'Folder'
    await execa(IGNITE, ['g', 'component', '--folder', folderName, componentName], exopts)
    expect(jetpack.exists(`App/Components/${folderName}/${componentName}.js`)).toBe('file')
    expect(jetpack.exists(`App/Components/${folderName}/Styles/${componentName}Style.js`)).toBe('file')
    const lint = await execa('npm', ['-s', 'run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generates a component in a relative path', async () => {
    await execa(IGNITE, ['g', 'component', 'My/SubFolder/Test'], exopts)
    expect(jetpack.exists('App/Components/My/SubFolder/Test.js')).toBe('file')
    expect(jetpack.exists('App/Components/My/SubFolder/Styles/TestStyle.js')).toBe('file')
    const lint = await execa('npm', ['-s', 'run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate listview of type row works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestRow', '--type=Row', '--codeType=listview', '--dataType=Single'], exopts)
    expect(jetpack.exists('App/Containers/TestRow.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestRowStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate flatlist of type row works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestFlatRow', '--type=Row', '--codeType=flatlist', '--dataType=Single'], exopts)
    expect(jetpack.exists('App/Containers/TestFlatRow.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestFlatRowStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate listview of sections works', async () => {
    await execa(
      IGNITE,
      ['g', 'list', 'TestSection', '--type=Row', '--codeType=listview', '--dataType=Sectioned'],
      exopts
    )
    expect(jetpack.exists('App/Containers/TestSection.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestSectionStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate flatlist of sections works', async () => {
    await execa(
      IGNITE,
      ['g', 'list', 'TestFlatSection', '--type=Row', '--codeType=flatlist', '--dataType=Sectioned'],
      exopts
    )
    expect(jetpack.exists('App/Containers/TestFlatSection.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestFlatSectionStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate listview of type grid works', async () => {
    await execa(IGNITE, ['g', 'list', 'TestGrid', '--type=Grid', '--codeType=listview', '--dataType=Single'], exopts)
    expect(jetpack.exists('App/Containers/TestGrid.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestGridStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate redux works', async () => {
    await execa(IGNITE, ['g', 'redux', 'Test'], exopts)
    expect(jetpack.exists('App/Redux/TestRedux.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate container works', async () => {
    await execa(IGNITE, ['g', 'container', 'Container'], exopts)
    expect(jetpack.exists('App/Containers/Container.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/ContainerStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate saga works', async () => {
    await execa(IGNITE, ['g', 'saga', 'Test'], exopts)
    expect(jetpack.exists('App/Sagas/TestSagas.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })

  test('generate screen works', async () => {
    await execa(IGNITE, ['g', 'screen', 'Test'], exopts)
    expect(jetpack.exists('App/Containers/TestScreen.js')).toBe('file')
    expect(jetpack.exists('App/Containers/Styles/TestScreenStyle.js')).toBe('file')
    const lint = await execa('npm', ['run', 'lint', '--loglevel=error'], exopts)
    expect(lint.stderr).toBe('')
  })
})


================================================
FILE: test/interface.test.js
================================================
const boilerplate = require('../boilerplate')
const plugin = require('../plugin')

test('boilerplate interface', async () => {
  expect(typeof boilerplate.install).toBe('function')
})

test('plugin interface', async () => {
  expect(typeof plugin.add).toBe('function')
  expect(typeof plugin.remove).toBe('function')
})


================================================
FILE: test/react-native-version.test.js
================================================
const boilerplate = require('../lib/react-native-version')

// grab a few things from the boilerplate module
const get = boilerplate.getReactNativeVersion
const DEFAULT = boilerplate.REACT_NATIVE_VERSION

/**
 * Runs with a valid gluegun context and a staged version number.
 *
 * @param {*} reactNativeVersion The React Native version to use.
 * @return {string} The version number we should be using.
 */
const mock = reactNativeVersion => get({
  parameters: {
    options: {
      'react-native-version': reactNativeVersion
    }
  }
})

// this would only happen if we screwed something up in our boilerplate.js
test('it handles strange inputs from code', () => {
  expect(get()).toBe(DEFAULT)
  expect(get(null)).toBe(DEFAULT)
  expect(get(true)).toBe(DEFAULT)
  expect(get(8)).toBe(DEFAULT)
  expect(get('hello')).toBe(DEFAULT)
  expect(get([])).toBe(DEFAULT)
  expect(get({})).toBe(DEFAULT)
  expect(get(() => true)).toBe(DEFAULT)
})

// this could happen because it's valid input via minimist from the user
test('it handles strange input from the user', () => {
  expect(mock(true)).toBe(DEFAULT)
  expect(mock(false)).toBe(DEFAULT)
  expect(mock([])).toBe(DEFAULT)
  expect(mock({})).toBe(DEFAULT)
})

// very edge-casey
test('it handles not-quite semver numbers', () => {
  expect(mock(0)).toBe(DEFAULT)
  expect(mock(0.25)).toBe(DEFAULT)
})

// happy path
test('it handles valid versions', () => {
  expect(mock('0.41.0')).toBe('0.41.0')
  expect(mock('0.41.0-beta.1')).toBe('0.41.0-beta.1')
  expect(mock(DEFAULT)).toBe(DEFAULT)
  expect(mock('next')).toBe('next')
})
Download .txt
gitextract_lzwitwv2/

├── .circleci/
│   └── config.yml
├── .gitignore
├── LICENSE
├── boilerplate/
│   ├── .babelrc
│   ├── .editorconfig
│   ├── App/
│   │   ├── Components/
│   │   │   ├── AlertMessage.js
│   │   │   ├── AlertMessage.story.js
│   │   │   ├── DrawerButton.js
│   │   │   ├── DrawerButton.story.js
│   │   │   ├── FullButton.js
│   │   │   ├── FullButton.story.js
│   │   │   ├── README.md
│   │   │   ├── RoundedButton.js
│   │   │   ├── RoundedButton.story.js
│   │   │   ├── Stories.js
│   │   │   └── Styles/
│   │   │       ├── AlertMessageStyles.js
│   │   │       ├── DrawerButtonStyles.js
│   │   │       ├── FullButtonStyles.js
│   │   │       ├── README.md
│   │   │       └── RoundedButtonStyles.js
│   │   ├── Config/
│   │   │   ├── AppConfig.js
│   │   │   ├── DebugConfig.js
│   │   │   ├── README.md
│   │   │   ├── ReactotronConfig.js
│   │   │   └── index.js
│   │   ├── Containers/
│   │   │   ├── App.js
│   │   │   ├── LaunchScreen.js
│   │   │   ├── README.md
│   │   │   ├── RootContainer.js
│   │   │   └── Styles/
│   │   │       ├── LaunchScreenStyles.js
│   │   │       ├── README.md
│   │   │       └── RootContainerStyles.js
│   │   ├── Fixtures/
│   │   │   ├── README.md
│   │   │   ├── gantman.json
│   │   │   ├── rateLimit.json
│   │   │   ├── root.json
│   │   │   └── skellock.json
│   │   ├── Images/
│   │   │   └── README.md
│   │   ├── Lib/
│   │   │   └── README.md
│   │   ├── Navigation/
│   │   │   ├── AppNavigation.js
│   │   │   ├── ReduxNavigation.js
│   │   │   └── Styles/
│   │   │       └── NavigationStyles.js
│   │   ├── Redux/
│   │   │   ├── CreateStore.js
│   │   │   ├── GithubRedux.js
│   │   │   ├── NavigationRedux.js
│   │   │   ├── ScreenTrackingMiddleware.js
│   │   │   ├── SearchRedux.js
│   │   │   ├── StartupRedux.js
│   │   │   └── index.js
│   │   ├── Sagas/
│   │   │   ├── GithubSagas.js
│   │   │   ├── StartupSagas.js
│   │   │   └── index.js
│   │   ├── Services/
│   │   │   ├── Api.js
│   │   │   ├── ExamplesRegistry.js
│   │   │   └── FixtureApi.js
│   │   ├── Themes/
│   │   │   ├── ApplicationStyles.js
│   │   │   ├── Colors.js
│   │   │   ├── Fonts.js
│   │   │   ├── Images.js
│   │   │   ├── Metrics.js
│   │   │   ├── README.md
│   │   │   └── index.js
│   │   └── Transforms/
│   │       ├── ConvertFromKelvin.js
│   │       └── README.md
│   ├── README.md
│   ├── Tests/
│   │   ├── Components/
│   │   │   ├── AlertMessageTest.js
│   │   │   ├── DrawerButtonTest.js
│   │   │   ├── FullButtonTest.js
│   │   │   └── RoundedButtonTest.js
│   │   ├── Redux/
│   │   │   └── GithubReduxTest.js
│   │   ├── Sagas/
│   │   │   ├── GithubSagaTest.js
│   │   │   └── StartupSagaTest.js
│   │   ├── Services/
│   │   │   └── FixtureAPITest.js
│   │   ├── Setup.js.ejs
│   │   └── StoriesTest.js
│   ├── ignite.json.ejs
│   ├── index.js.ejs
│   ├── package.json.ejs
│   └── storybook/
│       ├── addons.js
│       ├── index.js
│       └── storybook.ejs
├── boilerplate.js
├── commands/
│   └── generate/
│       ├── component.js
│       ├── container.js
│       ├── generate.js
│       ├── list.js
│       ├── redux.js
│       ├── saga.js
│       └── screen.js
├── ignite.json
├── lib/
│   ├── patterns.js
│   └── react-native-version.js
├── options.js
├── package.json
├── plugin.js
├── readme.md
├── screenExamples.js
├── templates/
│   ├── component-style.ejs
│   ├── component-test.ejs
│   ├── component.ejs
│   ├── container-style.ejs
│   ├── container.ejs
│   ├── examples/
│   │   ├── GridExample.js.ejs
│   │   ├── RowExample.js.ejs
│   │   ├── SectionExample.js.ejs
│   │   └── Styles/
│   │       ├── GridExampleStyle.js.ejs
│   │       ├── RowExampleStyle.js.ejs
│   │       └── SectionExampleStyle.js.ejs
│   ├── flatlist-grid-style.ejs
│   ├── flatlist-grid.ejs
│   ├── flatlist-sections.ejs
│   ├── flatlist.ejs
│   ├── listview-grid-style.ejs
│   ├── listview-sections.ejs
│   ├── listview-style.ejs
│   ├── listview.ejs
│   ├── redux-test-ava.ejs
│   ├── redux-test-jest.ejs
│   ├── redux.ejs
│   ├── saga-test-ava.ejs
│   ├── saga-test-jest.ejs
│   ├── saga.ejs
│   ├── screen-style.ejs
│   └── screen.ejs
└── test/
    ├── generators-integration.test.js
    ├── interface.test.js
    └── react-native-version.test.js
Download .txt
SYMBOL INDEX (33 symbols across 16 files)

FILE: boilerplate.js
  function install (line 26) | async function install(context) {

FILE: boilerplate/App/Components/AlertMessage.js
  class AlertMessage (line 6) | class AlertMessage extends Component {
    method render (line 16) | render () {

FILE: boilerplate/App/Components/DrawerButton.js
  class DrawerButton (line 20) | class DrawerButton extends Component {
    method render (line 26) | render () {

FILE: boilerplate/App/Components/FullButton.js
  class FullButton (line 20) | class FullButton extends Component {
    method render (line 27) | render () {

FILE: boilerplate/App/Components/RoundedButton.js
  class RoundedButton (line 20) | class RoundedButton extends Component {
    method getText (line 28) | getText () {
    method render (line 33) | render () {

FILE: boilerplate/App/Containers/App.js
  class App (line 20) | class App extends Component {
    method render (line 21) | render () {

FILE: boilerplate/App/Containers/LaunchScreen.js
  class LaunchScreen (line 8) | class LaunchScreen extends Component {
    method render (line 9) | render () {

FILE: boilerplate/App/Containers/RootContainer.js
  class RootContainer (line 10) | class RootContainer extends Component {
    method componentDidMount (line 11) | componentDidMount () {
    method render (line 15) | render () {

FILE: boilerplate/App/Navigation/ReduxNavigation.js
  class ReduxNavigation (line 17) | class ReduxNavigation extends React.Component {
    method componentDidMount (line 18) | componentDidMount () {
    method componentWillUnmount (line 32) | componentWillUnmount () {
    method render (line 37) | render () {

FILE: boilerplate/App/Redux/GithubRedux.js
  constant INITIAL_STATE (line 17) | const INITIAL_STATE = Immutable({

FILE: boilerplate/App/Redux/SearchRedux.js
  constant LIST_DATA (line 6) | const LIST_DATA = ['sausage', 'blubber', 'pencil', 'cloud', 'moon', 'wat...
  constant INITIAL_STATE (line 30) | const INITIAL_STATE = Immutable({

FILE: lib/react-native-version.js
  constant REACT_NATIVE_VERSION (line 4) | const REACT_NATIVE_VERSION = '0.63.0'

FILE: plugin.js
  function add (line 8) | async function add (context) {
  function remove (line 17) | async function remove (context) {

FILE: screenExamples.js
  function add (line 24) | async function add (context) {
  function remove (line 34) | async function remove (context) {

FILE: test/generators-integration.test.js
  constant IGNITE (line 5) | const IGNITE = 'npx ignite-cli'
  constant APP (line 6) | const APP = 'IntegrationTest'
  constant BOILERPLATE (line 7) | const BOILERPLATE = jetpack.path(__dirname, '..')

FILE: test/react-native-version.test.js
  constant DEFAULT (line 5) | const DEFAULT = boilerplate.REACT_NATIVE_VERSION
Condensed preview — 127 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (176K chars).
[
  {
    "path": ".circleci/config.yml",
    "chars": 2089,
    "preview": "# Javascript Node CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-javascript/ for more "
  },
  {
    "path": ".gitignore",
    "chars": 280,
    "preview": ".DS_Store\nnpm-debug.log\nnpm-debug.log*\ncoverage\n.nyc_output\nlerna-debug.log\nnode_modules\n.vscode/*\n!.vscode/settings.jso"
  },
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "MIT License\n\nCopyright (c) 2016 Infinite Red, Inc.\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "boilerplate/.babelrc",
    "chars": 148,
    "preview": "{\n  \"presets\": [\"module:metro-react-native-babel-preset\"],\n  \"env\": {\n    \"production\": {\n      \"plugins\": [\"ignite-igno"
  },
  {
    "path": "boilerplate/.editorconfig",
    "chars": 311,
    "preview": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with"
  },
  {
    "path": "boilerplate/App/Components/AlertMessage.js",
    "chars": 839,
    "preview": "import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { View, Text } from 'react-native'\nim"
  },
  {
    "path": "boilerplate/App/Components/AlertMessage.story.js",
    "chars": 458,
    "preview": "import React from 'react'\nimport { storiesOf } from '@storybook/react-native'\n\nimport AlertMessage from './AlertMessage'"
  },
  {
    "path": "boilerplate/App/Components/DrawerButton.js",
    "chars": 994,
    "preview": "import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { Text, TouchableOpacity } from 'reac"
  },
  {
    "path": "boilerplate/App/Components/DrawerButton.story.js",
    "chars": 361,
    "preview": "import React from 'react'\nimport { View } from 'react-native'\nimport { storiesOf } from '@storybook/react-native'\n\nimpor"
  },
  {
    "path": "boilerplate/App/Components/FullButton.js",
    "chars": 1061,
    "preview": "import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { TouchableOpacity, Text } from 'reac"
  },
  {
    "path": "boilerplate/App/Components/FullButton.story.js",
    "chars": 353,
    "preview": "import React from 'react'\nimport { storiesOf } from '@storybook/react-native'\n\nimport FullButton from './FullButton'\n\nst"
  },
  {
    "path": "boilerplate/App/Components/README.md",
    "chars": 67,
    "preview": "### Components Folder\nAll components are stored and organized here\n"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton.js",
    "chars": 1198,
    "preview": "import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { TouchableOpacity, Text } from 'reac"
  },
  {
    "path": "boilerplate/App/Components/RoundedButton.story.js",
    "chars": 359,
    "preview": "import React from 'react'\nimport { storiesOf } from '@storybook/react-native'\n\nimport RoundedButton from './RoundedButto"
  },
  {
    "path": "boilerplate/App/Components/Stories.js",
    "chars": 119,
    "preview": "import './AlertMessage.story'\nimport './DrawerButton.story'\nimport './FullButton.story'\nimport './RoundedButton.story'\n"
  },
  {
    "path": "boilerplate/App/Components/Styles/AlertMessageStyles.js",
    "chars": 572,
    "preview": "import { StyleSheet } from 'react-native'\nimport { Colors, Metrics, Fonts } from '../../Themes/'\n\nexport default StyleSh"
  },
  {
    "path": "boilerplate/App/Components/Styles/DrawerButtonStyles.js",
    "chars": 174,
    "preview": "import { Metrics, Colors, Fonts } from '../../Themes'\n\nexport default {\n  text: {\n    ...Fonts.style.h5,\n    color: Colo"
  },
  {
    "path": "boilerplate/App/Components/Styles/FullButtonStyles.js",
    "chars": 476,
    "preview": "import { StyleSheet } from 'react-native'\nimport { Fonts, Colors } from '../../Themes/'\n\nexport default StyleSheet.creat"
  },
  {
    "path": "boilerplate/App/Components/Styles/README.md",
    "chars": 69,
    "preview": "### Styles Folder\nComponent styles are separated from functionality.\n"
  },
  {
    "path": "boilerplate/App/Components/Styles/RoundedButtonStyles.js",
    "chars": 497,
    "preview": "import { StyleSheet } from 'react-native'\nimport { Fonts, Colors, Metrics } from '../../Themes/'\n\nexport default StyleSh"
  },
  {
    "path": "boilerplate/App/Config/AppConfig.js",
    "chars": 135,
    "preview": "// Simple React Native specific changes\n\nexport default {\n  // font scaling override - RN default is on\n  allowTextFontS"
  },
  {
    "path": "boilerplate/App/Config/DebugConfig.js",
    "chars": 159,
    "preview": "export default {\n  useFixtures: false,\n  ezLogin: false,\n  yellowBox: __DEV__,\n  reduxLogging: __DEV__,\n  includeExample"
  },
  {
    "path": "boilerplate/App/Config/README.md",
    "chars": 256,
    "preview": "### Config Folder\nAll application specific configuration falls in this folder.\n\n`AppConfig.js` - production values.\n`Deb"
  },
  {
    "path": "boilerplate/App/Config/ReactotronConfig.js",
    "chars": 810,
    "preview": "import Config from '../Config/DebugConfig'\nimport Immutable from 'seamless-immutable'\nimport Reactotron from 'reactotron"
  },
  {
    "path": "boilerplate/App/Config/index.js",
    "chars": 331,
    "preview": "import DebugConfig from './DebugConfig'\nimport AppConfig from './AppConfig' // eslint-disable-line no-unused-vars\n\nif (_"
  },
  {
    "path": "boilerplate/App/Containers/App.js",
    "chars": 860,
    "preview": "import '../Config'\nimport DebugConfig from '../Config/DebugConfig'\nimport React, { Component } from 'react'\nimport { Pro"
  },
  {
    "path": "boilerplate/App/Containers/LaunchScreen.js",
    "chars": 1076,
    "preview": "import React, { Component } from 'react'\nimport { ScrollView, Text, Image, View } from 'react-native'\nimport { Images } "
  },
  {
    "path": "boilerplate/App/Containers/README.md",
    "chars": 991,
    "preview": "### Containers Folder\nA container is what they call a \"Smart Component\" in Redux.  It is a component\nwhich knows about R"
  },
  {
    "path": "boilerplate/App/Containers/RootContainer.js",
    "chars": 804,
    "preview": "import React, { Component } from 'react'\nimport { View, StatusBar } from 'react-native'\nimport ReduxNavigation from '../"
  },
  {
    "path": "boilerplate/App/Containers/Styles/LaunchScreenStyles.js",
    "chars": 416,
    "preview": "import { StyleSheet } from 'react-native'\nimport { Metrics, ApplicationStyles } from '../../Themes/'\n\nexport default Sty"
  },
  {
    "path": "boilerplate/App/Containers/Styles/README.md",
    "chars": 69,
    "preview": "### Styles Folder\nContainer styles are separated from functionality.\n"
  },
  {
    "path": "boilerplate/App/Containers/Styles/RootContainerStyles.js",
    "chars": 471,
    "preview": "import {StyleSheet} from 'react-native'\nimport {Fonts, Metrics, Colors} from '../../Themes/'\n\nexport default StyleSheet."
  },
  {
    "path": "boilerplate/App/Fixtures/README.md",
    "chars": 283,
    "preview": "### Fixtures folder\nAll key API responses are housed here.\n\nThese API responses can be used for several reasons.  _E.G._"
  },
  {
    "path": "boilerplate/App/Fixtures/gantman.json",
    "chars": 7470,
    "preview": "{\n  \"total_count\": 7,\n  \"incomplete_results\": false,\n  \"items\": [\n    {\n      \"login\": \"GantMan\",\n      \"id\": 997157,\n  "
  },
  {
    "path": "boilerplate/App/Fixtures/rateLimit.json",
    "chars": 281,
    "preview": "{\n  \"resources\": {\n    \"core\": {\n      \"limit\": 60,\n      \"remaining\": 42,\n      \"reset\": 1488126913\n    },\n    \"search\""
  },
  {
    "path": "boilerplate/App/Fixtures/root.json",
    "chars": 2164,
    "preview": "{\n  \"current_user_url\": \"https://api.github.com/user\",\n  \"current_user_authorizations_html_url\": \"https://github.com/set"
  },
  {
    "path": "boilerplate/App/Fixtures/skellock.json",
    "chars": 1111,
    "preview": "{\n  \"total_count\": 1,\n  \"incomplete_results\": false,\n  \"items\": [\n    {\n      \"login\": \"skellock\",\n      \"id\": 68273,\n  "
  },
  {
    "path": "boilerplate/App/Images/README.md",
    "chars": 56,
    "preview": "### Images folder\nHolds all images for the applications."
  },
  {
    "path": "boilerplate/App/Lib/README.md",
    "chars": 455,
    "preview": "# Lib\n\nAt first glance, this could appear to be a \"miscellaneous\" folder, but we recommend that you treat this as provin"
  },
  {
    "path": "boilerplate/App/Navigation/AppNavigation.js",
    "chars": 542,
    "preview": "import { createAppContainer } from 'react-navigation'\nimport { createStackNavigator } from 'react-navigation-stack';\nimp"
  },
  {
    "path": "boilerplate/App/Navigation/ReduxNavigation.js",
    "chars": 1365,
    "preview": "import * as React from 'react'\nimport { BackHandler, Platform } from 'react-native'\nimport {\n  createReactNavigationRedu"
  },
  {
    "path": "boilerplate/App/Navigation/Styles/NavigationStyles.js",
    "chars": 180,
    "preview": "import { StyleSheet } from 'react-native'\nimport { Colors } from '../../Themes/'\n\nexport default StyleSheet.create({\n  h"
  },
  {
    "path": "boilerplate/App/Redux/CreateStore.js",
    "chars": 1474,
    "preview": "import { createStore, applyMiddleware, compose } from 'redux'\nimport Config from '../Config/DebugConfig'\nimport createSa"
  },
  {
    "path": "boilerplate/App/Redux/GithubRedux.js",
    "chars": 1321,
    "preview": "import { createReducer, createActions } from 'reduxsauce'\nimport Immutable from 'seamless-immutable'\n\n/* ------------- T"
  },
  {
    "path": "boilerplate/App/Redux/NavigationRedux.js",
    "chars": 203,
    "preview": "import AppNavigation from '../Navigation/AppNavigation'\n\nexport const reducer = (state, action) => {\n  const newState = "
  },
  {
    "path": "boilerplate/App/Redux/ScreenTrackingMiddleware.js",
    "chars": 1063,
    "preview": "import { NavigationActions } from 'react-navigation'\n\n// gets the current screen from navigation state\nconst getCurrentR"
  },
  {
    "path": "boilerplate/App/Redux/SearchRedux.js",
    "chars": 2047,
    "preview": "import { createReducer, createActions } from 'reduxsauce'\nimport Immutable from 'seamless-immutable'\nimport { filter } f"
  },
  {
    "path": "boilerplate/App/Redux/StartupRedux.js",
    "chars": 227,
    "preview": "import { createActions } from 'reduxsauce'\n\n/* ------------- Types and Action Creators ------------- */\n\nconst { Types, "
  },
  {
    "path": "boilerplate/App/Redux/index.js",
    "chars": 822,
    "preview": "import { combineReducers } from 'redux'\nimport configureStore from './CreateStore'\nimport rootSaga from '../Sagas/'\n\n/* "
  },
  {
    "path": "boilerplate/App/Sagas/GithubSagas.js",
    "chars": 558,
    "preview": "import { call, put } from 'redux-saga/effects'\nimport { path } from 'ramda'\nimport GithubActions from '../Redux/GithubRe"
  },
  {
    "path": "boilerplate/App/Sagas/StartupSagas.js",
    "chars": 1239,
    "preview": "import { put, select } from 'redux-saga/effects'\nimport GithubActions, { GithubSelectors } from '../Redux/GithubRedux'\ni"
  },
  {
    "path": "boilerplate/App/Sagas/index.js",
    "chars": 1006,
    "preview": "import { takeLatest, all } from 'redux-saga/effects'\nimport API from '../Services/Api'\nimport FixtureAPI from '../Servic"
  },
  {
    "path": "boilerplate/App/Services/Api.js",
    "chars": 1715,
    "preview": "// a library to wrap and simplify api calls\nimport apisauce from 'apisauce'\n\n// our \"constructor\"\nconst create = (baseUR"
  },
  {
    "path": "boilerplate/App/Services/ExamplesRegistry.js",
    "chars": 1521,
    "preview": "import React from 'react'\nimport { Text, View } from 'react-native'\nimport R from 'ramda'\nimport { ApplicationStyles } f"
  },
  {
    "path": "boilerplate/App/Services/FixtureApi.js",
    "chars": 596,
    "preview": "export default {\n  // Functions return fixtures\n  getRoot: () => {\n    return {\n      ok: true,\n      data: require('../"
  },
  {
    "path": "boilerplate/App/Themes/ApplicationStyles.js",
    "chars": 1900,
    "preview": "import Fonts from './Fonts'\nimport Metrics from './Metrics'\nimport Colors from './Colors'\n\n// This file is for a reusabl"
  },
  {
    "path": "boilerplate/App/Themes/Colors.js",
    "chars": 633,
    "preview": "const colors = {\n  background: '#1F0808',\n  clear: 'rgba(0,0,0,0)',\n  facebook: '#3b5998',\n  transparent: 'rgba(0,0,0,0)"
  },
  {
    "path": "boilerplate/App/Themes/Fonts.js",
    "chars": 829,
    "preview": "const type = {\n  base: 'Avenir-Book',\n  bold: 'Avenir-Black',\n  emphasis: 'HelveticaNeue-Italic'\n}\n\nconst size = {\n  h1:"
  },
  {
    "path": "boilerplate/App/Themes/Images.js",
    "chars": 1137,
    "preview": "// leave off @2x/@3x\nconst images = {\n  logo: require('../Images/ir.png'),\n  clearLogo: require('../Images/top_logo.png'"
  },
  {
    "path": "boilerplate/App/Themes/Metrics.js",
    "chars": 677,
    "preview": "import {Dimensions, Platform} from 'react-native'\n\nconst { width, height } = Dimensions.get('window')\n\n// Used via Metri"
  },
  {
    "path": "boilerplate/App/Themes/README.md",
    "chars": 93,
    "preview": "### Themes Folder\nApplication specific themes\n* Base Styles\n* Fonts\n* Metrics\n* Colors\n\netc.\n"
  },
  {
    "path": "boilerplate/App/Themes/index.js",
    "chars": 234,
    "preview": "import Colors from './Colors'\nimport Fonts from './Fonts'\nimport Metrics from './Metrics'\nimport Images from './Images'\n"
  },
  {
    "path": "boilerplate/App/Transforms/ConvertFromKelvin.js",
    "chars": 151,
    "preview": "export default (kelvin: number) => {\n  const celsius = kelvin - 273.15\n  const fahrenheit = (celsius * 1.8000) + 32\n\n  r"
  },
  {
    "path": "boilerplate/App/Transforms/README.md",
    "chars": 550,
    "preview": "# Transforms\n\nA common pattern when working with APIs is to change data to play nice between your app & the API.  \n\nWe'v"
  },
  {
    "path": "boilerplate/README.md",
    "chars": 2346,
    "preview": "#  <%= props.name %>\n[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat"
  },
  {
    "path": "boilerplate/Tests/Components/AlertMessageTest.js",
    "chars": 758,
    "preview": "import 'react-native'\nimport React from 'react'\nimport AlertMessage from '../../App/Components/AlertMessage'\nimport rend"
  },
  {
    "path": "boilerplate/Tests/Components/DrawerButtonTest.js",
    "chars": 664,
    "preview": "import 'react-native'\nimport React from 'react'\nimport DrawerButton from '../../App/Components/DrawerButton'\nimport { sh"
  },
  {
    "path": "boilerplate/Tests/Components/FullButtonTest.js",
    "chars": 722,
    "preview": "import 'react-native'\nimport React from 'react'\nimport FullButton from '../../App/Components/FullButton'\nimport { shallo"
  },
  {
    "path": "boilerplate/Tests/Components/RoundedButtonTest.js",
    "chars": 946,
    "preview": "import 'react-native'\nimport React from 'react'\nimport RoundedButton from '../../App/Components/RoundedButton'\nimport { "
  },
  {
    "path": "boilerplate/Tests/Redux/GithubReduxTest.js",
    "chars": 761,
    "preview": "import Actions, { reducer, INITIAL_STATE } from '../../App/Redux/GithubRedux'\n\ntest('request', () => {\n  const username "
  },
  {
    "path": "boilerplate/Tests/Sagas/GithubSagaTest.js",
    "chars": 1213,
    "preview": "import FixtureAPI from '../../App/Services/FixtureApi'\nimport { put, call } from 'redux-saga/effects'\nimport { getUserAv"
  },
  {
    "path": "boilerplate/Tests/Sagas/StartupSagaTest.js",
    "chars": 466,
    "preview": "import { select, put } from 'redux-saga/effects'\nimport { selectAvatar, startup } from '../../App/Sagas/StartupSagas'\nim"
  },
  {
    "path": "boilerplate/Tests/Services/FixtureAPITest.js",
    "chars": 1146,
    "preview": "import API from '../../App/Services/Api'\nimport FixtureAPI from '../../App/Services/FixtureApi'\nimport R from 'ramda'\n\nt"
  },
  {
    "path": "boilerplate/Tests/Setup.js.ejs",
    "chars": 853,
    "preview": "import { configure } from 'enzyme'\nimport Adapter from 'enzyme-adapter-react-16'\n\nconfigure({ adapter: new Adapter() })\n"
  },
  {
    "path": "boilerplate/Tests/StoriesTest.js",
    "chars": 75,
    "preview": "import initStoryshots from '@storybook/addon-storyshots'\n\ninitStoryshots()\n"
  },
  {
    "path": "boilerplate/ignite.json.ejs",
    "chars": 172,
    "preview": "{\n  \"createdWith\": \"<%= props.igniteVersion %>\",\n  \"boilerplate\": \"ignite-andross\",\n  \"examples\": \"classic\",\n  \"navigati"
  },
  {
    "path": "boilerplate/index.js.ejs",
    "chars": 184,
    "preview": "import './App/Config/ReactotronConfig'\nimport { AppRegistry } from 'react-native'\nimport App from './App/Containers/App'"
  },
  {
    "path": "boilerplate/package.json.ejs",
    "chars": 3676,
    "preview": "{\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"clean\": \"rm -rf $TMPDIR/react-* && watchman watch-del-all && npm cache clean"
  },
  {
    "path": "boilerplate/storybook/addons.js",
    "chars": 84,
    "preview": "import '@storybook/addon-actions/register'\nimport '@storybook/addon-links/register'\n"
  },
  {
    "path": "boilerplate/storybook/index.js",
    "chars": 66,
    "preview": "import StorybookUI from './storybook'\n\nexport default StorybookUI\n"
  },
  {
    "path": "boilerplate/storybook/storybook.ejs",
    "chars": 499,
    "preview": "import { AppRegistry } from 'react-native'\nimport { getStorybookUI, configure } from '@storybook/react-native'\n\n// impor"
  },
  {
    "path": "boilerplate.js",
    "chars": 9455,
    "preview": "const options = require('./options')\nconst { mergeDeepRight, pipe, assoc, omit, __ } = require('ramda')\nconst { getReact"
  },
  {
    "path": "commands/generate/component.js",
    "chars": 1472,
    "preview": "module.exports = {\n  description: 'Generates a component, styles, and an optional test.',\n  run: async function(toolbox)"
  },
  {
    "path": "commands/generate/container.js",
    "chars": 1958,
    "preview": "const patterns = require('../../lib/patterns')\n\nmodule.exports = {\n  description: 'Generates a redux smart component.',\n"
  },
  {
    "path": "commands/generate/generate.js",
    "chars": 124,
    "preview": "module.exports = {\n  name: 'generate',\n  alias: ['g'],\n  run: async toolbox => {\n    toolbox.print.printHelp(toolbox)\n  "
  },
  {
    "path": "commands/generate/list.js",
    "chars": 4740,
    "preview": "module.exports = {\n  alias: ['ls', 'listview'],\n  description: 'Generates a screen with a ListView/Flatlist/SectionList "
  },
  {
    "path": "commands/generate/redux.js",
    "chars": 851,
    "preview": "module.exports = {\n  description: ' Generates a action/creator/reducer set for Redux.',\n  run: async function(toolbox) {"
  },
  {
    "path": "commands/generate/saga.js",
    "chars": 877,
    "preview": "module.exports = {\n  description: 'Generates a saga with an optional test.',\n  run: async function(toolbox) {\n    // gra"
  },
  {
    "path": "commands/generate/screen.js",
    "chars": 2026,
    "preview": "const patterns = require('../../lib/patterns')\n\nmodule.exports = {\n  description: 'Generates an opinionated container.',"
  },
  {
    "path": "ignite.json",
    "chars": 126,
    "preview": "{\n  \"generators\": [\n    \"component\",\n    \"container\",\n    \"listview\",\n    \"list\",\n    \"redux\",\n    \"saga\",\n    \"screen\"\n"
  },
  {
    "path": "lib/patterns.js",
    "chars": 332,
    "preview": "const constants = {\n  PATTERN_IMPORTS: 'imports',\n  PATTERN_ROUTES: 'routes'\n}\n//  [constants.PATTERN_IMPORTS]: `import["
  },
  {
    "path": "lib/react-native-version.js",
    "chars": 914,
    "preview": "const { pathOr, is } = require('ramda')\n\n// the default React Native version for this boilerplate\nconst REACT_NATIVE_VER"
  },
  {
    "path": "options.js",
    "chars": 1245,
    "preview": "/**\n * The questions to ask during the install process.\n */\nconst questions = [\n  {\n    name: 'dev-screens',\n    message"
  },
  {
    "path": "package.json",
    "chars": 1884,
    "preview": "{\n  \"name\": \"ignite-andross\",\n  \"description\": \"Infinite Red's hot boilerplate for React Native.\",\n  \"license\": \"MIT\",\n "
  },
  {
    "path": "plugin.js",
    "chars": 388,
    "preview": "const screenExamples = require('./screenExamples')\n\n/**\n * Add the plugin.\n *\n * @param {any} context - The gluegun cont"
  },
  {
    "path": "readme.md",
    "chars": 8374,
    "preview": "<p align=\"center\"><img src=\"http://ir_public.s3.amazonaws.com/projects/ignite/ignite-andross-launch-screen.png\" alt=\"log"
  },
  {
    "path": "screenExamples.js",
    "chars": 911,
    "preview": "const screenExamples = [\n  {\n    title: 'Row Example',\n    screen: 'examples/RowExample.js.ejs',\n    ancillary: ['exampl"
  },
  {
    "path": "templates/component-style.ejs",
    "chars": 112,
    "preview": "import { StyleSheet } from 'react-native'\n\nexport default StyleSheet.create({\n  container: {\n    flex: 1\n  }\n})\n"
  },
  {
    "path": "templates/component-test.ejs",
    "chars": 851,
    "preview": "import test from 'ava'\nimport React from 'react'\nimport { shallow } from 'enzyme'\nimport <%= props.name %> from '../../A"
  },
  {
    "path": "templates/component.ejs",
    "chars": 584,
    "preview": "import React from 'react'\n// import PropTypes from 'prop-types';\nimport { View, Text } from 'react-native'\nimport styles"
  },
  {
    "path": "templates/container-style.ejs",
    "chars": 237,
    "preview": "import { StyleSheet } from 'react-native'\nimport { Colors, Metrics } from '../../Themes/'\n\nexport default StyleSheet.cre"
  },
  {
    "path": "templates/container.ejs",
    "chars": 651,
    "preview": "import React from 'react'\nimport { ScrollView, Text } from 'react-native'\nimport { connect } from 'react-redux'\n// Add A"
  },
  {
    "path": "templates/examples/GridExample.js.ejs",
    "chars": 3775,
    "preview": "import React, { Component } from 'react'\nimport { View, Text, ListView } from 'react-native'\nimport { connect } from 're"
  },
  {
    "path": "templates/examples/RowExample.js.ejs",
    "chars": 3695,
    "preview": "import React, { Component } from 'react'\nimport { View, Text, ListView } from 'react-native'\nimport { connect } from 're"
  },
  {
    "path": "templates/examples/SectionExample.js.ejs",
    "chars": 5094,
    "preview": "import React, { Component } from 'react'\nimport { View, ListView, Text } from 'react-native'\nimport { connect } from 're"
  },
  {
    "path": "templates/examples/Styles/GridExampleStyle.js.ejs",
    "chars": 866,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../../../../DevScreens/"
  },
  {
    "path": "templates/examples/Styles/RowExampleStyle.js.ejs",
    "chars": 716,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../../../../DevScreens/"
  },
  {
    "path": "templates/examples/Styles/SectionExampleStyle.js.ejs",
    "chars": 716,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../../../../DevScreens/"
  },
  {
    "path": "templates/flatlist-grid-style.ejs",
    "chars": 752,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../Themes'\n\nexport defa"
  },
  {
    "path": "templates/flatlist-grid.ejs",
    "chars": 4079,
    "preview": "import React from 'react'\nimport { View, Text, FlatList } from 'react-native'\nimport { connect } from 'react-redux'\n\n// "
  },
  {
    "path": "templates/flatlist-sections.ejs",
    "chars": 6339,
    "preview": "import React from 'react'\nimport { View, SectionList, Text } from 'react-native'\nimport { connect } from 'react-redux'\n\n"
  },
  {
    "path": "templates/flatlist.ejs",
    "chars": 4054,
    "preview": "import React from 'react'\nimport { View, Text, FlatList } from 'react-native'\nimport { connect } from 'react-redux'\n\n// "
  },
  {
    "path": "templates/listview-grid-style.ejs",
    "chars": 1000,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../Themes'\n\nexport defa"
  },
  {
    "path": "templates/listview-sections.ejs",
    "chars": 5240,
    "preview": "import React, { Component } from 'react'\nimport { View, ListView, Text } from 'react-native'\nimport { connect } from 're"
  },
  {
    "path": "templates/listview-style.ejs",
    "chars": 656,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles, Metrics, Colors } from '../../Themes'\n\nexport defa"
  },
  {
    "path": "templates/listview.ejs",
    "chars": 3757,
    "preview": "import React, { Component } from 'react'\nimport { View, Text, ListView } from 'react-native'\nimport { connect } from 're"
  },
  {
    "path": "templates/redux-test-ava.ejs",
    "chars": 567,
    "preview": "import test from 'ava'\nimport Actions, { reducer, INITIAL_STATE } from '../../App/Redux/<%= props.name %>Redux'\n\ntest('a"
  },
  {
    "path": "templates/redux-test-jest.ejs",
    "chars": 579,
    "preview": "import Actions, { reducer, INITIAL_STATE } from '../../App/Redux/<%= props.name %>Redux'\n\nit('attempt', () => {\n  const "
  },
  {
    "path": "templates/redux.ejs",
    "chars": 1506,
    "preview": "import { createReducer, createActions } from 'reduxsauce'\nimport Immutable from 'seamless-immutable'\n\n/* ------------- T"
  },
  {
    "path": "templates/saga-test-ava.ejs",
    "chars": 1546,
    "preview": "/* ***********************************************************\n* Wiring Instructions\n* To make this test work, you'll ne"
  },
  {
    "path": "templates/saga-test-jest.ejs",
    "chars": 1522,
    "preview": "/* ***********************************************************\n* Wiring Instructions\n* To make this test work, you'll ne"
  },
  {
    "path": "templates/saga.ejs",
    "chars": 1450,
    "preview": "/* ***********************************************************\n* A short word on how to use this automagically generated"
  },
  {
    "path": "templates/screen-style.ejs",
    "chars": 161,
    "preview": "import { StyleSheet } from 'react-native'\nimport { ApplicationStyles } from '../../Themes/'\n\nexport default StyleSheet.c"
  },
  {
    "path": "templates/screen.ejs",
    "chars": 801,
    "preview": "import React, { Component } from 'react'\nimport { ScrollView, Text, KeyboardAvoidingView } from 'react-native'\nimport { "
  },
  {
    "path": "test/generators-integration.test.js",
    "chars": 6249,
    "preview": "const execa = require('execa')\nconst jetpack = require('fs-jetpack')\nconst tempy = require('tempy')\n\nconst IGNITE = 'npx"
  },
  {
    "path": "test/interface.test.js",
    "chars": 320,
    "preview": "const boilerplate = require('../boilerplate')\nconst plugin = require('../plugin')\n\ntest('boilerplate interface', async ("
  },
  {
    "path": "test/react-native-version.test.js",
    "chars": 1581,
    "preview": "const boilerplate = require('../lib/react-native-version')\n\n// grab a few things from the boilerplate module\nconst get ="
  }
]

About this extraction

This page contains the full source code of the infinitered/ignite-andross GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 127 files (157.8 KB), approximately 43.7k tokens, and a symbol index with 33 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!