<file_sep>import React, { Component, PropTypes } from 'react';
import { TouchableOpacity, View, Image } from 'react-native';

export default class ConfigScreen extends React.Component {

  static defaultProps = {
    isMens: false,
    shirtStyleToggler: null,
    showColorPicker: null,
    showCaptionEditor: null,
    showSizePicker: null
  };

  static propTypes = {
    isMens: React.PropTypes.bool.isRequired,
    styleToggler: React.PropTypes.func.isRequired,
    showColorPicker: React.PropTypes.func.isRequired,
    showCaptionEditor: React.PropTypes.func.isRequired,
    showSizePicker: React.PropTypes.func.isRequired
  };

  render() {
    return (
      <View style={styles.buttonContainer}>

        <TouchableOpacity onPress={() => this.props.styleToggler(!this.props.isMens)}>
          <Image source={this.props.isMens ? require('./../assets/images/icon-women.png') : require('./../assets/images/icon-men.png')}  />
        </TouchableOpacity>

        <TouchableOpacity onPress={() => {this.props.showCaptionEditor()}}>
          <View style={styles.buttonWrapper}>
            <Image source={require('./../assets/images/icon-caption.png')} />
          </View>
        </TouchableOpacity>

        <TouchableOpacity onPress={() => {this.props.showSizePicker()}}>
          <View style={styles.buttonWrapper}>
            <Image source={require('./../assets/images/icon-size.png')} />
          </View>
        </TouchableOpacity>

        <TouchableOpacity onPress={() => {this.props.showColorPicker()}}>
          <View style={styles.buttonWrapper}>
            <Image source={require('./../assets/images/icon-color-palette.png')} />
          </View>
        </TouchableOpacity>

      </View>
    );
  }
}

const styles = {
  buttonContainer: {
    backgroundColor: '#eee',
    opacity: 0.8,
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
    paddingTop: 5,
    paddingBottom: 5,
    height: 80,
  },
  buttonWrapper: {
    backgroundColor: '#eee',
    opacity: 0.8,
    flexDirection: 'column',
    justifyContent: 'space-around',
    alignItems: 'center',
    padding: 2,
  },
};
<file_sep>import React, { Component } from 'react';
import { Image, View, StyleSheet, Text, ToastAndroid } from 'react-native';
import { Container, Content, Header, Left, Button, Icon, Body, Title } from 'native-base';
import ShirtModel from '../Model/ShirtModel';
import Shirt from './Shirt';

export default class CheckoutScreen extends React.Component {

  static propTypes = {
    shirt: React.PropTypes.instanceOf(ShirtModel).isRequired,
  };

  static defaultProps = {
    shirt: null,
  };

  constructor(props) {
    super(props);
    this.state = {
      shirt: this.props.shirt,
    };
  }

  render() {
    return (
      <Image source={require('./../assets/images/editor-bg.jpg')}
             style={styles.backgroundImage}>
        <Container>
          <Header style={{ backgroundColor: '#666' }}>
            <Left>
              <Button transparent onPress={
                () => {
                  this.props.navigator.pop()
                }
              }>
                <Icon name='md-arrow-round-back' />
              </Button>
            </Left>
            <Body>
              <Title style={{ fontSize: 15 }}>Checkout</Title>
            </Body>
          </Header>
          <Content contentContainerStyle={styles.contentContainer}>
            <Shirt
              shirt={this.props.shirt}
            />
            <Button
              info
              block
              rounded
              style={{ margin: 10 }}
              onPress={() => {
                this.logUserPurchase();
              }}
            >
              <Text style={[styles.btnText]}>Buy Now</Text>
            </Button>
          </Content>
        </Container>
      </Image>
    )
  }

  logUserPurchase() {
    ToastAndroid.showWithGravity("Your purchase was successful", ToastAndroid.LONG, ToastAndroid.CENTER);
  }
}

const styles = StyleSheet.create({
  backgroundImage: {
    flex: 1
  },
  contentContainer: {
    flex: 1,
    justifyContent: 'space-between',
    padding: 10,
  },
  btnText: {
    color: '#FFF',
    fontWeight: 'bold',
  }
});
<file_sep>import React, { Component, PropTypes } from 'react';
import { Image } from 'react-native';
import { Content, Header, Left, Right, Button, Icon, Body, Title } from 'native-base';
import { takeSnapshot, dirs } from 'react-native-view-shot';
import ConfigButtonBar from './ConfigButtonBar.js';
import Shirt from './Shirt.js';
import ConfigFooter from './ConfigFooter.js';
import ModalColorPicker from './ModalColorPicker';
import ModalTextEditor from './ModalTextEditor';
import ModalSizePicker from './ModalSizePicker';
import ShirtModel from '../Model/ShirtModel';
import storage from '../Model/ShirtStorage';

