Vote component (#30)
* gh-pages setup * Minor change * Change router * Change font paths * v1 to GH pages while waiting for domain * Removes bad DApps, adds Nuo * vote component + some styles * styles for upvote and downvote * fix: downvotes * Adds routes to vote component
This commit is contained in:
parent
4c94c909f6
commit
f5e8da9d5c
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -42,11 +42,11 @@ const DappListItem = props => {
|
|||
<img src={sntIcon} alt="SNT" width="16" height="16" />
|
||||
12,345
|
||||
</span>
|
||||
<a className={styles.vote} href="#abc">
|
||||
<a className={styles.vote} href="/vote">
|
||||
<img src={upvoteArrowIcon} alt="" />
|
||||
Upvote
|
||||
</a>
|
||||
<a className={styles.vote} href="#abc">
|
||||
<a className={styles.vote} href="/vote">
|
||||
<img src={downvoteArrowIcon} alt="" />
|
||||
Downvote
|
||||
</a>
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/Inter-Regular.woff2') format('woff2'),
|
||||
url('/fonts/Inter-Regular.woff') format('woff');
|
||||
src: url('/discover-dapps/fonts/Inter-Regular.woff2') format('woff2'),
|
||||
url('/discover-dapps/fonts/Inter-Regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/Inter-Italic.woff2') format('woff2'),
|
||||
url('/fonts/Inter-Italic.woff') format('woff');
|
||||
src: url('/discover-dapps/fonts/Inter-Italic.woff2') format('woff2'),
|
||||
url('/discover-dapps/fonts/Inter-Italic.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -25,13 +25,13 @@
|
|||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('/fonts/Inter-Bold.woff2') format('woff2'),
|
||||
url('/fonts/Inter-Bold.woff') format('woff');
|
||||
src: url('/discover-dapps/fonts/Inter-Bold.woff2') format('woff2'),
|
||||
url('/discover-dapps/fonts/Inter-Bold.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url('/fonts/Inter-BoldItalic.woff2') format('woff2'),
|
||||
url('/fonts/Inter-BoldItalic.woff') format('woff');
|
||||
src: url('/discover-dapps/fonts/Inter-BoldItalic.woff2') format('woff2'),
|
||||
url('/discover-dapps/fonts/Inter-BoldItalic.woff') format('woff');
|
||||
}
|
||||
|
|
|
@ -3,13 +3,15 @@ import { Route, Switch } from 'react-router-dom'
|
|||
import Home from '../Home'
|
||||
import Filtered from '../Filtered'
|
||||
import RecentlyAdded from '../RecentlyAdded'
|
||||
import Vote from '../Vote'
|
||||
import Dapps from '../Dapps'
|
||||
|
||||
export default () => (
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route exact path="/discover-dapps/" component={Home} />
|
||||
<Route path="/categories" component={Filtered} />
|
||||
<Route path="/all" component={Dapps} />
|
||||
<Route path="/recently-added" component={RecentlyAdded} />
|
||||
<Route path="/vote" component={Vote} />
|
||||
</Switch>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { connect } from 'react-redux'
|
||||
import Vote from './Vote'
|
||||
|
||||
const mapDispatchToProps = dispatch => ({})
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps,
|
||||
)(Vote)
|
|
@ -0,0 +1,155 @@
|
|||
import React, { Component } from 'react'
|
||||
// import PropTypes from 'prop-types'
|
||||
import ReactImageFallback from 'react-image-fallback'
|
||||
import styles from './Vote.module.scss'
|
||||
import sntIcon from '../../common/assets/images/SNT.svg'
|
||||
import CategoriesUtils from '../Categories/Categories.utils'
|
||||
import Categories from '../../common/utils/categories'
|
||||
import icon from '../../common/assets/images/icon.svg'
|
||||
|
||||
const getCategoryName = category =>
|
||||
Categories.find(x => x.key === category).value
|
||||
|
||||
class Vote extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
isUpvote: true,
|
||||
sntValue: 0,
|
||||
}
|
||||
this.onClickTab = this.onClickTab.bind(this)
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
}
|
||||
|
||||
onClickTab(showUpvote) {
|
||||
return () => {
|
||||
this.setState({ isUpvote: showUpvote })
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
this.setState({ sntValue: e.target.value })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isUpvote, sntValue } = this.state
|
||||
|
||||
// TODO: extract these to props
|
||||
|
||||
const dapp = {
|
||||
name: 'Kyber',
|
||||
url: 'https://web3.kyber.network',
|
||||
description:
|
||||
'On-chain, instant and liquid platform for exchange and payment service',
|
||||
image: '/images/dapps/kyber.png',
|
||||
category: 'EXCHANGES',
|
||||
dateAdded: null,
|
||||
}
|
||||
|
||||
const currentSNTamount = 23456
|
||||
const categoryPosition = 2
|
||||
const upvoteSNTcost = 12422
|
||||
const downvoteSNTcost = 3244
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.tabs}>
|
||||
<button
|
||||
className={isUpvote ? styles.active : ''}
|
||||
type="button"
|
||||
onClick={this.onClickTab(true)}
|
||||
>
|
||||
↑ UPVOTE
|
||||
</button>
|
||||
<button
|
||||
className={!isUpvote ? styles.active : ''}
|
||||
type="button"
|
||||
onClick={this.onClickTab(false)}
|
||||
>
|
||||
↓ DOWNVOTE
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.dapp}>
|
||||
<ReactImageFallback
|
||||
className={styles.image}
|
||||
src={dapp.image}
|
||||
fallbackImage={icon}
|
||||
alt="App icon"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
{dapp.name}
|
||||
</div>
|
||||
<div className={styles.items}>
|
||||
{isUpvote && upvoteSNTcost > 0 && (
|
||||
<span className={styles.greenBadge}>
|
||||
{`${upvoteSNTcost.toLocaleString()} ↑`}
|
||||
</span>
|
||||
)}
|
||||
{!isUpvote && downvoteSNTcost > 0 && (
|
||||
<span className={styles.redBadge}>
|
||||
{`${downvoteSNTcost.toLocaleString()} ↓`}
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.item}>
|
||||
<img src={sntIcon} alt="SNT" width="24" height="24" />
|
||||
{currentSNTamount.toLocaleString()}
|
||||
</span>
|
||||
{isUpvote && upvoteSNTcost > 0 && (
|
||||
<span className={styles.greenBadge}>
|
||||
{`№${categoryPosition - 1} ↑`}
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.item}>
|
||||
<img
|
||||
src={CategoriesUtils(dapp.category)}
|
||||
alt={getCategoryName(dapp.category)}
|
||||
width="24"
|
||||
height="24"
|
||||
/>
|
||||
{`${getCategoryName(dapp.category)} №${categoryPosition}`}
|
||||
</span>
|
||||
</div>
|
||||
{!isUpvote && (
|
||||
<div className={styles.inputArea}>
|
||||
<span>{downvoteSNTcost}</span>
|
||||
</div>
|
||||
)}
|
||||
{isUpvote && (
|
||||
<div className={styles.inputArea}>
|
||||
<input type="text" value={sntValue} onChange={this.handleChange} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.footer}>
|
||||
{isUpvote && (
|
||||
<p className={styles.disclaimer}>
|
||||
SNT you spend to upvote is locked in the contract and contributes
|
||||
directly to {dapp.name}'s ranking.{' '}
|
||||
<a href="#" target="_blank">
|
||||
Learn more↗
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
{!isUpvote && (
|
||||
<p className={styles.disclaimer}>
|
||||
SNT you spend to downvote goes directly back to {dapp.name}.
|
||||
Downvoting moves their DApp down by 1% of the current ranking. The
|
||||
cost is fixed by our unique bonded curve.{' '}
|
||||
<a href="#" target="_blank">
|
||||
Learn more↗
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
<button type="submit" disabled={!sntValue}>
|
||||
{isUpvote ? 'Upvote' : 'Downvote'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Vote.propTypes = {}
|
||||
|
||||
export default Vote
|
|
@ -0,0 +1,170 @@
|
|||
@import '../../common/styles/variables';
|
||||
|
||||
.tabs {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #eef2f5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tabs button {
|
||||
color: #939ba1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
font-family: $font;
|
||||
height: calculateRem(40);
|
||||
letter-spacing: calculateRem(0.2);
|
||||
display: inline-block;
|
||||
width: 130px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabs button.active:after {
|
||||
display: block;
|
||||
clear: both;
|
||||
content: '';
|
||||
position: relative;
|
||||
left: 0;
|
||||
bottom: -9px;
|
||||
height: 1px;
|
||||
width: 24px;
|
||||
border-bottom: 2px solid $link-color;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tabs button.active {
|
||||
color: $link-color;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer button {
|
||||
background: $link-color;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
margin: calculateRem(10) auto;
|
||||
border: none;
|
||||
font-family: $font;
|
||||
padding: calculateRem(11) calculateRem(38);
|
||||
font-size: calculateRem(15);
|
||||
}
|
||||
|
||||
.footer button:disabled,
|
||||
.footer button[disabled] {
|
||||
background: $text-color;
|
||||
}
|
||||
|
||||
.footer .disclaimer {
|
||||
font-size: calculateRem(15);
|
||||
color: $text-color;
|
||||
line-height: 22px;
|
||||
font-family: $font;
|
||||
padding: calculateRem(16);
|
||||
border-bottom: 1px solid #eef2f5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer .disclaimer a {
|
||||
text-decoration: none;
|
||||
color: $link-color;
|
||||
}
|
||||
|
||||
.item,
|
||||
.dapp {
|
||||
line-height: 22px;
|
||||
font-family: $font;
|
||||
font-size: calculateRem(15);
|
||||
display: block;
|
||||
height: calculateRem(32);
|
||||
padding: 0 calculateRem(12);
|
||||
}
|
||||
|
||||
.item img,
|
||||
.dapp img {
|
||||
margin-right: calculateRem(12);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.dapp {
|
||||
margin-bottom: calculateRem(12);
|
||||
font-size: calculateRem(17);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dapp img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.badge {
|
||||
float: right;
|
||||
border-radius: 24px;
|
||||
color: #ffffff;
|
||||
font-family: $font;
|
||||
font-size: calculateRem(15);
|
||||
margin-right: calculateRem(16);
|
||||
padding: calculateRem(3) calculateRem(10);
|
||||
}
|
||||
|
||||
.greenBadge {
|
||||
@extend .badge;
|
||||
background: #44d058;
|
||||
}
|
||||
|
||||
.redBadge {
|
||||
@extend .badge;
|
||||
background: #f00;
|
||||
}
|
||||
|
||||
.inputArea {
|
||||
text-align: center;
|
||||
width: 300px;
|
||||
position: fixed;
|
||||
top: 40%;
|
||||
}
|
||||
|
||||
.inputArea input {
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
border: none;
|
||||
border-bottom: 1px solid #eef2f5;
|
||||
text-align: center;
|
||||
font-size: calculateRem(32);
|
||||
line-height: calculateRem(28);
|
||||
font-family: $font;
|
||||
}
|
||||
|
||||
.inputArea input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.inputArea::after {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 0;
|
||||
transition: all 0.05s ease-in-out;
|
||||
content: 'SNT';
|
||||
color: #939ba1;
|
||||
font-size: calculateRem(32);
|
||||
line-height: calculateRem(28);
|
||||
font-family: $font;
|
||||
}
|
||||
|
||||
.inputArea span {
|
||||
font-size: calculateRem(32);
|
||||
line-height: calculateRem(28);
|
||||
font-family: $font;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.image {
|
||||
max-width: calculateRem(40);
|
||||
max-height: calculateRem(40);
|
||||
margin-top: calculateRem(15);
|
||||
margin-right: calculateRem(16);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import Vote from './Vote.container'
|
||||
|
||||
export default Vote
|
Loading…
Reference in New Issue