Skip to content

Commit

Permalink
UI for NFT
Browse files Browse the repository at this point in the history
  • Loading branch information
1aerostorm committed Sep 24, 2023
1 parent 23c9050 commit 438d2f7
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 29 deletions.
Binary file added app/assets/images/nft.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/components/all.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
@import "./elements/common/DialogManager/index";
@import "./elements/common/TooltipManager/index";
@import "./elements/market/CMCWidget";
@import "./elements/nft/NFTSmallIcon";
@import "./elements/donate/Donate";
@import "./elements/postEditor/MarkdownEditor/MarkdownEditor";
@import "./elements/postEditor/MarkdownEditorToolbar/index";
Expand Down
12 changes: 12 additions & 0 deletions app/components/elements/nft/NFTSmallIcon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React, { Component, } from 'react'

class NFTSmallIcon extends Component {
render() {
const { image, ...rest } = this.props

return <a className='NFTSmallIcon'
style={{ backgroundImage: `url(${image})` }} href={image} target='_blank' rel='nofollow noreferrer' {...rest}></a>
}
}

export default NFTSmallIcon
11 changes: 11 additions & 0 deletions app/components/elements/nft/NFTSmallIcon.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.NFTSmallIcon {
background-size: cover;
background-repeat: no-repeat;
background-position: 50% 50%;
border-radius: 50%;

width: 3rem;
height: 3rem;
display: inline-block;
vertical-align: top;
}
14 changes: 14 additions & 0 deletions app/components/modules/Donate.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ class Donate extends React.Component {
})
}

showGiftNft = (e) => {
e.preventDefault()
const { opts, } = this.props
const { to, permlink, } = opts
this.props.showGiftNft(to, permlink)
}