const { DownloadDir } = dirs;

export default class ConfigScreen extends React.Component {

  static propTypes = {
    shirt: React.PropTypes.instanceOf(ShirtModel).isRequired,
  };

  static defaultProps = {
    shirt: null,
  };

  constructor(props) {
    super(props);
    this.state = {
      shirt: this.props.shirt,
      isColorPickerVisible: false,
      isCaptionEditorVisible: false,
      isSizePickerVisible: false,
    };
  }

  updateShirt = (newShirt) => {
    this.setState({shirt: newShirt});
  }

  toggleStyle = (isMens) => {
    this.updateShirt(Object.assign(this.state.shirt, { isMens: isMens }));
  }
  setCaption = (requestedText) => {
    this.updateShirt(Object.assign(this.state.shirt, { caption: requestedText }));
  }
  setColor = (requestedColor) => {
    this.updateShirt(Object.assign(this.state.shirt, { color: requestedColor }));
  }
  setSize = (requestedSize, price) => {
    this.updateShirt(Object.assign(this.state.shirt, { size: requestedSize, price: price }));
  }


  showColorPicker = (isVisible = true) => {
    this.setState({ isColorPickerVisible: isVisible });
  }
  showCaptionEditor = (isVisible = true) => {
    this.setState({ isCaptionEditorVisible: isVisible });
  }
  showSizePicker = (isVisible = true) => {
    this.setState({ isSizePickerVisible: isVisible });
  }


  guid() {
    function s4() {
      return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
  }

  pressHandler = (btn) => {

    let path = '';
    let shirtId = '';

    if (this.props.shirt.id === "-1") {
      path = DownloadDir + "/ShirtShop" + Math.random().toString().split('.')[1] + ".jpg";
      shirtId = this.guid();
    } else {
      path = this.props.shirt.thumbnailUri;
      shirtId = this.props.shirt.id;
    }

    console.log('saving the shirt');
    takeSnapshot(this.refs['snapshot'], {
      path: path,
      format: 'jpeg',
      quality: 0.8,
      result: 'base64'
    }).then(
      (base64data) => {
        let shirt = Object.assign(this.state.shirt, {id: shirtId, thumbnailUri: 'data:image/png;base64,' + base64data});

        storage.load({
          key: 'shirts'
        }).then(shirts => {

          let foundIndex = shirts.findIndex(shirt => shirt.id === shirtId);
          if (foundIndex === -1) {
            shirts.push(shirt);
          } else {
            shirts[foundIndex] = shirt;
          }
          storage.save({
            key: 'shirts',
            rawData: shirts
          });

          switch(btn) {
            case 'save' :
              this.redirectOnSave();
            break;
            case 'buy' :
              this.navigateToConfirmation();
            break;
          }

        }).catch(err => {
          //console.warn(err.message);
          switch (err.name) {
            case 'NotFoundError':
            case 'ExpiredError':
              storage.save({
                key: 'shirts',
                rawData: [shirt]
              });
              this.redirectOnSave();
              break;
          }
        });
      },
      error => console.error("Snapshot failed", error)
      );


  }

  navigateToConfirmation = () => {
    this.props.navigator.push({
      name: 'Confirmation',
      passProps: {
        shirt: this.state.shirt
      }
    })
  }

  redirectOnSave = () => {
    this.props.navigator.push({
      name: 'List'
    })
  }

  render() {
    return (
        <Image source={require('./../assets/images/editor-bg.jpg')}
          style={styles.backgroundImage}>
          <Header style={{ backgroundColor: '#666' }}>
            <Left>
              <Button transparent>
                <Icon
                  onPress={() => {
                    this.props.navigator.pop();
                  }}
                  name='md-arrow-back' />
              </Button>
            </Left>
            <Body>
              <Title style={{ fontSize: 15 }}>Design Your Shirt</Title>
            </Body>
            <Right />
          </Header>
          <Content>
            <ConfigButtonBar
              isMens={this.state.shirt.isMens}
              styleToggler={this.toggleStyle}
              showColorPicker={this.showColorPicker}
              showCaptionEditor={this.showCaptionEditor}
              showSizePicker={this.showSizePicker}
            />

            <Shirt
              ref="snapshot"
              shirt={this.state.shirt}
            />

            <ModalColorPicker
              isModalVisible={this.state.isColorPickerVisible}
              showColorPicker={this.showColorPicker}
              setColor={this.setColor}
              color={this.state.shirt.color}
            />

            <ModalTextEditor
              isModalVisible={this.state.isCaptionEditorVisible}
              showCaptionEditor={this.showCaptionEditor}
              setCaption={this.setCaption}
              caption={this.state.shirt.caption}
            />

            <ModalSizePicker
              isModalVisible={this.state.isSizePickerVisible}
              showSizePicker={this.showSizePicker}
              setSize={this.setSize}
              size={this.state.shirt.size}
              price={this.state.shirt.price}
            />

          </Content>
          <ConfigFooter
            pressHandler={this.pressHandler}
          />

        </Image>

    );
  }
}

const styles = {
  backgroundImage: {
    flex: 1,
    width: null,
    height: null,
    resizeMode: 'cover'
  },
};
<file_sep>import React, { Component } from 'react';
import { TouchableOpacity, View, Image, Text, StyleSheet, Dimensions } from 'react-native';
import storage from '../Model/ShirtStorage';

let { height, width } = Dimensions.get("window");

export default class HomeScreen extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <Image
        resizeMode="cover"
        source={require('./../assets/images/splash-tilt-soft.png')}
        style={styles.splashContainer}
      >
      <Text style={styles.mainTitle}>Shirt Shop</Text>
        <Text style={styles.subTitle}>Shirt design, made easy.</Text>
        <TouchableOpacity
          onPress={
            () => {
              this.props.navigator.push({
                name: 'List'
              });
            }
          }
        >
          <View style={ styles.buttonWrapper }>
            <Text style={ styles.btn }>Get Started</Text>
          </View>
        </TouchableOpacity>
      </Image>
    );
  }
}

