Merge branch 'develop' of github.com:status-im/discover-dapps into develop

Merge kstoykov changes and dapp updates
This commit is contained in:
cryptowanderer 2019-05-16 19:31:29 +02:00
commit 18175727d8
37 changed files with 1114 additions and 282 deletions

View File

@ -44,7 +44,12 @@ const DappListItem = props => {
<div> <div>
<div onClick={() => onToggleProfileModal(name)}> <div onClick={() => onToggleProfileModal(name)}>
<h2 className={styles.header}>{name}</h2> <h2 className={styles.header}>{name}</h2>
<p className={styles.description}>{description}</p> <p
className={styles.description}
style={{ WebkitBoxOrient: 'vertical' }}
>
{description}
</p>
</div> </div>
<a className={styles.url} href={url}> <a className={styles.url} href={url}>
{url} {url}

View File

@ -52,6 +52,11 @@
max-height: calculateRem(40); max-height: calculateRem(40);
overflow-y: hidden; overflow-y: hidden;
cursor: pointer; cursor: pointer;
display: -webkit-box;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
} }
.position { .position {

View File

@ -10,12 +10,14 @@ const FeatureDapps = props => {
<div className={styles.grid}> <div className={styles.grid}>
{props.featured.map((dapp, index) => ( {props.featured.map((dapp, index) => (
<a className={styles.dapp} key={index} href={dapp.url}> <a className={styles.dapp} key={index} href={dapp.url}>
<div className={styles.bannerWrapper}>
<ReactImageFallback <ReactImageFallback
src={dapp.banner} src={dapp.banner}
className={styles.banner} className={styles.banner}
alt={`${dapp.name} banner`} alt={`${dapp.name} banner`}
fallbackImage={fallbackBanner} fallbackImage={fallbackBanner}
/> />
</div>
<div className={styles.dapp_details}> <div className={styles.dapp_details}>
<ReactImageFallback <ReactImageFallback
className={styles.dapp_details__image} className={styles.dapp_details__image}

View File

@ -18,6 +18,7 @@
} }
.dapp { .dapp {
min-width: 220px;
font-family: $font; font-family: $font;
background: $background; background: $background;
display: flex; display: flex;
@ -26,12 +27,21 @@
text-decoration: none; text-decoration: none;
} }
.bannerWrapper {
width: 100%;
height: 0;
position: relative;
padding-bottom: 48%;
.banner { .banner {
max-width: calculateRem(400); width: 100%;
max-height: calculateRem(400); height: 100%;
width: 70vw; position: absolute;
margin-right: calculateRem(16); left: 0;
border-radius: 1%; top: 0;
border-radius: 20px;
object-fit: cover;
}
} }
.dapp_details { .dapp_details {

10
src/common/data/alert.js Normal file
View File

@ -0,0 +1,10 @@
const alert = {
visible: false,
msg: '',
positiveLabel: '',
negativeLabel: '',
positiveListener: null,
negativeListener: null,
}
export default alert

View File

@ -0,0 +1,5 @@
const howToSubmit = {
visible: false,
}
export default howToSubmit

View File

@ -7,6 +7,8 @@ import profile from '../../modules/Profile/Profile.reducer'
import submit from '../../modules/Submit/Submit.reducer' import submit from '../../modules/Submit/Submit.reducer'
import desktopMenu from '../../modules/DesktopMenu/DesktopMenu.reducer' import desktopMenu from '../../modules/DesktopMenu/DesktopMenu.reducer'
import transactionStatus from '../../modules/TransactionStatus/TransactionStatus.recuder' import transactionStatus from '../../modules/TransactionStatus/TransactionStatus.recuder'
import alert from '../../modules/Alert/Alert.reducer'
import howToSubmit from '../../modules/HowToSubmit/HowToSubmit.reducer'
export default history => export default history =>
combineReducers({ combineReducers({
@ -18,4 +20,6 @@ export default history =>
submit, submit,
desktopMenu, desktopMenu,
transactionStatus, transactionStatus,
alert,
howToSubmit,
}) })

View File

@ -0,0 +1,13 @@
import { connect } from 'react-redux'
import Alert from './Alert'
import { hideAlertAction } from './Alert.reducer'
const mapStateToProps = state => state.alert
const mapDispatchToProps = dispatch => ({
hideAlert: () => dispatch(hideAlertAction()),
})
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Alert)

View File

@ -0,0 +1,61 @@
import React from 'react'
import PropTypes from 'prop-types'
import styles from './Alert.module.scss'
class Alert extends React.Component {
constructor(props) {
super(props)
this.onClickPositive = this.onClickPositive.bind(this)
this.onClickNegative = this.onClickNegative.bind(this)
}
onClickPositive() {
const { hideAlert, positiveListener } = this.props
hideAlert()
if (positiveListener !== null) positiveListener()
}
onClickNegative() {
const { hideAlert, negativeListener } = this.props
hideAlert()
if (negativeListener !== null) negativeListener()
}
render() {
const { visible, msg, positiveLabel, negativeLabel } = this.props
const cssClassActive = visible ? styles.active : ''
return (
<div className={`${styles.alertWrapper} ${cssClassActive}`}>
<div className={styles.alert}>
<div className={styles.msg}>{msg}</div>
<div className={styles.actions}>
<div className={styles.textButton} onClick={this.onClickPositive}>
{positiveLabel}
</div>
{negativeLabel !== '' && (
<div className={styles.textButton} onClick={this.onClickNegative}>
{negativeLabel}
</div>
)}
</div>
</div>
</div>
)
}
}
Alert.defaultProps = {
negativeLabel: '',
positiveListener: null,
negativeListener: null,
}
Alert.propTypes = {
visible: PropTypes.bool.isRequired,
msg: PropTypes.string.isRequired,
positiveLabel: PropTypes.string.isRequired,
negativeLabel: PropTypes.string,
positiveListener: PropTypes.func,
negativeListener: PropTypes.func,
hideAlert: PropTypes.func.isRequired,
}
export default Alert

View File

@ -0,0 +1,66 @@
@import '../../common/styles/variables';
.alertWrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
left: 0;
top: 0;
font-family: $font;
font-size: 16px;
background: rgba(255, 255, 255, 0.5);
opacity: 0;
z-index: 4098;
pointer-events: none;
transition-property: opacity;
transition-duration: 0.25s;
.alert {
width: 280px;
max-width: 90%;
box-sizing: border-box;
font-weight: 400;
padding: 16px;
border-radius: 4px;
opacity: 0;
background: #fff;
border-radius: 8px;
box-shadow: 0px 2px 16px rgba(0, 9, 26, 0.12);
.msg {
max-height: 384px;
text-align: left;
margin-bottom: 16px;
overflow: auto;
}
.actions {
display: flex;
justify-content: flex-end;
}
.textButton {
color: $link-color;
text-transform: uppercase;
text-decoration: none !important;
font-weight: 700;
font-size: 14px;
border-radius: 4px;
padding: 4px 12px;
cursor: pointer;
}
}
}
.alertWrapper.active {
opacity: 1;
pointer-events: auto;
.alert {
opacity: 1;
}
}

View File

@ -0,0 +1,59 @@
import alertInitialState from '../../common/data/alert'
import reducerUtil from '../../common/utils/reducer'
const SHOW_ALERT = 'SHOW_ALERT'
const HIDE_ALERT = 'HIDE_ALERT'
export const showAlertAction = (
msg,
positiveLabel,
negativeLabel,
positiveListener,
negativeListener,
) => ({
type: SHOW_ALERT,
payload: {
msg,
positiveLabel,
negativeLabel,
positiveListener,
negativeListener,
},
})
export const hideAlertAction = () => ({
type: HIDE_ALERT,
payload: null,
})
const showAlert = (state, payload) => {
const {
msg,
positiveLabel,
negativeLabel,
positiveListener,
negativeListener,
} = payload
return Object.assign({}, state, {
visible: true,
msg,
positiveLabel: positiveLabel !== undefined ? positiveLabel : 'OK',
negativeLabel,
positiveListener,
negativeListener,
})
}
const hideAlert = state => {
return Object.assign({}, state, {
visible: false,
})
}
const map = {
[SHOW_ALERT]: showAlert,
[HIDE_ALERT]: hideAlert,
}
export default reducerUtil(map, alertInitialState)

View File

@ -0,0 +1,3 @@
import Alert from './Alert.container'
export default Alert

View File

@ -10,6 +10,8 @@ import Vote from '../Vote'
import Submit from '../Submit' import Submit from '../Submit'
import Terms from '../Terms/Terms' import Terms from '../Terms/Terms'
import TransactionStatus from '../TransactionStatus' import TransactionStatus from '../TransactionStatus'
import Alert from '../Alert'
import HowToSubmit from '../HowToSubmit'
class Router extends React.Component { class Router extends React.Component {
componentDidMount() { componentDidMount() {
@ -29,7 +31,9 @@ class Router extends React.Component {
</Switch>, </Switch>,
<Vote key={2} />, <Vote key={2} />,
<Submit key={3} />, <Submit key={3} />,
<TransactionStatus key={4} />, <HowToSubmit key={4} />,
<TransactionStatus key={5} />,
<Alert key={6} />,
] ]
} }
} }

View File

@ -20,7 +20,7 @@
margin: 0 calculateRem(10) calculateRem(30) calculateRem(10); margin: 0 calculateRem(10) calculateRem(30) calculateRem(10);
@media (min-width: $desktop) { @media (min-width: $desktop) {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: unset; grid-template-rows: unset;
} }
} }

View File

@ -1,8 +1,8 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { push } from 'connected-react-router' import { push } from 'connected-react-router'
import CategorySelector from './CategorySelector' import CategorySelector from './CategorySelector'
import { showSubmitAction } from '../Submit/Submit.reducer'
import { closeDesktopAction } from '../DesktopMenu/DesktopMenu.reducer' import { closeDesktopAction } from '../DesktopMenu/DesktopMenu.reducer'
import { showHowToSubmitAction } from '../HowToSubmit/HowToSubmit.reducer'
// import { selectCategory } from './CategorySelector.reducer' // import { selectCategory } from './CategorySelector.reducer'
// const mapStateToProps = state => ({ category: state.selectedCategory }) // const mapStateToProps = state => ({ category: state.selectedCategory })
@ -11,7 +11,7 @@ const mapDispatchToProps = dispatch => ({
dispatch(push(`/categories/${category}`)) dispatch(push(`/categories/${category}`))
//dispatch(selectCategory(category)) //dispatch(selectCategory(category))
}, },
onClickSubmit: () => dispatch(showSubmitAction()), onClickSubmit: () => dispatch(showHowToSubmitAction()),
onClickCloseDesktopMenu: () => dispatch(closeDesktopAction()), onClickCloseDesktopMenu: () => dispatch(closeDesktopAction()),
}) })

