Skip to content

Commit

Permalink
HF 30 - Auction UI
Browse files Browse the repository at this point in the history
  • Loading branch information
1aerostorm committed Jan 24, 2024
1 parent cad52b5 commit f173217
Show file tree
Hide file tree
Showing 15 changed files with 747 additions and 100 deletions.
3 changes: 2 additions & 1 deletion app/components/all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@
@import "./modules/nft/NFTCollections";
@import "./modules/nft/CreateNFTCollection";
@import "./modules/nft/IssueNFTToken";
@import "./modules/nft/NFTAuction";
@import "./modules/nft/NFTTokens";
@import "./modules/nft/NFTPlaceBet";
@import "./modules/nft/NFTPlaceOfferBet";
@import "./modules/nft/NFTTokenSell";
@import "./modules/nft/NFTTokenTransfer";

Expand Down
84 changes: 84 additions & 0 deletions app/components/elements/TimeExactWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import tt from 'counterpart'

import Tooltip from 'app/components/elements/Tooltip';

const SECOND = 1000
const MINUTE = 60*SECOND
const HOUR = 60*MINUTE
const DAY = 24*HOUR

function formatTimeExact(dt, maxDepth = 2) {
const msec = +dt
const now = Date.now()

const formatMsec = (ms, depth = 1, prev = null) => {
if (depth > maxDepth) return ''
if (ms >= DAY) {
const days = Math.floor(ms / DAY)
const remainder = ms % DAY
const res = days ? days + tt('time_exact_wrapper_jsx.days') : ''
return res + formatMsec(remainder, ++depth, DAY)
} else if (ms >= HOUR && (!prev || prev == DAY)) {
const hours = Math.floor(ms / HOUR)
const remainder = ms % HOUR
const res = hours ? hours + tt('time_exact_wrapper_jsx.hours') : ''
return res + formatMsec(remainder, ++depth, HOUR)
} else if (ms >= MINUTE && (!prev || prev == HOUR)) {
const minutes = Math.floor(ms / MINUTE)
const remainder = ms % MINUTE
const res = minutes ? minutes + tt('time_exact_wrapper_jsx.minutes') : ''
return res + formatMsec(remainder, ++depth, MINUTE)
} else if (!prev || prev == MINUTE) {
const secs = Math.floor(ms / SECOND)
return secs ? secs + tt('time_exact_wrapper_jsx.secs') : ''
} else {
return ''
}
}

const deltaSign = now - msec
const delta = Math.abs(deltaSign)

const result = formatMsec(delta)
return {
result,
ago: deltaSign > 0
}
}

export default class TimeExactWrapper extends React.Component {
updateState = () => {
let { date } = this.props
if (date && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$/.test(date)) {
date = date + 'Z' // Firefox really wants this Z (Zulu)
}
const dt = new Date(date)
const state = {
dt,
...formatTimeExact(dt)
}
this.setState(state)
return state
}

componentDidMount() {
this.updateState()
}

componentDidUpdate(prevProps) {
if (this.props.date !== prevProps.date)
this.updateState()
}

render() {
const { className } = this.props
let state = this.state
state = state || this.updateState()
const { dt, result } = state
return <Tooltip t={dt.toLocaleString()} className={className}>
{result}
</Tooltip>
}
}
254 changes: 254 additions & 0 deletions app/components/modules/nft/NFTAuction.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import React, { Component, } from 'react'
import tt from 'counterpart'
import { connect, } from 'react-redux'
import CloseButton from 'react-foundation-components/lib/global/close-button'
import { Formik, Form, Field, ErrorMessage, } from 'formik'
import { api } from 'golos-lib-js'
import { validateAccountName, Asset, AssetEditor } from 'golos-lib-js/lib/utils'

import AmountField from 'app/components/elements/forms/AmountField'
import AmountAssetField from 'app/components/elements/forms/AmountAssetField'
import LoadingIndicator from 'app/components/elements/LoadingIndicator'
import NFTSmallIcon from 'app/components/elements/nft/NFTSmallIcon'
import transaction from 'app/redux/Transaction'

const isDev = () => {
return process.env.NODE_ENV === 'development'
}

