liquid-funding/app/components/projects/Project.jsx

218 lines
6.8 KiB
React
Raw Normal View History

2019-02-12 22:00:00 +00:00
import web3 from 'Embark/web3'
import React, { useMemo, useState, useEffect } from 'react'
2019-02-11 22:07:11 +00:00
import Typography from '@material-ui/core/Typography'
2019-02-12 13:48:49 +00:00
import Button from '@material-ui/core/Button'
2019-02-12 22:00:00 +00:00
import withObservables from '@nozbe/with-observables'
import { Q } from '@nozbe/watermelondb'
import { withDatabase } from '@nozbe/watermelondb/DatabaseProvider'
2019-02-12 13:48:49 +00:00
import Card from '@material-ui/core/Card'
import CardHeader from '@material-ui/core/CardHeader'
import CardMedia from '@material-ui/core/CardMedia'
import CardContent from '@material-ui/core/CardContent'
import CardActions from '@material-ui/core/CardActions'
import CardActionArea from '@material-ui/core/CardActionArea'
2019-02-12 17:50:14 +00:00
import LinearProgress from '@material-ui/core/LinearProgress'
2019-02-12 13:48:49 +00:00
import Avatar from '@material-ui/core/Avatar'
2019-02-12 22:00:00 +00:00
import { uniqBy, length } from 'ramda'
2019-02-11 22:07:11 +00:00
import { withStyles } from '@material-ui/core/styles'
import PropTypes from 'prop-types'
2019-02-12 22:00:00 +00:00
import { toEther } from '../../utils/conversions'
import { getTokenLabel } from '../../utils/currencies'
import { timeSinceBlock } from '../../utils/dates'
2019-02-11 22:07:11 +00:00
const styles = theme => ({
root: {
display: 'grid',
2019-02-12 01:19:57 +00:00
gridTemplateColumns: '1fr 5fr',
2019-02-12 15:16:43 +00:00
gridTemplateRows: '1fr 4fr',
2019-02-11 22:07:11 +00:00
gridColumnGap: '1em',
2019-02-12 01:19:57 +00:00
gridRowGap: '36px',
2019-02-12 16:11:27 +00:00
margin: '1.75rem 4.5rem',
fontFamily: theme.typography.fontFamily
2019-02-11 22:07:11 +00:00
},
paper: {
padding: theme.spacing.unit * 2,
textAlign: 'center',
color: theme.palette.text.secondary,
},
title: {
fontSize: '2.5rem'
},
subTitle: {
fontSize: '1.5rem',
fontWeight: 200
},
creator:{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-around',
2019-02-12 01:19:57 +00:00
alignItems: 'end'
2019-02-11 22:07:11 +00:00
},
creatorName: {
fontSize: '1rem'
},
media: {
objectFit: 'cover'
},
secondRow: {
gridColumnStart: '1',
2019-02-12 01:19:57 +00:00
gridColumnEnd: '3',
display: 'grid',
gridTemplateColumns: '2fr 1fr',
gridColumnGap: '3em'
},
infoBox: {
display: 'grid',
2019-02-12 17:50:14 +00:00
gridTemplateRows: '0rem 2.5rem 2.5rem 2.5rem',
2019-02-12 13:48:49 +00:00
gridRowGap: '2rem'
},
infoBoxSection: {
display: 'flex',
flexDirection: 'column'
2019-02-12 01:19:57 +00:00
},
infoText: {
fontSize: '2rem'
},
2019-02-12 17:50:14 +00:00
raisedAmount: {
fontSize: '2em',
color: theme.palette.primary.main
},
2019-02-12 01:19:57 +00:00
subtext: {
2019-02-12 16:11:27 +00:00
...theme.typography.caption,
fontSize: '1rem'
2019-02-12 17:50:14 +00:00
},
linearColorPrimary: {
backgroundColor: '#00000014',
},
linearBarColorPrimary: {
backgroundColor: theme.palette.primary.main
2019-02-11 22:07:11 +00:00
}
})
2019-02-12 22:00:00 +00:00
function getReceivedAmount(id, transfers){
return transfers
.filter(t => t.returnValues.to === id)
.reduce((pv, cv) => {
const amount = Number(toEther(cv.returnValues.amount))
return pv + amount
}, 0)
}
function getWithdrawnAmount(id, transfers){
return transfers
.filter(t => t.returnValues.from === id)
.reduce((pv, cv) => {
const amount = Number(toEther(cv.returnValues.amount))
return pv + amount
}, 0)
}
function getAmountsPledged(pledges){
const amounts = {}
pledges.forEach(pledge => {
const { token, amount } = pledge
if (amounts[token]) amounts[token] += Number(toEther(amount))
else amounts[token] = Number(toEther(amount))
})
return Object
.entries(amounts)
.map(entry => ([getTokenLabel(entry[0]), entry[1]]))
}
function getNumberOfBackers(pledges){
return length(uniqBy(p => p.owner, pledges))
}
async function getProjectAge(id, events, setState){
const event = events.find(e => e.returnValues.idProject === id)
const { timestamp } = await web3.eth.getBlock(event.blockNumber)
setState(timeSinceBlock(timestamp, 'days'))
}
function Project({ classes, match, profile, transfers, pledges, projectAddedEvents }) {
2019-02-12 17:50:14 +00:00
const projectId = match.params.id
2019-02-12 22:00:00 +00:00
const [projectAge, setAge] = useState(null)
useEffect(() => {
getProjectAge(projectId, projectAddedEvents, setAge)
})
const received = useMemo(() => getReceivedAmount(projectId, transfers), [projectId, transfers])
const withdrawn = useMemo(() => getWithdrawnAmount(projectId, transfers), [projectId, transfers])
const amountsPledged = useMemo(() => getAmountsPledged(pledges), [pledges])
const numberOfBackers = useMemo(() => getNumberOfBackers(pledges), [pledges])
2019-02-11 22:07:11 +00:00
return (
<div className={classes.root}>
<div className={classes.creator}>
2019-02-12 17:50:14 +00:00
<Avatar src="https://material-ui.com/static/images/avatar/1.jpg" />
2019-02-11 22:07:11 +00:00
<Typography className={classes.creatorName}>By Creator Name</Typography>
</div>
<div>
<Typography className={classes.title} component="h2" gutterBottom>
Akira, The Linux Design Tool
</Typography>
<Typography className={classes.subTitle} component="h5" gutterBottom>
UX/UI Design application for Linux
</Typography>
</div>
<div className={classes.secondRow}>
2019-02-12 15:16:43 +00:00
<CardMedia
component="img"
alt="video"
className={classes.media}
src="https://images.pexels.com/photos/1464143/pexels-photo-1464143.jpeg?cs=srgb&dl=background-camera-close-up-1464143.jpg&fm=jpg"
title="media-description"
/>
2019-02-12 13:48:49 +00:00
2019-02-12 01:19:57 +00:00
<div className={classes.infoBox}>
2019-02-12 17:50:14 +00:00
<LinearProgress
classes={{
colorPrimary: classes.linearColorPrimary,
barColorPrimary: classes.linearBarColorPrimary,
}}
variant="determinate"
value={30}
/>
2019-02-12 13:48:49 +00:00
<div className={classes.infoBoxSection}>
2019-02-12 17:50:14 +00:00
<span className={classes.raisedAmount}>
2019-02-12 22:00:00 +00:00
{`${amountsPledged[0][1]} ${amountsPledged[0][0]}`}
2019-02-12 13:48:49 +00:00
</span>
<span className={classes.subtext}>pledged of $48,894 goal</span>
</div>
<div className={classes.infoBoxSection}>
2019-02-12 22:00:00 +00:00
<span className={classes.infoText}>{numberOfBackers}</span>
2019-02-12 13:48:49 +00:00
<span className={classes.subtext}>backers</span>
</div>
<div className={classes.infoBoxSection}>
2019-02-12 22:00:00 +00:00
<span className={classes.infoText}>{projectAge}</span>
2019-02-12 13:48:49 +00:00
<span className={classes.subtext}>days active</span>
</div>
<Button color="primary" variant="contained" style={{ height: '50px' }}>Back this project</Button>
2019-02-12 01:19:57 +00:00
</div>
2019-02-11 22:07:11 +00:00
</div>
</div>
)
}
Project.propTypes = {
classes: PropTypes.object.isRequired,
2019-02-12 22:00:00 +00:00
match: PropTypes.object,
profile: PropTypes.array.isRequired,
2019-02-11 22:07:11 +00:00
}
2019-02-12 22:00:00 +00:00
const StyledProject = withStyles(styles)(Project)
export default withDatabase(withObservables([], ({ database, match }) => ({
profile: database.collections.get('profiles').query(
Q.where('id_profile', match.params.id)
).observe(),
transfers: database.collections.get('lp_events').query(
Q.where('event', 'Transfer')
).observe(),
projectAddedEvents: database.collections.get('lp_events').query(
Q.where('event', 'ProjectAdded')
).observe(),
pledges: database.collections.get('pledges').query(
Q.where('intended_project', match.params.id)
).observe()
}))(StyledProject))