View File

@ -1,19 +1,21 @@
import hardcodedDapps from '../../common/data/dapps' import hardcodedDapps from '../../common/data/dapps'
import * as Categories from '../../common/data/categories' import * as Categories from '../../common/data/categories'
import reducerUtil from '../../common/utils/reducer' import reducerUtil from '../../common/utils/reducer'
import { showAlertAction } from '../Alert/Alert.reducer'
//import BlockchainTool from '../../common/blockchain' //import BlockchainTool from '../../common/blockchain'
const ON_FINISH_FETCH_ALL_DAPPS_ACTION = 'ON_FINISH_FETCH_ALL_DAPPS_ACTION' const ON_FINISH_FETCH_ALL_DAPPS_ACTION =
'DAPPS_ON_FINISH_FETCH_ALL_DAPPS_ACTION'
const ON_START_FETCH_HIGHEST_RANKED = 'ON_START_FETCH_HIGHEST_RANKED' const ON_START_FETCH_HIGHEST_RANKED = 'DAPPS_ON_START_FETCH_HIGHEST_RANKED'
const ON_FINISH_FETCH_HIGHEST_RANKED = 'ON_FINISH_FETCH_HIGHEST_RANKED' const ON_FINISH_FETCH_HIGHEST_RANKED = 'DAPPS_ON_FINISH_FETCH_HIGHEST_RANKED'
const ON_START_FETCH_RECENTLY_ADDED = 'ON_START_FETCH_RECENTLY_ADDED' const ON_START_FETCH_RECENTLY_ADDED = 'DAPPS_ON_START_FETCH_RECENTLY_ADDED'
const ON_FINISH_FETCH_RECENTLY_ADDED = 'ON_FINISH_FETCH_RECENTLY_ADDED' const ON_FINISH_FETCH_RECENTLY_ADDED = 'DAPPS_ON_FINISH_FETCH_RECENTLY_ADDED'
const ON_START_FETCH_BY_CATEGORY = 'ON_START_FETCH_BY_CATEGORY' const ON_START_FETCH_BY_CATEGORY = 'DAPPS_ON_START_FETCH_BY_CATEGORY'
const ON_FINISH_FETCH_BY_CATEGORY = 'ON_FINISH_FETCH_BY_CATEGORY' const ON_FINISH_FETCH_BY_CATEGORY = 'DAPPS_ON_FINISH_FETCH_BY_CATEGORY'
const ON_ADD_NEW_DAPP = 'ON_ADD_NEW_DAPP' const ON_UPDATE_DAPP_DATA = 'DAPPS_ON_UPDATE_DAPP_DATA'
const RECENTLY_ADDED_SIZE = 50 const RECENTLY_ADDED_SIZE = 50
const HIGHEST_RANKED_SIZE = 50 const HIGHEST_RANKED_SIZE = 50
@ -100,9 +102,12 @@ export const onFinishFetchByCategoryAction = (category, dapps) => ({
const fetchAllDappsInState = async (dispatch, getState) => { const fetchAllDappsInState = async (dispatch, getState) => {
const stateDapps = getState().dapps const stateDapps = getState().dapps
if (stateDapps.dapps === null) { if (stateDapps.dapps === null) {
try {
let dapps = await BlockchainSDK.DiscoverService.getDApps() let dapps = await BlockchainSDK.DiscoverService.getDApps()
dapps = dapps.map(dapp => { dapps = dapps.map(dapp => {
return Object.assign(dapp.metadata, { sntValue: parseInt(dapp.rate, 10) }) return Object.assign(dapp.metadata, {
sntValue: parseInt(dapp.rate, 10),
})
}) })
dapps.sort((a, b) => { dapps.sort((a, b) => {
return b.sntValue - a.sntValue return b.sntValue - a.sntValue
@ -110,6 +115,10 @@ const fetchAllDappsInState = async (dispatch, getState) => {
dispatch(onFinishFetchAllDappsAction(dapps)) dispatch(onFinishFetchAllDappsAction(dapps))
return dapps return dapps
} catch (e) {
dispatch(showAlertAction(e.message))
return []
}
} }
return stateDapps.dapps return stateDapps.dapps
} }
@ -178,8 +187,8 @@ export const fetchByCategoryAction = category => {
} }
} }
export const onAddNewDappAction = dapp => ({ export const onUpdateDappDataAction = dapp => ({
type: ON_ADD_NEW_DAPP, type: ON_UPDATE_DAPP_DATA,
payload: dapp, payload: dapp,
}) })
@ -249,11 +258,21 @@ const insertDappIntoSortedArray = (source, dapp, cmp) => {
} }
} }
const onAddNewDapp = (state, dapp) => { const onUpdateDappData = (state, dapp) => {
const dappsCategoryMap = new Map() const dappsCategoryMap = new Map()
const { dapps } = state const { dapps } = state
let { highestRanked, recentlyAdded } = state let { highestRanked, recentlyAdded } = state
let update = false
for (let i = 0; i < dapps.length; i += 1) {
if (dapps[i].id === dapp.id) {
Object.assign(dapps[i], dapp) // update object in dapps list. All other lists actually contains pointer to this options so it is updated everywhere (dapps, highestRanked, recentlyAdded and dappCategoryState)
update = true
break
}
}
if (update === false) {
insertDappIntoSortedArray(dapps, dapp, (target, dappItem) => { insertDappIntoSortedArray(dapps, dapp, (target, dappItem) => {
return target.sntValue < dappItem.sntValue return target.sntValue < dappItem.sntValue
}) })
@ -272,10 +291,11 @@ const onAddNewDapp = (state, dapp) => {
state.dappsCategoryMap.forEach((dappState, category_) => { state.dappsCategoryMap.forEach((dappState, category_) => {
dappsCategoryMap.set(category_, dappState) dappsCategoryMap.set(category_, dappState)
}) })
const dappState = dappsCategoryMap.get(dapp.metadata.category) const dappState = dappsCategoryMap.get(dapp.category)
insertDappIntoSortedArray(dappState.items, dapp, (target, dappItem) => { insertDappIntoSortedArray(dappState.items, dapp, (target, dappItem) => {
return target.sntValue < dappItem.sntValue return target.sntValue < dappItem.sntValue
}) })
}
return Object.assign({}, state, { return Object.assign({}, state, {
dapps, dapps,
@ -293,7 +313,7 @@ const map = {
[ON_FINISH_FETCH_RECENTLY_ADDED]: onFinishFetchRecentlyAdded, [ON_FINISH_FETCH_RECENTLY_ADDED]: onFinishFetchRecentlyAdded,
[ON_START_FETCH_BY_CATEGORY]: onStartFetchByCategory, [ON_START_FETCH_BY_CATEGORY]: onStartFetchByCategory,
[ON_FINISH_FETCH_BY_CATEGORY]: onFinishFetchByCategory, [ON_FINISH_FETCH_BY_CATEGORY]: onFinishFetchByCategory,
[ON_ADD_NEW_DAPP]: onAddNewDapp, [ON_UPDATE_DAPP_DATA]: onUpdateDappData,
} }
const dappsCategoryMap = new Map() const dappsCategoryMap = new Map()

View File

@ -28,8 +28,11 @@ class DesktopMenu extends React.Component {
render() { render() {
const { visible, onClickShow } = this.props const { visible, onClickShow } = this.props
const cssClassVisible = visible ? styles.visible : '' const cssClassVisible = visible ? styles.visible : ''
const cssClassNameVisibleDim = visible ? styles.dimVisible : ''
return ( return (
<>
<div className={`${styles.dim} ${cssClassNameVisibleDim}`} />
<div ref={this.nodes.root} className={styles.cnt} onClick={onClickShow}> <div ref={this.nodes.root} className={styles.cnt} onClick={onClickShow}>
<div className={`${styles.dropDown} ${cssClassVisible}`}> <div className={`${styles.dropDown} ${cssClassVisible}`}>
<CategorySelector <CategorySelector
@ -40,6 +43,7 @@ class DesktopMenu extends React.Component {
/> />
</div> </div>
</div> </div>
</>
) )
} }
} }

View File

@ -1,5 +1,23 @@
@import '../../common/styles/variables'; @import '../../common/styles/variables';
.dim {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: rgba(255, 255, 255, 0.5);
z-index: 20;
transition-duration: 0.4s;
transition-property: opacity;
}
.dim:not(.dimVisible) {
opacity: 0;
pointer-events: none;
}
.cnt { .cnt {
width: 40px; width: 40px;
height: 40px; height: 40px;
@ -16,6 +34,7 @@
0px 2px 4px rgba(0, 34, 51, 0.16); 0px 2px 4px rgba(0, 34, 51, 0.16);
transform: translateY(-50%); transform: translateY(-50%);
cursor: pointer; cursor: pointer;
z-index: 24;
@media (min-width: $desktop) { @media (min-width: $desktop) {
display: flex; display: flex;

View File

@ -1,9 +1,9 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import Footer from './Footer' import Footer from './Footer'
import { showSubmitAction } from '../Submit/Submit.reducer' import { showHowToSubmitAction } from '../HowToSubmit/HowToSubmit.reducer'
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onClickSubmit: () => dispatch(showSubmitAction()), onClickSubmit: () => dispatch(showHowToSubmitAction()),
}) })
export default connect( export default connect(

View File

@ -0,0 +1,23 @@
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import HowToSubmit from './HowToSubmit'
import { hideHowToSubmitAction } from './HowToSubmit.reducer'
import { showSubmitAction } from '../Submit/Submit.reducer'
const mapStateToProps = state => state.howToSubmit
const mapDispatchToProps = dispatch => ({
onClickClose: () => dispatch(hideHowToSubmitAction()),
onClickGetStarted: () => {
dispatch(hideHowToSubmitAction())
setTimeout(() => {
dispatch(showSubmitAction())
}, 0)
},
})
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(HowToSubmit),
)

View File

@ -0,0 +1,118 @@
import React from 'react'
import PropTypes from 'prop-types'
import styles from './HowToSubmit.module.scss'
import Modal from '../../common/components/Modal'
class HowToSubmit extends React.Component {
constructor(props) {
super(props)
}
render() {
const { visible, onClickClose, onClickGetStarted } = this.props
return (
<Modal
visible={visible && window.location.hash === '#how-to-submit'}
onClickClose={onClickClose}
windowClassName={styles.modalWindow}
>
<div className={styles.cnt}>
<div className={styles.title}>Terms and conditions</div>
<div className={styles.frame}>
<div className={styles.frameTitle}>Submit your ÐApp</div>
<ol>
<li>
Upload a name, url, description, category and image for your
DApp in the next step compulsory.
</li>
<li>
Stake the amount of SNT you want to rank your DApp optional.
</li>
<li>Hit submit.</li>
<li>
Our team will ensure that your DApp works well on mobile devices
and will then include it on the live site using the information
you provided in Step 1.
</li>
</ol>
</div>
<div className={styles.frame}>
<div className={styles.frameTitle}>Staking</div>
<p>
You need not stake anything to be included - your DApp just wont
appear in the Highest Ranked section. If you do stake SNT, your
DApp will appear in that section immediately. The DApp with the
highest effective balance (that is, SNT staked plus/minus votes
cast for/against) ranks highest.
</p>
<p>
SNT you stake is locked in the Discover contract. You can, at any
time, withdraw a percentage of what you have staked. The more you
stake, the lower the percentage you can withdraw. Withdrawals must
be made from the same wallet as you submitted with, so PLEASE
SECURE THIS ADDRESS.
</p>
</div>
<div className={`${styles.frame} ${styles.withBorder}`}>
<ol>
<li>
Staking <strong>10 000 SNT</strong> returns a rate of{' '}
<strong>99.5%</strong>, so you can withdraw up to{' '}
<strong>9 950 SNT.</strong>
</li>
<li>
Staking <strong>1 000 000 SNT</strong> returns a rate of 50.99%,
so you can withdraw up to <strong>509 958 SNT.</strong>
</li>
</ol>
</div>
<div className={styles.frame}>
<p>
Furthermore, the operators of{' '}
<a href="https://dap.ps">https://dap.ps</a> reserve the right to
remove any DApp from the UI for reasons including, but not limited
to:
</p>
</div>
<div className={`${styles.frame} ${styles.withBorder}`}>
<ol>
<li>Malicious code injection</li>
<li>
Violation of <a>Status' principles</a>
</li>
<li>Lack of usability (especially on mobile)</li>
<li>Vote manipulation.</li>
</ol>
</div>
<div className={styles.frame}>
<p>
Anyone is welcome to fork the software and implement different UI
choices for the same underlying contract. Note that Discover is
not affiliated with Status directly, we have simply chosen to use
SNT as a token of value, to abide by <a>Status principles</a>,
and to take a mobile-first approach to development.
</p>
</div>
<div className={styles.footerActions}>
<button
className={styles.submitButton}
type="submit"
onClick={onClickGetStarted}
>
Continue
</button>
</div>
</div>
</Modal>
)
}
}
HowToSubmit.propTypes = {
visible: PropTypes.bool.isRequired,
onClickClose: PropTypes.func.isRequired,
onClickGetStarted: PropTypes.func.isRequired,
}
export default HowToSubmit

View File

@ -0,0 +1,87 @@
@import '../../common/styles/variables';
.modalWindow {
height: 100%;
}
.cnt {
font-family: $font;
}
.title {
line-height: 40px;
text-align: center;
text-transform: uppercase;
font-weight: 600;
font-size: 17px;
border-bottom: 1px solid #f7f7f7;
}
a {
color: $link-color;
}
.frame {
font-size: 14px;
margin: 14px 16px;
.frameTitle {
line-height: 22px;
color: #939ba1;
font-size: 15px;
margin-bottom: 2px;
}
ol {
line-height: 24px;
list-style: none;
counter-reset: item;
padding: 0;
margin: 0;
}
ol li {
position: relative;
padding-left: 24px;
}
ol li:before {
content: counter(item) '. ';
counter-increment: item;
position: absolute;
left: 0;
top: 0;
color: #939ba1;
}
p {
line-height: 24px;
text-indent: 32px;
margin: 0;
}
}
.frame.withBorder {
border: 1px solid #eef2f5;
border-radius: 16px;
padding: 12px 16px;
}
.footerActions {
height: 64px;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #f7f7f7;
.submitButton {
background: $link-color;
border-radius: 8px;
color: #fff;
border: none;
font-family: $font;
padding: calculateRem(11) calculateRem(38);
font-size: calculateRem(15);
cursor: pointer;
}
}

View File

@ -0,0 +1,40 @@
import howToSubmitInitialState from '../../common/data/how-to-submit'
import reducerUtil from '../../common/utils/reducer'
const SHOW = 'HOW_TO_SHOW'
const HIDE = 'HOW_TO_HIDE'
export const showHowToSubmitAction = () => {
window.location.hash = 'how-to-submit'
return {
type: SHOW,
payload: null,
}
}
export const hideHowToSubmitAction = () => {
window.history.back()
return {
type: HIDE,
payload: null,
}
}
const show = state => {
return Object.assign({}, state, {
visible: true,
})
}
const hide = state => {
return Object.assign({}, state, {
visible: false,
})
}
const map = {
[SHOW]: show,
[HIDE]: hide,
}
export default reducerUtil(map, howToSubmitInitialState)

View File

@ -0,0 +1,3 @@
import HowToSubmit from './HowToSubmit.container'
export default HowToSubmit

View File

@ -108,6 +108,7 @@ class Profile extends Component {
const { innerWidth } = window const { innerWidth } = window
const { dapp_name } = this.props.match.params const { dapp_name } = this.props.match.params
// in order to access dapps list please use this.props.dapps.dapps. Also have in mind that this list might be null at the beginning
this.props.dapps.find(dapp => { this.props.dapps.find(dapp => {
if (dapp.name.toLowerCase() === dapp_name.toLowerCase()) { if (dapp.name.toLowerCase() === dapp_name.toLowerCase()) {
if (innerWidth >= 1024) { if (innerWidth >= 1024) {

View File

@ -1,5 +1,6 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom' import { withRouter } from 'react-router-dom'
import { push } from 'connected-react-router'
import Submit from './Submit' import Submit from './Submit'
import { import {
@ -15,12 +16,8 @@ import {
onImgCancelAction, onImgCancelAction,
submitAction, submitAction,
} from './Submit.reducer' } from './Submit.reducer'
import { onStartProgressAction } from '../TransactionStatus/TransactionStatus.recuder'
const mapStateToProps = state => const mapStateToProps = state => state.submit
Object.assign(state.submit, {
hasTransaction: state.transactionStatus.progress,
})
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onClickClose: () => dispatch(closeSubmitAction()), onClickClose: () => dispatch(closeSubmitAction()),
onInputName: name => dispatch(onInputNameAction(name)), onInputName: name => dispatch(onInputNameAction(name)),
@ -32,10 +29,8 @@ const mapDispatchToProps = dispatch => ({
onImgMove: (x, y) => dispatch(onImgMoveAction(x, y)), onImgMove: (x, y) => dispatch(onImgMoveAction(x, y)),
onImgCancel: () => dispatch(onImgCancelAction()), onImgCancel: () => dispatch(onImgCancelAction()),
onImgDone: imgBase64 => dispatch(onImgDoneAction(imgBase64)), onImgDone: imgBase64 => dispatch(onImgDoneAction(imgBase64)),
onSubmit: dapp => { onSubmit: dapp => dispatch(submitAction(dapp)),
dispatch(onStartProgressAction(dapp)) onClickTerms: () => dispatch(push('/terms')),
dispatch(submitAction(dapp))
},
}) })
export default withRouter( export default withRouter(

View File

@ -165,7 +165,7 @@ class Submit extends React.Component {
imgControl, imgControl,
imgControlZoom, imgControlZoom,
onImgCancel, onImgCancel,
hasTransaction, onClickTerms,
} = this.props } = this.props
const canSubmit = const canSubmit =
@ -182,13 +182,6 @@ class Submit extends React.Component {
{imgControl ? 'Position and size your image' : 'Submit a Ðapp'} {imgControl ? 'Position and size your image' : 'Submit a Ðapp'}
</div> </div>
<div className={imgControl ? styles.cntWithImgControl : ''}> <div className={imgControl ? styles.cntWithImgControl : ''}>
{hasTransaction && (
<div className={styles.hasTransaction}>
There is an active transaction. Please wait for it to finish and
then you could be able to create your Ðapp
</div>
)}
{!hasTransaction && (
<div className={imgControl ? styles.withImgControl : ''}> <div className={imgControl ? styles.withImgControl : ''}>
<div className={styles.block}> <div className={styles.block}>
<div className={styles.labelRow}> <div className={styles.labelRow}>
@ -242,9 +235,8 @@ class Submit extends React.Component {
/> />
</div> </div>
<div className={styles.imgInfo}> <div className={styles.imgInfo}>
The image should be a square 1:1 ratio JPG or PNG file, The image should be a square 1:1 ratio JPG or PNG file, minimum
minimum size is 160 × 160 pixels. The image will be placed in size is 160 × 160 pixels. The image will be placed in a circle
a circle
</div> </div>
</div> </div>
<div className={styles.block}> <div className={styles.block}>
@ -261,7 +253,7 @@ class Submit extends React.Component {
<div className={`${styles.block} ${styles.blockSubmit}`}> <div className={`${styles.block} ${styles.blockSubmit}`}>
<div className={styles.terms}> <div className={styles.terms}>
By continuing you agree to our By continuing you agree to our
<a href="#">Terms and Conditions.</a> <a onClick={onClickTerms}> Terms and Conditions.</a>
</div> </div>
<button <button
className={styles.submitButton} className={styles.submitButton}
@ -273,8 +265,7 @@ class Submit extends React.Component {
</button> </button>
</div> </div>
</div> </div>
)} {imgControl && (
{!hasTransaction && imgControl && (
<div className={styles.imgControl}> <div className={styles.imgControl}>
<div <div
className={styles.imgCanvasCnt} className={styles.imgCanvasCnt}
@ -351,7 +342,7 @@ Submit.propTypes = {
onImgCancel: PropTypes.func.isRequired, onImgCancel: PropTypes.func.isRequired,
onImgDone: PropTypes.func.isRequired, onImgDone: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
hasTransaction: PropTypes.bool.isRequired, onClickTerms: PropTypes.func.isRequired,
} }
export default Submit export default Submit

View File

@ -161,6 +161,7 @@
line-height: 22px; line-height: 22px;
color: #939ba1; color: #939ba1;
font-size: 15px; font-size: 15px;
cursor: pointer;
a { a {
color: $link-color; color: $link-color;

View File

@ -3,40 +3,60 @@ import reducerUtil from '../../common/utils/reducer'
import { import {
onReceiveTransactionInfoAction, onReceiveTransactionInfoAction,
checkTransactionStatusAction, checkTransactionStatusAction,
onStartProgressAction,
hideAction,
} from '../TransactionStatus/TransactionStatus.recuder' } from '../TransactionStatus/TransactionStatus.recuder'
import { showAlertAction } from '../Alert/Alert.reducer'
// import BlockchainTool from '../../common/blockchain' // import BlockchainTool from '../../common/blockchain'
//const BlockchainSDK = BlockchainTool.init() //const BlockchainSDK = BlockchainTool.init()
const BlockchainSDK = { DiscoverService: {} } const BlockchainSDK = { DiscoverService: {} }
BlockchainSDK.DiscoverService.createDapp = async (snt, dapp) => { BlockchainSDK.DiscoverService.createDapp = async (snt, dapp) => {
return { return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
tx: '0x3513rewrsdfsdf', tx: '0x3513rewrsdfsdf',
id: 1, id: 1,
} })
}, 1000)
})
} }
const SHOW_SUBMIT = 'SHOW_SUBMIT' const SHOW_SUBMIT_AFTER_CHECK = 'SUBMIT_SHOW_SUBMIT_AFTER_CHECK'
const CLOSE_SUBMIT = 'CLOSE_SUBMIT' const CLOSE_SUBMIT = 'SUBMIT_CLOSE_SUBMIT'
const ON_INPUT_NAME = 'ON_INPUT_NAME' const ON_INPUT_NAME = 'SUBMIT_ON_INPUT_NAME'
const ON_INPUT_DESC = 'ON_INPUT_DESC' const ON_INPUT_DESC = 'SUBMIT_ON_INPUT_DESC'
const ON_INPUT_URL = 'ON_INPUT_URL' const ON_INPUT_URL = 'SUBMIT_ON_INPUT_URL'
const ON_SELECT_CATEGORY = 'ON_SELECT_CATEGORY' const ON_SELECT_CATEGORY = 'SUBMIT_ON_SELECT_CATEGORY'
const ON_IMG_READ = 'ON_IMG_READ' const ON_IMG_READ = 'SUBMIT_ON_IMG_READ'
const ON_IMG_ZOOM = 'ON_IMG_ZOOM' const ON_IMG_ZOOM = 'SUBMIT_ON_IMG_ZOOM'
const ON_IMG_MOVE_CONTROL = 'ON_IMG_MOVE_CONTROL' const ON_IMG_MOVE_CONTROL = 'SUBMIT_ON_IMG_MOVE_CONTROL'
const ON_IMG_MOVE = 'ON_IMG_MOVE' const ON_IMG_MOVE = 'SUBMIT_ON_IMG_MOVE'
const ON_IMG_CANCEL = 'ON_IMG_CANCEL' const ON_IMG_CANCEL = 'SUBMIT_ON_IMG_CANCEL'
const ON_IMG_DONE = 'ON_IMG_DONE' const ON_IMG_DONE = 'SUBMIT_ON_IMG_DONE'
export const showSubmitAction = () => { export const showSubmitActionAfterCheck = () => {
window.location.hash = 'submit' window.location.hash = 'submit'
return { return {
type: SHOW_SUBMIT, type: SHOW_SUBMIT_AFTER_CHECK,
payload: null, payload: null,
} }
} }
export const showSubmitAction = () => {
return (dispatch, getState) => {
const state = getState()
if (state.transactionStatus.progress) {
dispatch(
showAlertAction(
'There is an active transaction. Please wait for it to finish and then you could be able to create your Ðapp',
),
)
} else dispatch(showSubmitActionAfterCheck())
}
}
export const closeSubmitAction = () => { export const closeSubmitAction = () => {
window.history.back() window.history.back()
return { return {
@ -98,7 +118,15 @@ export const onImgDoneAction = imgBase64 => ({
export const submitAction = dapp => { export const submitAction = dapp => {
return async dispatch => { return async dispatch => {
dispatch(closeSubmitAction()) dispatch(closeSubmitAction())
const { tx, id } = await BlockchainSDK.DiscoverService.createDApp(1, { dispatch(
onStartProgressAction(
dapp.name,
dapp.img,
'Status is an open source mobile DApp browser and messenger build for #Etherium',
),
)
try {
const { tx, id } = await BlockchainSDK.DiscoverService.createDapp(1, {
name: dapp.name, name: dapp.name,
url: dapp.url, url: dapp.url,
desc: dapp.desc, desc: dapp.desc,
@ -107,10 +135,14 @@ export const submitAction = dapp => {
}) })
dispatch(onReceiveTransactionInfoAction(id, tx)) dispatch(onReceiveTransactionInfoAction(id, tx))
dispatch(checkTransactionStatusAction(tx)) dispatch(checkTransactionStatusAction(tx))
} catch (e) {
dispatch(hideAction())
dispatch(showAlertAction(e.message))
}
} }
} }
const showSubmit = state => { const showSubmitAfterCheck = state => {
return Object.assign({}, state, { return Object.assign({}, state, {
visible: true, visible: true,
id: '', id: '',
@ -202,7 +234,7 @@ const onImgDone = (state, imgBase64) => {
} }
const map = { const map = {
[SHOW_SUBMIT]: showSubmit, [SHOW_SUBMIT_AFTER_CHECK]: showSubmitAfterCheck,
[CLOSE_SUBMIT]: closeSubmit, [CLOSE_SUBMIT]: closeSubmit,
[ON_INPUT_NAME]: onInputName, [ON_INPUT_NAME]: onInputName,
[ON_INPUT_DESC]: onInputDesc, [ON_INPUT_DESC]: onInputDesc,

View File

@ -9,10 +9,12 @@ class Terms extends React.Component {
this.state = { this.state = {
termsAndConditionsHeight: DEFAULT_HEIGHT, termsAndConditionsHeight: DEFAULT_HEIGHT,
responsibilitiesHeight: DEFAULT_HEIGHT, responsibilitiesHeight: DEFAULT_HEIGHT,
limitationsHeight: DEFAULT_HEIGHT,
} }
this.nodes = { this.nodes = {
termsAndCoditions: React.createRef(), termsAndCoditions: React.createRef(),
responsibilities: React.createRef(), responsibilities: React.createRef(),
limitations: React.createRef(),
} }
this.onReadMoreTermsAndConditions = this.onReadMore.bind( this.onReadMoreTermsAndConditions = this.onReadMore.bind(
this, this,
@ -24,6 +26,11 @@ class Terms extends React.Component {
this.nodes.responsibilities, this.nodes.responsibilities,
'responsibilitiesHeight', 'responsibilitiesHeight',
) )
this.onReadMoreLimitations = this.onReadMore.bind(
this,
this.nodes.limitations,
'limitationsHeight',
)
} }
onReadMore(ref, propName) { onReadMore(ref, propName) {
@ -39,7 +46,11 @@ class Terms extends React.Component {
} }
render() { render() {
const { termsAndConditionsHeight, responsibilitiesHeight } = this.state const {
termsAndConditionsHeight,
responsibilitiesHeight,
limitationsHeight,
} = this.state
return ( return (
<div className={styles.cnt}> <div className={styles.cnt}>
@ -47,13 +58,15 @@ class Terms extends React.Component {
<div className={styles.frame}> <div className={styles.frame}>
<div className={styles.frameTitle}>Terms and conditions</div> <div className={styles.frameTitle}>Terms and conditions</div>
<div className={styles.frameImportant}> <div className={styles.frameImportant}>
<p>
You must be over 13, agree that using our service is legal in your You must be over 13, agree that using our service is legal in your
jurisdiction, and that you won't do anything illegal with what we jurisdiction, and that you won't do anything illegal with what we
provide. provide.
<br /> </p>
<br /> <p>
We are not lawyers or financial advisors, and you use this software We are not lawyers or financial advisors, and you use this
at your own risk. software at your own risk.
</p>
</div> </div>
<div <div
@ -61,7 +74,37 @@ class Terms extends React.Component {
style={{ maxHeight: termsAndConditionsHeight }} style={{ maxHeight: termsAndConditionsHeight }}
> >
<div ref={this.nodes.termsAndCoditions}> <div ref={this.nodes.termsAndCoditions}>
{'lorem ipsum '.repeat(30)} <p>
You accept the Terms by either (1) clicking to agree or accept
where these options are presented to you, or (2) actually using
Discover (Discover) at https://dap.ps
</p>
<p>
In order to use Discover you must be 13 years of age or older.
If you are between 13 and 18 years of age, you must have your
parent or legal guardians permission to use Discover.
</p>
<p>
By accessing Discover you accept the terms of use as set out
herein. All information is provided of a mere general nature for
informational purposes only. By accessing Discover you warrant
to the operators, contributors and the host thereof that you may
freely, without limitation, access the DApp store and all of its
contents in your jurisdiction and shall not use Discover and its
contents in any way that infringes on laws or the rights of
others including those of the aforementioned persons (including
the entities they may represent).
</p>
<p>
Neither Discover nor any of the persons or entities involved in
any way in respect of Discover, including its host and its
contributors, provide for specific legal, fiscal, economical
and/or any other kind of advice or recommendation that may be
relied upon. A visitor to Discover will therefore act at their
own risk in accessing or in any way relying on the content of
the Discover and the visitor is therefore solely responsible for
any consequences thereof.
</p>
</div> </div>
{termsAndConditionsHeight === DEFAULT_HEIGHT && ( {termsAndConditionsHeight === DEFAULT_HEIGHT && (
<div className={styles.readMoreCnt}> <div className={styles.readMoreCnt}>
@ -76,15 +119,14 @@ class Terms extends React.Component {
</div> </div>
</div> </div>
<div className={styles.frame}> <div className={styles.frame}>
<div className={styles.frameTitle}>Terms and conditions</div> <div className={styles.frameTitle}>Your Responsibilities</div>
<div className={styles.frameImportant}> <div className={styles.frameImportant}>
You must be over 13, agree that using our service is legal in your <p>You will protect your users' information, no matter what. </p>
jurisdiction, and that you won't do anything illegal with what we <p>
provide. You will not use information you do not have permission to use,
<br /> and you may not hack anyone by submitting malicious code or
<br /> otherwise manipulating our service.
We are not lawyers or financial advisors, and you use this software </p>
at your own risk.
</div> </div>
<div <div
@ -92,7 +134,37 @@ class Terms extends React.Component {
style={{ maxHeight: responsibilitiesHeight }} style={{ maxHeight: responsibilitiesHeight }}
> >
<div ref={this.nodes.responsibilities}> <div ref={this.nodes.responsibilities}>
{'lorem ipsum '.repeat(30)} <p>
You agree that if You make Your DApp available through Discover,
You will protect the privacy and legal rights of users. If the
users provide You with, or Your DApp accesses or uses,
usernames, passwords, or other login information or personal
information, You agree to make the users aware that the
information will be available to Your DApp, and You agree to
provide legally adequate privacy notice and protection for those
users. Further, Your Dapp may only use that information for the
limited purposes for which the user has given You permission to
do so.
</p>
<p>
If Your DApp stores personal or sensitive information provided
by users, You agree to do so securely and only for as long as it
is needed. However, if the user has opted into a separate
agreement with You that allows You or Your DApp to store or use
personal or sensitive information directly related to Your DApp
(not including other products or applications), then the terms
of that separate agreement will govern Your use of such
information.
</p>
<p>
You will not engage in any activity with Discover, including
making Your Dapp available via Discover, that interferes with,
disrupts, damages, or accesses in an unauthorized manner the
devices, servers, networks, or other properties or services of
any third party including, but not limited to, Status or any
Authorized Provider. You may not use user information obtained
via Discover to sell or distribute DApp outside of Discover.
</p>
</div> </div>
{responsibilitiesHeight === DEFAULT_HEIGHT && ( {responsibilitiesHeight === DEFAULT_HEIGHT && (
<div className={styles.readMoreCnt}> <div className={styles.readMoreCnt}>
@ -106,6 +178,84 @@ class Terms extends React.Component {
)} )}
</div> </div>
</div> </div>
<div className={styles.frame}>
<div className={styles.frameTitle}>Limitation of liability</div>
<div className={styles.frameImportant}>
<p>
The people responsible for Discover are not liable for your
mistakes.
</p>
</div>
<div
className={styles.frameContent}
style={{ maxHeight: limitationsHeight }}
>
<div ref={this.nodes.limitations}>
<p>
The content, data, materials and/or other services on Discover
are provided without any warranties of any kind regarding its
title, ownership, accuracy, completeness and correctness.
</p>
<p>
Specifically, unless otherwise required by law, in no event
shall the operators, contributors to or hosts of Discover be
liable for any damages of any kind, including, but not limited
to, loss of use, loss of assets or rights or privileges, loss of
profits, or loss of data arising out of or in any way connected
with the use of the DApps and the information thereon from time
to time.
</p>
<p>
In no way are the operators, contributors to or host of Discover
responsible for the actions, decisions, transactions, or other
behavior taken or not taken by You or person relying on You in
reliance upon Discover and its contents from time to time.
</p>
</div>
{limitationsHeight === DEFAULT_HEIGHT && (
<div className={styles.readMoreCnt}>
<div
className={styles.readMore}
onClick={this.onReadMoreLimitations}
>
Read more
</div>
</div>
)}
</div>
</div>
<div className={styles.frame}>
<div className={styles.frameTitle}>Limitation of liability</div>
<div className={styles.frameContent}>
<div ref={this.nodes.responsibilities}>
<p>
Swiss law exclusively applies to the use of content, data,
materials and/or other services provided for/on Discover. The
court of the Canton of Zug, Switzerland, will be the sole and
exclusive competent court regarding any dispute relating to or
stemming from the use of Discover including, without limitation,
in respect of any breach of or dispute in respect as referred
above, irrespective of the jurisdiction applicable thereto.
</p>
</div>
</div>
</div>
<div className={styles.frame}>
<div className={styles.frameTitle}>Last Amendment</div>
<div className={styles.frameContent}>
<div ref={this.nodes.responsibilities}>
<p>
These terms of use were amended for the last time on 15th April
2019 and may be altered at any time without prior notice.
</p>
<p>
<strong>Good luck reaching the top of the rankings!</strong>
</p>
</div>
</div>
</div>
</div> </div>
) )
} }

View File

@ -32,6 +32,14 @@
padding: 16px; padding: 16px;
border: 1px solid #f7f7f7; border: 1px solid #f7f7f7;
border-radius: 16px; border-radius: 16px;
p:first-child {
margin-top: 0;
}
p:last-child {
margin-bottom: 0;
}
} }
.frameContent { .frameContent {
@ -42,6 +50,14 @@
transition-duration: 0.4s; transition-duration: 0.4s;
transition-property: max-height; transition-property: max-height;
p:first-child {
margin-top: 0;
}
p:last-child {
margin-bottom: 0;
}
} }
.readMoreCnt { .readMoreCnt {

View File

@ -7,23 +7,9 @@ import loadingSpinner from '../../common/assets/images/loading-spinner.svg'
class TransactionStatus extends React.Component { class TransactionStatus extends React.Component {
componentDidMount() { componentDidMount() {
// this.checkPublished()
this.checkTransactionHash() this.checkTransactionHash()
} }
// componentDidUpdate() {
// this.checkPublished()
// }
// checkPublished() {
// const { published, hide } = this.props
// if (published) {
// setTimeout(() => {
// hide()
// }, 1000)
// }
// }
checkTransactionHash() { checkTransactionHash() {
const { dappTx, checkTransactionStatus } = this.props const { dappTx, checkTransactionStatus } = this.props
if (dappTx === '') return if (dappTx === '') return
@ -31,7 +17,15 @@ class TransactionStatus extends React.Component {
} }
render() { render() {
const { dappName, dappImg, published, progress, failed, hide } = this.props const {
dappName,
dappImg,
txDesc,
published,
progress,
failed,
hide,
} = this.props
return ( return (
<div className={`${styles.cnt} ${dappName !== '' ? styles.active : ''}`}> <div className={`${styles.cnt} ${dappName !== '' ? styles.active : ''}`}>
@ -50,10 +44,7 @@ class TransactionStatus extends React.Component {
</div> </div>
)} )}
</div> </div>
<div className={styles.info}> <div className={styles.info}>{txDesc}</div>
Status is an open source mobile DApp browser and messenger build for
#Etherium
</div>
{published && <div className={styles.status}> Published</div>} {published && <div className={styles.status}> Published</div>}
{progress && ( {progress && (
<div className={styles.status}> <div className={styles.status}>
@ -76,6 +67,7 @@ TransactionStatus.propTypes = {
dappTx: PropTypes.string.isRequired, dappTx: PropTypes.string.isRequired,
dappName: PropTypes.string.isRequired, dappName: PropTypes.string.isRequired,
dappImg: PropTypes.string.isRequired, dappImg: PropTypes.string.isRequired,
txDesc: PropTypes.string.isRequired,
progress: PropTypes.bool.isRequired, progress: PropTypes.bool.isRequired,
published: PropTypes.bool.isRequired, published: PropTypes.bool.isRequired,
failed: PropTypes.bool.isRequired, failed: PropTypes.bool.isRequired,

View File

@ -31,6 +31,7 @@
} }
.data { .data {
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -53,9 +54,18 @@
} }
.close { .close {
width: 20px;
height: 20px;
flex: 0 0 auto; flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 16px;
font-weight: 300; font-weight: 300;
margin-left: auto; margin-left: auto;
border-radius: 50%;
background: #939ba1;
transform: rotate(45deg); transform: rotate(45deg);
cursor: pointer; cursor: pointer;
} }
@ -65,7 +75,7 @@
line-height: 18px; line-height: 18px;
color: #939ba1; color: #939ba1;
font-size: 13px; font-size: 13px;
margin-bottom: 2px; margin-bottom: 8px;
} }
.status { .status {

View File

@ -4,17 +4,19 @@ import {
transactionStatusFetchedInstance, transactionStatusFetchedInstance,
transactionStatusInitInstance, transactionStatusInitInstance,
} from './TransactionStatus.utilities' } from './TransactionStatus.utilities'
import { onAddNewDappAction } from '../Dapps/Dapps.reducer' import { onUpdateDappDataAction } from '../Dapps/Dapps.reducer'
import { showAlertAction } from '../Alert/Alert.reducer'
// import BlockchainTool from '../../common/blockchain' // import BlockchainTool from '../../common/blockchain'
const HIDE = 'HIDE' const HIDE = 'TXS_HIDE'
const ON_START_PROGRESS = 'ON_START_PROGRESS' const ON_START_PROGRESS = 'TXS_ON_START_PROGRESS'
const ON_RECEIVE_TRANSACTION_TX = 'ON_RECEIVE_TRANSACTION_TX' const ON_RECEIVE_TRANSACTION_TX = 'TXS_ON_RECEIVE_TRANSACTION_TX'
const ON_CHANGE_TRANSACTION_STATUS_DATA = 'ON_CHANGE_TRANSACTION_STATUS_DATA' const ON_CHANGE_TRANSACTION_STATUS_DATA =
'TXS_ON_CHANGE_TRANSACTION_STATUS_DATA'
//const BlockchainSDK = BlockchainTool.init() //const BlockchainSDK = BlockchainTool.init()
const BlockchainSDK = { DiscoverService: {} } const BlockchainSDK = { DiscoverService: {}, utils: {} }
BlockchainSDK.DiscoverService.getTxStatus = async tx => { BlockchainSDK.utils.getTxStatus = async tx => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
resolve(1) resolve(1)
@ -24,6 +26,7 @@ BlockchainSDK.DiscoverService.getTxStatus = async tx => {
BlockchainSDK.DiscoverService.getDAppDataById = async id => { BlockchainSDK.DiscoverService.getDAppDataById = async id => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
resolve({ resolve({
id: 'something unique',
metadata: { metadata: {
name: 'Newly added', name: 'Newly added',
url: 'https://www.astroledger.org/#/onSale', url: 'https://www.astroledger.org/#/onSale',
@ -43,9 +46,9 @@ export const hideAction = () => ({
payload: null, payload: null,
}) })
export const onStartProgressAction = dapp => ({ export const onStartProgressAction = (dappName, dappImg, desc) => ({
type: ON_START_PROGRESS, type: ON_START_PROGRESS,
payload: dapp, payload: { dappName, dappImg, desc },
}) })
export const onReceiveTransactionInfoAction = (id, tx) => ({ export const onReceiveTransactionInfoAction = (id, tx) => ({
@ -61,7 +64,12 @@ export const onChangeTransactionStatusDataAction = transactionStatus => ({
export const checkTransactionStatusAction = tx => { export const checkTransactionStatusAction = tx => {
return async dispatch => { return async dispatch => {
const status = await BlockchainSDK.utils.getTxStatus(tx) let status = 2
try {
status = await BlockchainSDK.utils.getTxStatus(tx)
} catch (e) {
// if can't read current status from network assume it is still waiting
}
const statusInt = parseInt(status, 10) const statusInt = parseInt(status, 10)
const transacationStatus = transactionStatusFetchedInstance() const transacationStatus = transactionStatusFetchedInstance()
let dapp let dapp
@ -73,13 +81,22 @@ export const checkTransactionStatusAction = tx => {
default: default:
case 1: case 1:
transacationStatus.setPublished(true) transacationStatus.setPublished(true)
try {
dapp = await BlockchainSDK.DiscoverService.getDAppDataById( dapp = await BlockchainSDK.DiscoverService.getDAppDataById(
transacationStatus.dappId, transacationStatus.dappId,
) )
dapp = Object.assign(dapp.metadata, { dapp = Object.assign(dapp.metadata, {
id: dapp.id,
sntValue: parseInt(dapp.rate, 10), sntValue: parseInt(dapp.rate, 10),
}) })
dispatch(onAddNewDappAction(dapp)) dispatch(onUpdateDappDataAction(dapp))
} catch (e) {
dispatch(
showAlertAction(e.message, 'OK', '', () => {
window.location.reload()
}),
)
}
break break
case 2: case 2:
transacationStatus.setProgress(true) transacationStatus.setProgress(true)
@ -96,11 +113,17 @@ export const checkTransactionStatusAction = tx => {
const hide = state => { const hide = state => {
const transacationStatus = transactionStatusFetchedInstance() const transacationStatus = transactionStatusFetchedInstance()
transacationStatus.setDappName('') transacationStatus.setDappName('')
transacationStatus.setProgress(false)
return Object.assign({}, state, transacationStatus) return Object.assign({}, state, transacationStatus)
} }
const onStartProgress = (state, dapp) => { const onStartProgress = (state, payload) => {
const transacationStatus = transactionStatusInitInstance(dapp.name, dapp.img) const { dappName, dappImg, desc } = payload
const transacationStatus = transactionStatusInitInstance(
dappName,
dappImg,
desc,
)
transacationStatus.persistTransactionData() transacationStatus.persistTransactionData()
return Object.assign({}, state, transacationStatus) return Object.assign({}, state, transacationStatus)
} }

View File

@ -4,6 +4,7 @@ class TransactionStatus {
constructor() { constructor() {
this.dappId = '' this.dappId = ''
this.dappTx = '' this.dappTx = ''
this.txDesc = ''
this.dappName = '' this.dappName = ''
this.dappImg = '' this.dappImg = ''
this.progress = false this.progress = false
@ -54,11 +55,12 @@ const getTransactionData = () => {
return localStorage.getItem(COOKIE_NAME) return localStorage.getItem(COOKIE_NAME)
} }
export const transactionStatusInitInstance = (name, img) => { export const transactionStatusInitInstance = (name, img, desc) => {
const model = new TransactionStatus() const model = new TransactionStatus()
model.dappName = name model.dappName = name
model.dappImg = img model.dappImg = img
model.progress = true model.progress = true
model.txDesc = desc
return model return model
} }

View File

@ -183,7 +183,7 @@ class Vote extends Component {
type="text" type="text"
value={sntValue} value={sntValue}
onChange={this.handleChange} onChange={this.handleChange}
style={{ width: `${19 * Math.max(1, sntValue.length)}px` }} style={{ width: `${21 * Math.max(1, sntValue.length)}px` }}
/> />
</div> </div>
)} )}

View File

@ -1,20 +1,26 @@
import voteInitialState from '../../common/data/vote' import voteInitialState from '../../common/data/vote'
import reducerUtil from '../../common/utils/reducer' import reducerUtil from '../../common/utils/reducer'
import { showAlertAction } from '../Alert/Alert.reducer'
import {
onStartProgressAction,
onReceiveTransactionInfoAction,
checkTransactionStatusAction,
} from '../TransactionStatus/TransactionStatus.recuder'
const SHOW_UP_VOTE = 'SHOW_UP_VOTE' const SHOW_UP_VOTE_AFTER_CHECK = 'VOTE_SHOW_UP_VOTE_AFTER_CHECK'
const SHOW_DOWN_VOTE = 'SHOW_DOWN_VOTE' const SHOW_DOWN_VOTE_AFTER_CHEECK = 'VOTE_SHOW_DOWN_VOTE_AFTER_CHEECK'
const CLOSE_VOTE = 'CLOSE_VOTE' const CLOSE_VOTE = 'VOTE_CLOSE_VOTE'
const SWITCH_TO_UPVOTE = 'SWITCH_TO_UPVOTE' const SWITCH_TO_UPVOTE = 'VOTE_SWITCH_TO_UPVOTE'
const SWITCH_TO_DOWNVOTE = 'SWITCH_TO_DOWNVOTE' const SWITCH_TO_DOWNVOTE = 'VOTE_SWITCH_TO_DOWNVOTE'
const ON_INPUT_SNT_VALUE = 'ON_INPUT_SNT_VALUE' const ON_INPUT_SNT_VALUE = 'VOTE_ON_INPUT_SNT_VALUE'
const UPDATE_AFTER_VOTING_VALUES = 'UPDATE_AFTER_VOTING_VALUES' const UPDATE_AFTER_VOTING_VALUES = 'VOTE_UPDATE_AFTER_VOTING_VALUES'
const BlockchainSDK = { DiscoverService: {} } const BlockchainSDK = { DiscoverService: {} }
BlockchainSDK.DiscoverService.upVoteEffect = (id, amount) => { BlockchainSDK.DiscoverService.upVoteEffect = (id, amount) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
resolve(amount) resolve(amount)
}, 1000) }, parseInt(Math.random() * 1000, 10))
}) })
} }
BlockchainSDK.DiscoverService.upVote = (id, amount) => { BlockchainSDK.DiscoverService.upVote = (id, amount) => {
@ -32,22 +38,48 @@ BlockchainSDK.DiscoverService.downVote = (id, amount) => {
}) })
} }
export const showUpVoteAction = dapp => { export const showUpVoteActionAfterCheck = dapp => {
window.location.hash = 'vote' window.location.hash = 'vote'
return { return {
type: SHOW_UP_VOTE, type: SHOW_UP_VOTE_AFTER_CHECK,
payload: dapp, payload: dapp,
} }
} }
export const showDownVoteAction = dapp => { export const showDownVoteActionAfterCheck = dapp => {
window.location.hash = 'vote' window.location.hash = 'vote'
return { return {
type: SHOW_DOWN_VOTE, type: SHOW_DOWN_VOTE_AFTER_CHEECK,
payload: dapp, payload: dapp,
} }
} }
export const showUpVoteAction = dapp => {
return (dispatch, getState) => {
const state = getState()
if (state.transactionStatus.progress) {
dispatch(
showAlertAction(
'There is an active transaction. Please wait for it to finish and then you could be able to vote',
),
)
} else dispatch(showUpVoteActionAfterCheck(dapp))
}
}
export const showDownVoteAction = dapp => {
return (dispatch, getState) => {
const state = getState()
if (state.transactionStatus.progress) {
dispatch(
showAlertAction(
'There is an active transaction. Please wait for it to finish and then you could be able to vote',
),
)
} else dispatch(showDownVoteActionAfterCheck(dapp))
}
}
export const switchToUpvoteAction = () => ({ export const switchToUpvoteAction = () => ({
type: SWITCH_TO_UPVOTE, type: SWITCH_TO_UPVOTE,
payload: null, payload: null,
@ -78,17 +110,23 @@ export const updateAfterVotingValuesAction = rating => ({
export const fetchVoteRatingAction = (dapp, isUpvote, sntValue) => { export const fetchVoteRatingAction = (dapp, isUpvote, sntValue) => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
let rating
try {
rating = await BlockchainSDK.DiscoverService.upVoteEffect(
dapp.id,
sntValue,
)
} catch (e) {
return
}
rating = parseInt(rating, 10)
const state = getState() const state = getState()
const voteState = state.vote const voteState = state.vote
if (voteState.dapp !== dapp) return if (voteState.dapp !== dapp) return
if (voteState.isUpvote !== isUpvote) return if (voteState.isUpvote !== isUpvote) return
if (isUpvote === true && voteState.sntValue !== sntValue.toString()) return if (isUpvote === true && voteState.sntValue !== sntValue.toString()) return
if (sntValue === 0) return if (sntValue === 0) return
const rating = await BlockchainSDK.DiscoverService.upVoteEffect(
dapp.id,
sntValue,
)
// const rating = dapp.sntValue + (isUpvote ? 1 : -1) * sntValue // const rating = dapp.sntValue + (isUpvote ? 1 : -1) * sntValue
dispatch(updateAfterVotingValuesAction(rating)) dispatch(updateAfterVotingValuesAction(rating))
} }
@ -97,20 +135,40 @@ export const fetchVoteRatingAction = (dapp, isUpvote, sntValue) => {
export const upVoteAction = (dapp, amount) => { export const upVoteAction = (dapp, amount) => {
return async dispatch => { return async dispatch => {
dispatch(closeVoteAction()) dispatch(closeVoteAction())
dispatch(
onStartProgressAction(dapp.name, dapp.image, `↑ Upvote by ${amount} SNT`),
)
try {
const tx = await BlockchainSDK.DiscoverService.upVote(dapp.id, amount) const tx = await BlockchainSDK.DiscoverService.upVote(dapp.id, amount)
console.log('upvote', tx) dispatch(onReceiveTransactionInfoAction(dapp.id, tx))
dispatch(checkTransactionStatusAction(tx))
} catch (e) {
dispatch(showAlertAction('There was an error, please try again'))
}
} }
} }
export const downVoteAction = (dapp, amount) => { export const downVoteAction = (dapp, amount) => {
return async dispatch => { return async dispatch => {
dispatch(closeVoteAction()) dispatch(closeVoteAction())
dispatch(
onStartProgressAction(
dapp.name,
dapp.image,
`↓ Downvote by ${amount} SNT`,
),
)
try {
const tx = await BlockchainSDK.DiscoverService.downVote(dapp.id, amount) const tx = await BlockchainSDK.DiscoverService.downVote(dapp.id, amount)
console.log('downvote', tx) dispatch(onReceiveTransactionInfoAction(dapp.id, tx))
dispatch(checkTransactionStatusAction(tx))
} catch (e) {
dispatch(showAlertAction('There was an error, please try again'))
}
} }
} }
const showUpVote = (state, dapp) => { const showUpVoteAfterCheck = (state, dapp) => {
return Object.assign({}, state, { return Object.assign({}, state, {
visible: true, visible: true,
dapp, dapp,
@ -120,7 +178,7 @@ const showUpVote = (state, dapp) => {
}) })
} }
const showDownVote = (state, dapp) => { const showDownVoteAfterCheck = (state, dapp) => {
return Object.assign({}, state, { return Object.assign({}, state, {
visible: true, visible: true,
dapp, dapp,
@ -164,8 +222,8 @@ const updateAfterVotingValues = (state, rating) => {
} }
const map = { const map = {
[SHOW_UP_VOTE]: showUpVote, [SHOW_UP_VOTE_AFTER_CHECK]: showUpVoteAfterCheck,
[SHOW_DOWN_VOTE]: showDownVote, [SHOW_DOWN_VOTE_AFTER_CHEECK]: showDownVoteAfterCheck,
[CLOSE_VOTE]: closeVote, [CLOSE_VOTE]: closeVote,
[SWITCH_TO_UPVOTE]: switchToUpvote, [SWITCH_TO_UPVOTE]: switchToUpvote,
[SWITCH_TO_DOWNVOTE]: switchToDownvote, [SWITCH_TO_DOWNVOTE]: switchToDownvote,