render() {
const { currentUser, currentAccount, opts, uias, sliderMax } = this.props
const { sym } = opts
Expand Down Expand Up @@ -189,6 +196,9 @@ class Donate extends React.Component {
<button type='submit' disabled={disabled} className='button'>
{tt('g.donate_support')}
</button>
<button className='button hollow' onClick={this.showGiftNft}>
{tt('transfer_jsx.gift_nft')}
</button>
</span>}

<FormikAgent opts={opts} setFieldValue={setFieldValue}
Expand Down Expand Up @@ -261,6 +271,10 @@ export default connect(
setDonateDefaults: (donateDefaults) => {
dispatch(user.actions.setDonateDefaults(donateDefaults))
},
showGiftNft: (author, permlink) => {
dispatch(user.actions.setGiftNftDefaults({ author, permlink }))
dispatch(user.actions.showGiftNft())
},
dispatchSubmit: async ({
to, amount, memo, isMemoEncrypted,
permlink, is_comment, vote, myVote, voteAllowType, currentUser, errorCallback
Expand Down
111 changes: 111 additions & 0 deletions app/components/modules/GiftNFT.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React from 'react'
import { connect } from 'react-redux'
import { Map } from 'immutable'
import tt from 'counterpart'
import { Asset } from 'golos-lib-js/lib/utils'

import NFTSmallIcon from 'app/components/elements/nft/NFTSmallIcon'
import LoadingIndicator from 'app/components/elements/LoadingIndicator'
import g from 'app/redux/GlobalReducer'
import transaction from 'app/redux/Transaction'
import { getAssetMeta } from 'app/utils/market/utils'

class GiftNFT extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}

componentDidMount() {
if (!this.props.nft_tokens) {
this.props.fetchNFTTokens(this.props.currentUser)
}
}

render() {
const { nft_tokens, nft_assets } = this.props

const tokens = nft_tokens ? nft_tokens.toJS() : []
const assets = nft_assets ? nft_assets.toJS() : null

let items = []
let count = 0
for (const token of tokens) {
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

let last_price
const last_buy_price = Asset(token.last_buy_price)
if (last_buy_price.amount > 0) {
const asset = assets[last_buy_price.symbol]
let imageUrl
if (asset) {
imageUrl = getAssetMeta(asset).image_url
}
last_price = <span title={last_buy_price.floatString}>
{imageUrl && <img className='price-icon' src={imageUrl} alt={''} />}
{last_buy_price.amountFloat}
</span>
}

items.push(<tr key={count} className={count % 2 == 0 ? '' : 'zebra'}>
<td title={data.title}>
<NFTSmallIcon image={image} />
</td>
<td title={data.title}>
{data.title}
</td>
<td>
{last_price}
</td>
</tr>)

++count
}

if (!items.length) {
items = <LoadingIndicator type='circle' />
} else {
items = <table><tbody>
{items}
</tbody></table>
}

return <div>
<div className='row'>
<h3>{tt('transfer_jsx.gift_nft')}</h3>
</div>
{items}
</div>
}
}

export default connect(
(state, ownProps) => {
const opts = state.user.get('donate_defaults', Map()).toJS()

const currentUser = state.user.getIn(['current'])
const currentAccount = currentUser && state.global.getIn(['accounts', currentUser.get('username')])


return { ...ownProps,
currentUser,
currentAccount,
nft_tokens: state.global.get('nft_tokens'),
nft_assets: state.global.get('nft_assets'),
}
},
dispatch => ({
fetchNFTTokens: (currentUser) => {
if (!currentUser) return
const account = currentUser.get('username')
dispatch(g.actions.fetchNftTokens({ account }))
},
})
)(GiftNFT)
13 changes: 13 additions & 0 deletions app/components/modules/Modals.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Reveal from 'react-foundation-components/lib/global/reveal';
import LoginForm from 'app/components/modules/LoginForm';
import ConfirmTransactionForm from 'app/components/modules/ConfirmTransactionForm';
import Donate from 'app/components/modules/Donate'
import GiftNFT from 'app/components/modules/GiftNFT'
import SignUp from 'app/components/modules/SignUp'
import ChangeAccount from 'app/components/modules/ChangeAccount'
import AddAccount from 'app/components/modules/AddAccount'
Expand All @@ -22,6 +23,7 @@ class Modals extends React.Component {
show_login_modal: PropTypes.bool,
show_confirm_modal: PropTypes.bool,
show_donate_modal: PropTypes.bool,
show_gift_nft_modal: PropTypes.bool,
show_signup_modal: PropTypes.bool,
show_promote_post_modal: PropTypes.bool,
show_change_account_modal: PropTypes.bool,
Expand Down Expand Up @@ -53,12 +55,14 @@ class Modals extends React.Component {
show_login_modal,
show_confirm_modal,
show_donate_modal,
show_gift_nft_modal,
show_signup_modal,
show_change_account_modal,
show_add_account_modal,
show_app_download_modal,
hideLogin,
hideDonate,
hideGiftNFT,
hideConfirm,
hideSignUp,
hideChangeAccount,
Expand Down Expand Up @@ -89,6 +93,10 @@ class Modals extends React.Component {
<CloseButton onClick={hideDonate} />
<Donate />
</Reveal>}
{show_gift_nft_modal && <Reveal onHide={hideGiftNFT} show={show_gift_nft_modal} revealStyle={{ width: '600px' }}>
<CloseButton onClick={hideGiftNFT} />
<GiftNFT />
</Reveal>}
{show_signup_modal && <Reveal onHide={hideSignUp} show={show_signup_modal}>
<CloseButton onClick={hideSignUp} />
<SignUp />
Expand Down Expand Up @@ -119,6 +127,7 @@ export default connect(
loginUnclosable,
show_confirm_modal: state.transaction.get('show_confirm_modal'),
show_donate_modal: state.user.get('show_donate_modal'),
show_gift_nft_modal: state.user.get('show_gift_nft_modal'),
show_promote_post_modal: state.user.get('show_promote_post_modal'),
show_signup_modal: state.user.get('show_signup_modal'),
show_change_account_modal: state.user.get('show_change_account_modal'),
Expand All @@ -140,6 +149,10 @@ export default connect(
if (e) e.preventDefault()
dispatch(user.actions.hideDonate())
},
hideGiftNFT: e => {
if (e) e.preventDefault()
dispatch(user.actions.hideGiftNft())
},
hidePromotePost: e => {
if (e) e.preventDefault();
dispatch(user.actions.hidePromotePost())
Expand Down
3 changes: 2 additions & 1 deletion app/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,8 @@
"balance": "Balance",
"private_key_in_memo": "Do not write your private keys in this field!",
"verified_exchange_no_memo": "You must include a memo for your exchange transfer, according to exchanger's rules.",
"verified_exchange_liquid_only": "You may exchange tokens from GOLOS balance only."
"verified_exchange_liquid_only": "You may exchange tokens from GOLOS balance only.",
"gift_nft": "Gift NFT"
},
"user_saga_js": {
"image_upload": {
Expand Down
3 changes: 2 additions & 1 deletion app/locales/ru-RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,8 @@
"can_issue": "Можно выпустить",
"private_key_in_memo": "Не пишите в этом поле приватные ключи!",
"verified_exchange_no_memo": "Для перевода токенов на этот аккаунт нужно указать заметку/memo.",
"verified_exchange_liquid_only": "Перевод токенов на этот аккаунт с TIP-баланса невозможен, используйте основной баланс."
"verified_exchange_liquid_only": "Перевод токенов на этот аккаунт с TIP-баланса невозможен, используйте основной баланс.",
"gift_nft": "Подарить NFT"
},
"user_profile": {
"unknown_account": "Неизвестный аккаунт",
Expand Down
43 changes: 43 additions & 0 deletions app/redux/FetchDataSaga.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { call, put, select, fork, cancelled, takeLatest, takeEvery } from 'redux-saga/effects';
import cookie from "react-cookie";
import {config, api} from 'golos-lib-js';
import { Asset } from 'golos-lib-js/lib/utils'

import { getPinnedPosts, getMutedInNew } from 'app/utils/NormalizeProfile';
import {loadFollows, fetchFollowCount} from 'app/redux/FollowSaga';
Expand All @@ -13,6 +14,7 @@ import session from 'app/utils/session'
import { getFilterApps, } from 'app/utils/ContentAccess';
import { reveseTag, getFilterTags } from 'app/utils/tags';
import { PUBLIC_API, CATEGORIES, SELECT_TAGS_KEY, DEBT_TOKEN_SHORT, LIQUID_TICKER } from 'app/client_config';
import { parseNFTImage, NFTImageStub } from 'app/utils/NFTUtils'
import { getSubs, notifyGetViews, } from 'app/utils/NotifyApiClient'
import { SearchRequest, searchData, stateSetVersion } from 'app/utils/SearchClient'
import { hashPermlink, } from 'app/utils/StateFunctions'
Expand All @@ -27,6 +29,7 @@ export function* fetchDataWatches () {
yield fork(watchFetchExchangeRates);
yield fork(watchFetchVestingDelegations);
yield fork(watchFetchUiaBalances);
yield fork(watchFetchNftTokens)
}

export function* watchGetContent() {
Expand Down Expand Up @@ -842,3 +845,43 @@ export function* fetchUiaBalances({ payload: { account } }) {
console.error('fetchUiaBalances', err)
}
}

export function* watchFetchNftTokens() {
yield takeLatest('global/FETCH_NFT_TOKENS', fetchNftTokens)
}

export function* fetchNftTokens({ payload: { account } }) {
try {
const nft_tokens = yield call([api, api.getNftTokensAsync], {
owner: account
})

const syms = new Set()

let nft_assets

try {
for (const no of nft_tokens) {
no.image = parseNFTImage(no.json_metadata) || NFTImageStub()

const price = Asset(no.last_buy_price)
syms.add(price.symbol)
}

nft_assets = {}
if (syms.size) {
const assets = yield call([api, api.getAssets], '', [...syms])
for (const a of assets) {
const supply = Asset(a.supply)
nft_assets[supply.symbol] = a
}
}
} catch (err) {
console.error(err)
}

yield put(GlobalReducer.actions.receiveNftTokens({nft_tokens, nft_assets}))
} catch (err) {
console.error('fetchNftTokens', err)
}
}
13 changes: 13 additions & 0 deletions app/redux/GlobalReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,19 @@ export default createModule({
return state.set('assets', fromJS(assets))
},
},
{
action: 'FETCH_NFT_TOKENS',
reducer: state => state,
},
{
action: 'RECEIVE_NFT_TOKENS',
reducer: (state, { payload: { nft_tokens, nft_assets } }) => {
let new_state = state.set('nft_tokens', fromJS(nft_tokens))
if (nft_assets)
new_state = new_state.set('nft_assets', fromJS(nft_assets))
return new_state
},
},
{
action: 'LINK_REPLY',
reducer: (state, { payload: op }) => {
Expand Down
4 changes: 4 additions & 0 deletions app/redux/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const defaultState = fromJS({
show_login_modal: false,
show_transfer_modal: false,
show_donate_modal: false,
show_nft_gift_modal: false,
show_convert_assets_modal: false,
show_promote_post_modal: false,
show_signup_modal: false,
Expand Down Expand Up @@ -71,6 +72,9 @@ export default createModule({
{ action: 'SHOW_DONATE', reducer: state => state.set('show_donate_modal', true) },
{ action: 'HIDE_DONATE', reducer: state => state.set('show_donate_modal', false) },
{ action: 'SET_DONATE_DEFAULTS', reducer: (state, {payload}) => state.set('donate_defaults', fromJS(payload)) },
{ action: 'SHOW_GIFT_NFT', reducer: state => state.set('show_gift_nft_modal', true) },
{ action: 'HIDE_GIFT_NFT', reducer: state => state.set('show_gift_nft_modal', false) },
{ action: 'SET_GIFT_NFT_DEFAULTS', reducer: (state, {payload}) => state.set('gift_nft_defaults', fromJS(payload)) },
{ action: 'SHOW_CONVERT_ASSETS', reducer: state => state.set('show_convert_assets_modal', true) },
{ action: 'HIDE_CONVERT_ASSETS', reducer: state => state.set('show_convert_assets_modal', false) },
{ action: 'SET_CONVERT_ASSETS_DEFAULTS', reducer: (state, {payload}) => state.set('convert_assets_defaults', fromJS(payload)) },
Expand Down
12 changes: 12 additions & 0 deletions app/utils/NFTUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

export function parseNFTImage(json_metadata) {
if (json_metadata) {
const meta = JSON.parse(json_metadata)
if (meta) return meta.image
}
return null
}

export function NFTImageStub() {
return require('app/assets/images/nft.png')
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"foundation-sites": "^6.4.3",
"fs-extra": "^10.0.1",
"git-rev-sync": "^3.0.2",
"golos-lib-js": "^0.9.55",
"golos-lib-js": "^0.9.56",
"history": "^2.0.0-rc2",
"immutable": "^3.8.2",
"intl": "^1.2.5",
Expand Down
Loading

0 comments on commit 438d2f7

Please sign in to comment.