const styles = StyleSheet.create({
  splashContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    height: height,
    width: width
  },
  mainTitle: {
    fontSize: 72,
    color: '#333'
  },
  subTitle: {
    fontSize: 28,
    color: '#000',
    marginBottom: 200
  },
  buttonWrapper: {
    flexDirection: 'row',
    justifyContent: 'center',
    width: 200,
    backgroundColor: '#666',
    borderRadius: 7
  },
  btn: {
    margin: 10,
    fontWeight: 'bold',
    fontSize: 24,
    color: 'white'
  }
});
<file_sep>export default class ShirtModel {

  id = '';
  size = 's';
  price = 0;
  isMens = false;
  caption = '';
  color = '';
  thumbnailUri = '';

  constructor(id, size, isMens, caption, color, thumbnailUri) {
    this.id = id;
    this.size = size;
    this.isMens = isMens;
    this.caption = caption;
    this.color = color;
    this.thumbnailUri = thumbnailUri;

    let price = 10;
    switch (size) {
      case 'S' :
        price = 10;
        break;
      case 'M' :
        price = 11;
      break;
      case 'L' :
        price = 12;
      break;
    }
    this.price = price;
  }

  getDescription() {
    let gender, size = '';
    gender = this.isMens ? 'men' : 'women';
    switch (this.size) {
      case 'S' :
        size = 'small';
        break;
      case 'M' :
        size = 'medium';
        break;
      case 'L' :
        size = 'large';
        break;
    }
    return gender + ', ' + size + ', ' + this.color + ', $'+this.price;
  }
}
<file_sep>import React, { Component } from 'react';
import { TouchableOpacity, Image, Dimensions } from 'react-native';
import PhotoGrid from 'react-native-photo-grid';
import { Container, Content, Header, Left, Right, Button, Text, Icon, Body, Title, Fab, View, List, ListItem, Thumbnail, Separator } from 'native-base';
import storage from '../Model/ShirtStorage';
import ShirtModel from '../Model/ShirtModel';

let { height } = Dimensions.get("window");

