├── .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 | ![alt tag](https://github.com/karina001/react-native-redux-crypto-tracker/blob/master/assets/fetching-api-data.png) 5 | ![alt tag](https://github.com/karina001/react-native-redux-crypto-tracker/blob/master/assets/xcodeiOSSimulator.png) 6 | ![alt tag](https://github.com/karina001/react-native-redux-crypto-tracker/blob/master/assets/mvp.jpg) 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 | --------------------------------------------------------------------------------