├── .babelrc
├── .gitignore
├── .watchmanconfig
├── App.js
├── README.md
├── app.json
├── assets
├── fetching-api-data.png
├── icon.png
├── mvp.jpg
├── splash.png
└── xcodeiOSSimulator.png
├── package-lock.json
├── package.json
└── src
├── Actions
└── FetchCoinData.js
├── Reducers
├── CryptoReducer.js
└── index.js
├── Store.js
├── Utils
├── ActionTypes.js
├── CoinIcons.js
└── Constants.js
└── components
├── CoinCard.js
├── CryptoContainer.js
├── Header.js
└── index.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {View} from 'react-native';
3 | import {Provider} from 'react-redux';
4 |
5 | import Store from './src/Store';
6 | import {Header, CryptoContainer} from './src/components';
7 |
8 | export default class App extends React.Component {
9 | render() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-redux-crypto-tracker
2 | Native mobile app built with react-native, redux, expo (https://expo.io), and coin-market-cap public api (https://coinmarketcap.com). Had a lot of fun building this, I think it's beautiful. This app followed a 4 chapter tutorial by @wesharehoodies! Find first chapter here: [https://medium.com/react-native-training/bitcoin-ripple-ethereum-price-checker-with-react-native-redux-e9d076037092] :)
3 |
4 | 
5 | 
6 | 
7 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "react-native-redux-crypto-tracker",
4 | "description": "This project is really great.",
5 | "slug": "react-native-redux-crypto-tracker",
6 | "privacy": "public",
7 | "sdkVersion": "30.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "updates": {
18 | "fallbackToCacheTimeout": 0
19 | },
20 | "assetBundlePatterns": [
21 | "**/*"
22 | ],
23 | "ios": {
24 | "supportsTablet": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/assets/fetching-api-data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/girl4tech/react-native-redux-crypto-tracker/abf19147462b774a9ebef618187a74fe4bec2866/assets/fetching-api-data.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/girl4tech/react-native-redux-crypto-tracker/abf19147462b774a9ebef618187a74fe4bec2866/assets/icon.png
--------------------------------------------------------------------------------
/assets/mvp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/girl4tech/react-native-redux-crypto-tracker/abf19147462b774a9ebef618187a74fe4bec2866/assets/mvp.jpg
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/girl4tech/react-native-redux-crypto-tracker/abf19147462b774a9ebef618187a74fe4bec2866/assets/splash.png
--------------------------------------------------------------------------------
/assets/xcodeiOSSimulator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/girl4tech/react-native-redux-crypto-tracker/abf19147462b774a9ebef618187a74fe4bec2866/assets/xcodeiOSSimulator.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty-project-template",
3 | "main": "node_modules/expo/AppEntry.js",
4 | "private": true,
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "eject": "expo eject",
10 | "postinstall": "remotedev-debugger --hostname localhost --port 5678 --injectserver"
11 | },
12 | "remotedev": {
13 | "hostname": "localhost",
14 | "port": 5678
15 | },
16 | "dependencies": {
17 | "axios": "^0.18.0",
18 | "expo": "^30.0.1",
19 | "react": "16.3.1",
20 | "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz",
21 | "react-native-loading-spinner-overlay": "^0.5.2",
22 | "react-redux": "^5.0.7",
23 | "redux": "^4.0.0",
24 | "redux-promise": "^0.6.0",
25 | "redux-thunk": "^2.3.0"
26 | },
27 | "devDependencies": {
28 | "redux-logger": "^3.0.6",
29 | "remote-redux-devtools": "^0.5.13",
30 | "remotedev-rn-debugger": "^0.8.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Actions/FetchCoinData.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import {apiBaseURL} from './../Utils/Constants';
3 | import {
4 | FETCHING_COIN_DATA,
5 | FETCHING_COIN_DATA_SUCCESS,
6 | FETCHING_COIN_DATA_FAIL
7 | } from './../Utils/ActionTypes';
8 |
9 | export default function FetchCoinData(){
10 | return dispatch =>{
11 | dispatch({type: FETCHING_COIN_DATA})
12 |
13 | return axios.get(`${apiBaseURL}/v1/ticker/?limit=10`)
14 | .then(res=>{
15 | dispatch({type: FETCHING_COIN_DATA_SUCCESS, payload:res.data})
16 | })
17 | .catch(err=>{
18 | dispatch({type: FETCHING_COIN_DATA_FAIL, payload:err.data})
19 | });
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Reducers/CryptoReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCHING_COIN_DATA,
3 | FETCHING_COIN_DATA_SUCCESS,
4 | FETCHING_COIN_DATA_FAIL
5 | } from './../Utils/ActionTypes';
6 |
7 | const initialState = {
8 | isFetching: false,
9 | data: [],
10 | hasError: false,
11 | errorMessage: null
12 | }
13 |
14 | export default function(state=initialState, action){
15 | switch(action.type){
16 | case FETCHING_COIN_DATA:
17 | return Object.assign({}, state,{
18 | isFetching: true,
19 | data:null,
20 | hasError: false,
21 | errorMessage:null
22 | });
23 | case FETCHING_COIN_DATA_SUCCESS:
24 | return Object.assign({}, state, {
25 | isFetching:false,
26 | data:action.payload,
27 | hasError:false,
28 | errorMessage:null
29 | });
30 | case FETCHING_COIN_DATA_FAIL:
31 | return Object.assign({}, state, {
32 | isFetching: false,
33 | data: action.payload,
34 | hasError: true,
35 | errorMessage: action.err
36 | });
37 |
38 | default:
39 | return state;
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/src/Reducers/index.js:
--------------------------------------------------------------------------------
1 | import {combineReducers} from 'redux';
2 | import CryptoReducer from './CryptoReducer';
3 |
4 | export default combineReducers({
5 | crypto: CryptoReducer
6 | });
--------------------------------------------------------------------------------
/src/Store.js:
--------------------------------------------------------------------------------
1 | import {Platform} from 'react-native';
2 | import {
3 | createStore,
4 | applyMiddleware,
5 | compose
6 | } from 'redux';
7 | import devTools from 'remote-redux-devtools';
8 | import promise from 'redux-promise';
9 | import thunk from 'redux-thunk';
10 | import logger from 'redux-logger';
11 |
12 | import RootReducer from './Reducers';
13 |
14 | const middleware = applyMiddleware(thunk, promise, logger);
15 |
16 | const Store = createStore(
17 | RootReducer,
18 | compose(
19 | middleware,
20 | devTools({
21 | name: Platform.OS,
22 | hostname: 'localhost',
23 | port:5678
24 | }),
25 | )
26 | );
27 |
28 | export default Store;
--------------------------------------------------------------------------------
/src/Utils/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const FETCHING_COIN_DATA = "FETCHING_COIN_DATA";
2 | export const FETCHING_COIN_DATA_SUCCESS = "FETCHING_COIN_DATA_SUCCESS";
3 | export const FETCHING_COIN_DATA_FAIL = "FETCHING_COIN_DATA_FAIL";
--------------------------------------------------------------------------------
/src/Utils/CoinIcons.js:
--------------------------------------------------------------------------------
1 | export const images = {
2 | BTC: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609483/bitcoin_eqld4v.png',
3 | ETH: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609485/ethereum_nw0chu.png',
4 | XRP: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609486/ripple_p0xeut.png',
5 | BCH: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1516327336/bch_2x_hahroi.png',
6 | LTC: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1512427497/ltc_fjbqjf.png',
7 | DASH: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609484/dash_oltvqi.png',
8 | XEM: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609486/nem_imprip.png',
9 | BCC: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609486/bitconnect_oj1bo5.png',
10 | XMR: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609486/monero_wzk3ur.png',
11 | NEO: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1508609486/neo_fvoo6c.png',
12 | MIOTA: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1512510148/miota_2x_xkby9u.png',
13 | ADA: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1513434489/cardano_unympj.png',
14 | BTG: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1513434542/bitcoin-gold_reytam.png',
15 | XLM: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1516326886/xlm_2x_jfwlwt.png',
16 | ADA: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1516326874/ada_2x_g4fs0c.png',
17 | IOTA: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1516327102/miota_2x_zsvtqc.png',
18 | TRX: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1516326885/trx_2x_ukhxjm.png',
19 | EOS: 'https://res.cloudinary.com/da7jhtpgh/image/upload/v1516326878/eos_2x_dvr7p0.png'
20 | };
--------------------------------------------------------------------------------
/src/Utils/Constants.js:
--------------------------------------------------------------------------------
1 | export const apiBaseURL="https://api.coinmarketcap.com";
2 |
--------------------------------------------------------------------------------
/src/components/CoinCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | View,
4 | Text,
5 | StyleSheet,
6 | Image
7 | } from 'react-native';
8 | import {images} from '../Utils/CoinIcons';
9 |
10 | const styles = StyleSheet.create({
11 | container:{
12 | display:"flex",
13 | marginBottom:20,
14 | borderBottomColor:"#e5e5e5",
15 | borderBottomWidth:3,
16 | padding:20
17 | },
18 | upperRow:{
19 | display:"flex",
20 | flexDirection:"row",
21 | marginBottom:15
22 | },
23 | coinSymbol:{
24 | marginTop:10,
25 | marginLeft:20,
26 | marginRight:5,
27 | fontWeight:"bold"
28 | },
29 | coinName:{
30 | marginTop:10,
31 | marginLeft:5,
32 | marginRight:20
33 | },
34 | seperator:{
35 | marginTop:10
36 | },
37 | coinPrice:{
38 | marginTop:10,
39 | marginLeft:"auto",
40 | marginRight:10,
41 | fontWeight:"bold"
42 | },
43 | image:{
44 | width:35,
45 | height:35,
46 | },
47 | moneySymbol:{
48 | fontWeight: "bold"
49 | },
50 | statisticsContainer:{
51 | display:"flex",
52 | borderTopColor:"#FAFAFA",
53 | borderTopWidth:2,
54 | padding:10,
55 | flexDirection:"row",
56 | justifyContent:"space-around"
57 | },
58 | percentChangePlus:{
59 | color:"#00BFA5",
60 | fontWeight:"bold",
61 | marginLeft:5
62 | },
63 | percentChangeMinus:{
64 | color:"#DD2C00",
65 | fontWeight:"bold",
66 | marginLeft:5
67 | }
68 | })
69 |
70 | const {
71 | container,
72 | image,
73 | moneySymbol,
74 | upperRow,
75 | coinSymbol,
76 | coinName,
77 | coinPrice,
78 | statisticsContainer,
79 | seperator,
80 | percentChangePlus,
81 | percentChangeMinus
82 | } = styles;
83 |
84 | const CoinCard=({symbol, coin_name, price_usd, percent_change_24h, percent_change_7d})=>{
85 |
86 | return (
87 |
88 |
89 |
90 |
94 | {symbol}
95 | |
96 | {coin_name}
97 | {price_usd} $
98 |
99 |
100 |
101 |
102 |
103 | 24h:
104 | {percent_change_7d} %
105 |
106 | 7d:
107 | {percent_change_7d} %
108 |
109 |
110 |
111 |
112 |
113 | );
114 | }
115 |
116 | export default CoinCard;
--------------------------------------------------------------------------------
/src/components/CryptoContainer.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {connect} from 'react-redux';
3 | import {View, Text, ScrollView, StyleSheet} from 'react-native';
4 | import Spinner from 'react-native-loading-spinner-overlay';
5 |
6 | import FetchCoinData from './../Actions/FetchCoinData';
7 | import CoinCard from './CoinCard';
8 |
9 | class CryptoContainer extends Component{
10 |
11 | componentWillMount(){
12 | this.props.FetchCoinData();
13 | }
14 |
15 | renderCoinCards(){
16 | const {crypto} = this.props;
17 | return crypto.data.map((coin)=>
18 |
26 | )
27 | }
28 |
29 | render(){
30 | const {crypto} = this.props;
31 | const {contentContainer} = styles;
32 |
33 | if (crypto.isFetching){
34 | return(
35 |
36 |
42 |
43 | )
44 | }
45 |
46 | return(
47 |
48 | {this.renderCoinCards()}
49 |
50 | )
51 | }
52 | }
53 |
54 | const styles ={
55 | contentContainer:{
56 | paddingBottom:140,
57 | paddingTop:55
58 | }
59 | }
60 |
61 | function mapStateToProps(state){
62 | return{
63 | crypto: state.crypto
64 | }
65 | }
66 |
67 | export default connect(mapStateToProps, {FetchCoinData})(CryptoContainer)
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {View, Text, StyleSheet} from 'react-native';
3 |
4 | const Header = () => {
5 | return (
6 |
7 |
8 | CoCreateX Cryptocurrency App
9 |
10 |
11 | )
12 | }
13 |
14 | const styles = StyleSheet.create({
15 | headerContainer:{
16 | display:"flex",
17 | marginTop:55,
18 | alignItems:"center",
19 | },
20 | header:{
21 | fontWeight:"bold",
22 | fontSize:20,
23 | }
24 | })
25 |
26 | const {headerContainer, header} = styles;
27 |
28 | export default Header;
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import Header from './Header.js';
2 | import CryptoContainer from './CryptoContainer';
3 |
4 | export {Header, CryptoContainer};
5 |
--------------------------------------------------------------------------------