export default class ListScreen extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      listMode: false,
      shirts: [],
    };
  }

  componentWillMount = () => {
    let tmpThis = this;

    // load saved shirts from storage
    storage.load({
      key: 'shirts'
    }).then(shirts => {
      tmpThis.setState({ shirts: shirts });
    }).catch(err => {
      console.log("no shirts found: " + err.message);
    });
  };

  navigateToConfig = (shirt) => {
    this.props.navigator.push({
      name: 'Config',
      passProps: {
        shirt: shirt
      }
    })
  }

  navigateToConfirmation = (shirt) => {
    this.props.navigator.push({
      name: 'Confirmation',
      passProps: {
        shirt: shirt
      }
    })
  }

  removeFromStorage = (shirtToRemove) => {
    let tmpThis = this;
    let shirts = this.state.shirts;
    let indexOfPosterToRemove = shirts.findIndex(shirt => shirt.id === shirtToRemove.id);
    if (indexOfPosterToRemove !== -1) {
      shirts.splice(indexOfPosterToRemove, 1);
    } else {
      console.log("something is broken, can't delete");
    }
    storage.save({
      key: 'shirts',
      rawData: shirts
    }).then(() => {
      tmpThis.setState({ shirts: shirts});
    });
  }

  render() {
    return (
      <Container>
        <Header
          style={{
            backgroundColor: '#666',
          }}
        >
          <Left>
            <Button
              transparent
              onPress={() => {
                this.props.navigator.pop();
              }}
            >
              <Icon name='md-arrow-back' />
            </Button>
          </Left>
          <Body>
            <Title style={{ fontSize: 15 }}>Your Saved Shirts</Title>
          </Body>
          <Right>
            <Button
              transparent
              onPress={() => {
                this.setState({ listMode: !this.state.listMode });
              }}
            >
              <Icon name={this.state.listMode ? 'md-grid' : 'md-list'} />
            </Button>
          </Right>
        </Header>
        <Content>
          {this.state.shirts.length === 0 ?
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
              <Text style={{ paddingTop: height / 3, fontSize: 24 }}>
                Click the button below to get started.
              </Text>
            </View>
            :
            this.renderShirts()
          }
        </Content>
        <Fab
          style={{ backgroundColor: '#666' }}
          position="bottomRight"
          onPress={() => {
            this.navigateToConfig(new ShirtModel("-1", 'S', false, 'Caption', '', ''))
          }}
        >
          <Icon name="md-add" />
        </Fab>
      </Container>
    );
  }

  renderShirts = () => {
    return (
      this.state.listMode ? this.renderPhotoList() : this.renderPhotoGrid()
    )
  }

  renderPhotoGrid = () => {
    return (
      <PhotoGrid
        data={this.state.shirts}
        itemsPerRow={2}
        itemMargin={0}
        renderItem={this.renderGridItem}
      />
    )
  }

  renderGridItem = (item, itemSize) => {
    let shirt = new ShirtModel(item.id, item.size, item.isMens, item.caption, item.color, item.thumbnailUri)
    return (
      <TouchableOpacity
        activeOpacity={1}
        key={shirt.id}
        style={{ width: itemSize, height: itemSize }}
        onPress={() => {
          this.navigateToConfig(shirt);
        }}
      >
        <View
          style={{height: itemSize, backgroundColor: 'black'}}
        >
          <Image
            resizeMode="contain"
            style={{ flex: 1, margin: 0 }}
            source={{ uri: item.thumbnailUri }}
          />
          <Text style={{width: itemSize, color: 'white', textAlign: 'center'}}>{shirt.getDescription()}</Text>
          <View style={{ flexDirection: 'row', justifyContent: 'center'}}>
            <Button
              small
              info
              rounded
              style={{ margin: 10 }}
              onPress={() => {
                this.removeFromStorage(shirt);
              }}
            >
              <Icon name="md-trash" />
            </Button>
            <Button
              small
              info
              rounded
              style={{ marginTop: 10, marginBottom: 10 }}
              onPress={() => {
                this.navigateToConfirmation(shirt);
              }}
            >
              <Icon name="md-cart" />
            </Button>
          </View>
        </View>
      </TouchableOpacity>
    )
  }

  renderPhotoList = () => {
    return (
      <List
        dataArray={this.state.shirts}
        renderRow={this.renderListRow}
      />
    )
  }

  renderListRow = (data) => {
    let shirt = new ShirtModel(data.id, data.size, data.isMens, data.caption, data.color, data.thumbnailUri)
    return (
      <View>
        <ListItem
          thumbnail
          onPress={() => {
            this.navigateToConfig(shirt);
          }}
        >
          <Left>
            <Thumbnail large source={{ uri: shirt.thumbnailUri }} />
          </Left>
          <Body>
            <Text style={{ margin: 10, padding: 10 }}>{shirt.caption}</Text>
          </Body>
          <Right>
            <View style={{ flexDirection: 'row', justifyContent: 'flex-end'}}>
              <Button
                small
                info
                rounded
                style={{ margin: 4 }}
                onPress={
                  () => this.removeFromStorage(shirt)
                }
              >
                <Icon name="md-trash" />
              </Button>
              <Button
                small
                info
                rounded
                style={{ margin: 4 }}
                onPress={
                  () => this.navigateToConfirmation(shirt)
                }
              >
                <Icon name="md-cart" />
              </Button>
            </View>
          </Right>
        </ListItem>
        <Separator bordered>
          <Text>{shirt.getDescription()}</Text>
        </Separator>
      </View>
    )
  }

}
<file_sep>import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';