class NFTAuction extends Component {
state = {
auction: {
min_price: AssetEditor('0.000 GOLOS'),
expiration: isDev() ? 15 : 7
}
}

async componentDidMount() {
const isHidden = (sym) => {
return ($STM_Config.hidden_assets && $STM_Config.hidden_assets[sym])
}
try {
let assets = {}
const assetsSys = {}
const data = await api.getAssetsAsync('', [], '', 5000, 'by_symbol_name', { system: true })
for (const asset of data) {
asset.supply = Asset(asset.supply)
const symbol = asset.supply.symbol
if (symbol === 'GOLOS' || symbol === 'GBG') {
assetsSys[symbol] = asset
} else {
assets[symbol] = asset
}
}
assets = { ...assetsSys, ...assets }
this.setState({
assets
})
} catch (err) {
console.error(err)
}
}

validate = (values) => {
const errors = {}
const { min_price } = values
if (min_price.asset.eq(0)) {
errors.min_price = tt('nft_token_sell_jsx.fill_price')
}
return errors
}

setSubmitting = (submitting) => {
this.setState({ submitting })
}

getToken = () => {
const { nft_tokens, tokenIdx } = this.props
if (tokenIdx !== undefined) {
return nft_tokens.toJS().data[tokenIdx]
}
return this.props.token
}

_onSubmit = async (values) => {
this.setSubmitting(true)
this.setState({
errorMessage: ''
})

const { currentUser, onClose, } = this.props
const token = this.getToken()
const { token_id } = token

const username = currentUser.get('username')

let expirationSec = parseInt(values.expiration)
if (!isDev()) {
expirationSec *= 3600*24
}
let gprops
try {
gprops = await api.getDynamicGlobalPropertiesAsync()
} catch (err) {
console.error(err)
alert('Error - blockchain unavailable')
return
}
let expiration = new Date(gprops.time)
expiration.setSeconds(expiration.getSeconds() + expirationSec)

await this.props.auction(token_id, values.min_price.asset, expiration, username, () => {
this.props.onClose()
this.setSubmitting(false)
this.doNotRender = true
this.props.refetch()
}, (err) => {
console.error(err)
this.setSubmitting(false)
this.setState({
errorMessage: err.toString()
})
})
}

onCancelMouseDown = (e) => {
e.preventDefault()
this.setState({
cancelMouseDown: true
})
}

onCancelMouseUp = (e) => {
e.preventDefault()
if (this.state.cancelMouseDown) {
this.props.onClose()
this.setState({
cancelMouseDown: false
})
}
}

onMouseUp = (e) => {
if (this.state.cancelMouseDown) {
this.props.onClose()
}
}

onAssetChange = (e, asset) => {
this.setState({
currentBalance: asset.supply.clone()
})
}

_renderSubmittingIndicator = () => {
const { submitting } = this.state

return submitting ? <span className='submitter'>
<LoadingIndicator type='circle' />
</span> : null
}

render() {
const { assets } = this.state

if (this.doNotRender || !assets) {
return <LoadingIndicator type='circle' />
}

const { onClose, } = this.props

const token = this.getToken()

const { json_metadata, image } = token

let data
if (json_metadata) {
data = JSON.parse(json_metadata)
}
data = data || {} // node allows to use '', null, object, or array

const { errorMessage, submitting, currentBalance, } = this.state

return <div className='NFTAuction'>
<CloseButton onClick={onClose} />
<h4>{tt('nft_tokens_jsx.start_auction')}</h4>
<div style={{ marginBottom: '0.5rem' }}>
<NFTSmallIcon image={image} />
<span style={{ display: 'inline-block', marginTop: '13px', marginLeft: '0.5rem' }}>{data.title}</span>
</div>
<Formik
initialValues={this.state.auction}
enableReinitialize={true}
validate={this.validate}
validateOnMount={true}
onSubmit={this._onSubmit}
>
{({
handleSubmit, isValid, values, errors, touched, setFieldValue, handleChange,
}) => {
return (
<Form onMouseUp={this.onMouseUp}>
<div>
{tt('nft_tokens_jsx.min_price')}
</div>
<div className='row'>
<div className='column small-12'>
<div className='input-group' style={{marginBottom: 5}}>
<AmountField name='min_price' autoFocus />
<span className="input-group-label" style={{paddingLeft: 0, paddingRight: 0}}>
<AmountAssetField amountField='min_price' setFieldValue={setFieldValue} values={values} assets={assets}
onChange={this.onAssetChange}/>
</span>
</div>
{errors.min_price && <div className='error'>{errors.min_price}</div>}
</div>
</div>
<div>
{tt('nft_tokens_jsx.auction_expiration')}
</div>
<div className='row'>
<div className='column small-12'>
<div className='input-group' style={{marginBottom: 5}}>
<Field type='number' name='expiration' />
<span className="input-group-label">
{isDev() ? tt('nft_tokens_jsx.auction_expiration_dev') : tt('nft_tokens_jsx.auction_expiration2')}
</span>
</div>
{errors.expiration && <div className='error'>{errors.expiration}</div>}
</div>
</div>
{(errorMessage && errorMessage !== 'Canceled') ? <div className='row'>
<div className='column small-12'>
<div className='error' style={{marginBottom:'0px'}}>{errorMessage}</div>
</div>
</div> : null}
<div className='row' style={{ marginTop: '0.5rem' }}>
<div className='column small-8'>
<button type='submit' disabled={!isValid || submitting} className='button'>
{tt('nft_tokens_jsx.start')}
</button>
<button type='button' disabled={submitting} className='button hollow'
onMouseDown={this.onCancelMouseDown} onMouseUp={this.onCancelMouseUp}>
{tt('g.cancel')}
</button>
{this._renderSubmittingIndicator()}
</div>
</div>
</Form>
)}}</Formik>
</div>
}
}

export default connect(
// mapStateToProps
(state, ownProps) => {
return { ...ownProps,
nft_tokens: state.global.get('nft_tokens'),
}
},

dispatch => ({
})
)(NFTAuction)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.NFTPlaceBet {
.NFTAuction {
.column {
padding-left: 0rem;
padding-right: 0rem;
Expand Down
Loading

0 comments on commit f173217

Please sign in to comment.