Skip to content

Commit

Permalink
feat: #11 added stats tab.
Browse files Browse the repository at this point in the history
  • Loading branch information
naodya committed May 18, 2020
1 parent 530799e commit f594604
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 12 deletions.
45 changes: 45 additions & 0 deletions src/components/Stats/Card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Text, View } from 'react-native';
import FontAwesome5Icon from '@expo/vector-icons/FontAwesome5';
import EntypoIcon from '@expo/vector-icons/Entypo';
import styles from './Card.style';

const Card = (props) => {
const { icon, title, stat, cardColor } = props;
return (
<View
style={[
{
borderRadius: 10,
backgroundColor: cardColor,
borderColor: cardColor,
},
styles.cardWrapper,
]}
>
<View style={styles.contentWrapper}>
<View style={styles.cardIcon}>
{icon === 'swap' ? (
<EntypoIcon name={icon} size={45} color="#ffffff" />
) : (
<FontAwesome5Icon name={icon} size={45} color="#ffffff" />
)}
</View>
<Text style={styles.cardStatText}>{stat}</Text>
<Text style={styles.cardTitleText}>{title}</Text>
</View>
</View>
);
};

Card.propTypes = {
icon: PropTypes.string,
title: PropTypes.string,
stat: PropTypes.number,
cardColor: PropTypes.string,
};

Card.defaultProps = {};

export default Card;
38 changes: 38 additions & 0 deletions src/components/Stats/Card.style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { StyleSheet, Dimensions } from 'react-native';
export default StyleSheet.create({
cardWrapper: {
flex: 1,
borderRadius: 10,
borderStyle: 'solid',
elevation: 10,
height: 160,
},
contentWrapper: {
marginHorizontal: 10,
paddingVertical: 10,
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center',
},
cardIcon: {
paddingVertical: 10,
marginTop: 10,
textAlign: 'center',
alignSelf: 'center',
},
cardTitleText: {
color: '#ffffff',
height: 52,
fontSize: 16,
lineHeight: 17,
textAlign: 'center',
},
cardStatText: {
color: '#ffffff',
paddingVertical: 10,
fontSize: 35,
fontWeight: 'bold',
lineHeight: 25,
textAlign: 'center',
},
});
186 changes: 186 additions & 0 deletions src/components/Stats/Grid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import React from 'react';
import { View, Dimensions, ViewPropTypes, FlatList } from 'react-native';
import PropTypes from 'prop-types';
import { chunkArray, calculateDimensions, generateStyles } from '../../utils';

class Grid extends React.Component {
constructor(props) {
super(props);

this.renderRow = this.renderRow.bind(this);
this.onLayout = this.onLayout.bind(this);

const { staticDimension, horizontal } = props;

// Calculate total dimensions and set to state
let totalDimension = staticDimension;

if (!staticDimension) {
const dimension = horizontal ? 'height' : 'width';
totalDimension = Dimensions.get('window')[dimension];
}

this.state = {
totalDimension,
};
}

onLayout(e) {
const { staticDimension, horizontal, onLayout } = this.props;
const { totalDimension } = this.state;

if (!staticDimension) {
const { width, height } = e.nativeEvent.layout || {};
const newTotalDimension = horizontal ? height : width;

if (totalDimension !== newTotalDimension) {
this.setState({
totalDimension: newTotalDimension,
});
}
}

// call onLayout prop if passed
if (onLayout) {
onLayout(e);
}
}

renderRow({
rowItems,
rowIndex,
separators,
isLastRow,
itemsPerRow,
rowStyle,
containerStyle,
}) {
const { spacing, horizontal, itemContainerStyle, renderItem } = this.props;

// To make up for the top padding
let additionalRowStyle = {};
if (isLastRow) {
additionalRowStyle = {
...(!horizontal ? { marginBottom: spacing } : {}),
...(horizontal ? { marginRight: spacing } : {}),
};
}

return (
<View style={[rowStyle, additionalRowStyle]}>
{rowItems.map((item, i) => (
<View
key={`item_${rowIndex * itemsPerRow + i}`}
style={[containerStyle, itemContainerStyle]}
>
{renderItem({
item,
index: rowIndex * itemsPerRow + i,
separators,
rowIndex,
})}
</View>
))}
</View>
);
}

render() {
const {
items,
style,
spacing,
fixed,
itemDimension,
renderItem,
horizontal,
onLayout,
header,
staticDimension,
itemContainerStyle,
...restProps
} = this.props;

const { totalDimension } = this.state;

const {
containerDimension,
itemsPerRow,
fixedSpacing,
} = calculateDimensions({
itemDimension,
staticDimension,
totalDimension,
spacing,
fixed,
});

const { containerStyle, rowStyle } = generateStyles({
horizontal,
itemDimension,
containerDimension,
spacing,
fixedSpacing,
fixed,
});

const rows = chunkArray(items, itemsPerRow); // Splitting the data into rows
return (
<FlatList
data={rows}
ListHeaderComponent={header}
renderItem={({ item, index }) =>
this.renderRow({
rowItems: item,
rowIndex: index,
isLastRow: index === rows.length - 1,
itemsPerRow,
rowStyle,
containerStyle,
})
}
style={[
{
...(horizontal
? { paddingLeft: spacing }
: { paddingTop: spacing }),
},
style,
]}
onLayout={this.onLayout}
keyExtractor={(_, index) => `row_${index}`}
{...restProps}
horizontal={horizontal}
ref={(flatList) => {
this.flatList = flatList;
}}
/>
);
}
}