import HomeScreen from './components/HomeScreen';
import ConfigScreen from './components/ConfigScreen';
import ListScreen from './components/ListScreen';
import ConfirmationScreen from './components/ConfirmationScreen';
import CheckoutScreen from './components/CheckoutScreen';

export default class ShirtShop extends Component {

  renderScene(route, navigator) {
    if (route.name === 'Home') {
      return <HomeScreen navigator={navigator} />
    }
    if (route.name === 'List') {
      return <ListScreen navigator={navigator} />
    }
    if (route.name === 'Config') {
      return <ConfigScreen navigator={navigator} {...route.passProps} />
    }
    if (route.name === 'Confirmation') {
      return <ConfirmationScreen navigator={navigator} {...route.passProps} />
    }
    if (route.name === 'Checkout') {
      return <CheckoutScreen navigator={navigator} {...route.passProps} />
    }
  }

  render() {
    return (
      <Navigator
        style={{ flex: 1 }}
        initialRoute={{ name: 'Home' }}
        renderScene={this.renderScene}
      />
    )
  }
}

AppRegistry.registerComponent('shirtshop', () => ShirtShop);
<file_sep>import React, { Component } from 'react';
import { Image, View, StyleSheet, Text, Dimensions } from 'react-native';
import { Container, Content, Header, Left, Button, Icon, Body, Title } from 'native-base';
import ShirtModel from '../Model/ShirtModel';
import Shirt from './Shirt';

let { height, width } = Dimensions.get("window");

export default class ConfirmationScreen extends React.Component {

  static propTypes = {
    shirt: React.PropTypes.instanceOf(ShirtModel).isRequired,
  };

  static defaultProps = {
    shirt: null,
  };

  render() {
    return (
      <Image source={require('./../assets/images/editor-bg.jpg')}
             style={styles.backgroundImage}>
        <Container>
          <Header style={{ backgroundColor: '#666' }}>
            <Left>
              <Button transparent onPress={
                () => {
                  this.props.navigator.pop();
                }
              }>
                <Icon name='md-arrow-round-back' />
              </Button>
            </Left>
            <Body>
              <Title style={{ fontSize: 15 }}>Order Confirmation</Title>
            </Body>
          </Header>
          <Content contentContainerStyle={styles.contentContainer}>
              <Shirt
                shirt={this.props.shirt}
              />
              <View style={styles.textWrapper}>
                <View>
                  <Text style={[styles.labels]}>Style:</Text>
                  <Text style={[styles.values]}>{this.props.shirt.isMens ? 'Mens' : 'Womens'}</Text>
                </View>
                <View>
                  <Text style={[styles.labels]}>Size:</Text>
                  <Text style={[styles.values]}>{this.props.shirt.size}</Text>
                </View>
                <View>
                  <Text style={[styles.labels]}>Price:</Text>
                  <Text style={[styles.values]}>${this.props.shirt.price}</Text>
                </View>
              </View>
              <Button
                info
                iconRight
                block
                rounded
                style={{ margin: 10 }}
                onPress={() => {
                  this.props.navigator.push({
                    name: 'Checkout',
                    passProps: {
                      shirt: this.props.shirt
                    }
                  })
                }}
              >
                <Text style={[styles.btnText]}>Proceed to Checkout</Text>
                <Icon name="md-cart" />
              </Button>
            </Content>
        </Container>
      </Image>
    )
  }
}

const styles = StyleSheet.create({
  backgroundImage: {
    flex: 1
  },
  contentContainer: {
    flex: 1,
    justifyContent: 'space-between',
    padding: 10,
  },
  textWrapper: {
    margin: 10,
    padding: 10,
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#333',
  },
  labels: {
    fontWeight: 'bold',
    color: '#eee',
    fontSize: 14,
  },
  values: {
    fontWeight: 'bold',
    color: '#999',
    fontSize: 20,
  },
  btnText: {
    color: '#FFF',
    fontWeight: 'bold',
  }
});