Grid.propTypes = {
renderItem: PropTypes.func.isRequired,
items: PropTypes.arrayOf(PropTypes.any).isRequired,
itemDimension: PropTypes.number,
fixed: PropTypes.bool,
spacing: PropTypes.number,
style: ViewPropTypes.style,
itemContainerStyle: ViewPropTypes.style,
staticDimension: PropTypes.number,
horizontal: PropTypes.bool,
onLayout: PropTypes.func,
};

Grid.defaultProps = {
fixed: false,
itemDimension: 80,
spacing: 30,
style: {},
itemContainerStyle: undefined,
staticDimension: undefined,
horizontal: false,
onLayout: null,
};

export default Grid;
96 changes: 96 additions & 0 deletions src/components/Stats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import { firestore } from '../../api/firebase';

import Grid from './Grid';
import Card from './Card';
import Loading from '../Loading';

// Context.
import { AppContext } from '../../context';

const Stats = () => {
const [statsData, setStatsData] = useState([]);
const [dataAvailability, setDataAvailability] = useState(true);

const { locale } = React.useContext(AppContext);

useEffect(() => {
const unsubscribe = firestore()
.collection('stats/app-stat-cards/data')
.onSnapshot(handleOnSnapshot, handleError);

// Stop listening for updates when no longer required
return () => unsubscribe();
}, []);

const handleOnSnapshot = (snapshot) => {
if (!snapshot.empty) {
let list = [];
snapshot.forEach((doc) => {
if (doc.exists) list = [...list, { id: doc.id, ...doc.data() }];
});

setStatsData(list);
}
};

const handleError = (error) => {
console.log(error.message);
setDataAvailability(false);
};

return (
<View style={styles.container}>
{dataAvailability === true ? (
statsData.length === 0 ? (
<Loading />
) : (
<View style={styles.gridWrapper}>
<Grid
itemDimension={150}
items={statsData}
numofLoadingItems={6}
spacing={10}
isLoading={statsData.length < 1 ? true : false}
renderItem={(item) => {
return (
<Card
title={item.item.text[locale]}
stat={item.item.stat}
icon={item.item.icon}
cardColor={item.item.cardColor}
/>
);
}}
/>
</View>
)
) : (
<View style={styles.statScreenWarningText}>
<Text>
Ooops! We are unable to communiate with the server. Are you offline?
</Text>
</View>
)}
</View>
);
};

export default Stats;

const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: 'stretch',
},
gridWrapper: {
justifyContent: 'flex-start',
},
statScreenWarningText: {
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 14,
height: Dimensions.get('window').height - 50,
},
});
14 changes: 5 additions & 9 deletions src/screens/StatsScreen.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import React from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { StyleSheet, View } from 'react-native';
import Stats from '../components/Stats';

const StatsScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text>Stats Screen</Text>
<TouchableOpacity
title="Full Screen"
onPress={() => navigation.navigate('FullScreen')}
>
<Text>Try Full Screen</Text>
</TouchableOpacity>
<Stats />
</View>
);
};
Expand All @@ -21,7 +16,8 @@ const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
alignItems: 'flex-start',
padding: 16,
},
});
Loading

0 comments on commit f594604

Please sign in to comment.