어제 Token의 임대 정보를 보여주는 기능을 만들어서 포스팅을 하였습니다.
그런데 만들고 나서 보니 현재 Pending Token이 있는 Token들을 보여주고 바로 Claim을 할 수 있으면 좋겠다는 생각이 들었습니다.
그래서 바로 기능을 추가하였습니다.
* Pending Token 보여주기
만약 가지고 있는 Token 중에 Claim 해야할 Pending Token이 있다면 빨간색으로 표시한 후
현재 보유 수량 + Pending Token 양
으로 표시하게끔 변경하였습니다.
1 ) 기본적으로 다음으로 포멧으로 Token의 수량이 표시됩니다. 스테이킹, 임대를 주거나 받은 것이 없으면 표시하지 않도록 소스를 수정하였습니다.
Token 이름 _ 현재보유수량 _ Pending 수량(Claim 가능 수량)
2 ) 스테이킹이나 임대주거나 받은 것이 있다면 다음과 같이 표시 됩니다.
Token 이름 _ 현재보유수량 _ Pending 수량(Claim 가능 수량) _ (스테이킹수량 _ + 임대받은 수량 _ - 임대 준 수량)
- Pending 수량이 있으면 빨간색으로 표시되어 버튼을 클릭하면 바로 Claim을 할 수 있습니다.
Claim이 완료되고 나면 결과에 대해 현재는 별다른 표시가 없지만 관련해서는 조금 더 수정을 해야할 듯 합니다.
화면으로 보여드리면 "와 신박하다" 하실텐데 그 점이 조금 아쉽네요.
1. steemApi.js
Token의 pending 수량을 알기 위해 Status 값과 수량의 계산을 위해 precision이 필요하여 info 값을 추가로 저장하였습니다.
const account = urlParts[1];
const [
tokenBalances,
tokenUnstakes,
tokenStatuses,
transferHistory,
allTokenBalances,
allTokenInfo,
] = await Promise.all([
ssc.findOne('tokens', 'balances', {
account,
symbol: LIQUID_TOKEN_UPPERCASE,
}),
ssc.findOne('tokens', 'pendingUnstakes', {
account,
symbol: LIQUID_TOKEN_UPPERCASE,
}),
getScotAccountDataAsync(account),
getSteemEngineAccountHistoryAsync(account),
ssc.find('tokens', 'balances', {
account,
}),
getScotDataAsync('info', {}),
]);
if (tokenBalances) {
state.accounts[account].token_balances = tokenBalances;
}
if (tokenUnstakes) {
state.accounts[account].token_unstakes = tokenUnstakes;
}
if (tokenStatuses && tokenStatuses[LIQUID_TOKEN_UPPERCASE]) {
state.accounts[account].token_status = tokenStatuses[LIQUID_TOKEN_UPPERCASE];
state.accounts[account].all_token_status = tokenStatuses;
}
if (transferHistory) {
// Reverse to show recent activity first
state.accounts[
account
].transfer_history = transferHistory.reverse();
}
if (allTokenBalances) {
state.accounts[account].all_token_balances = allTokenBalances;
}
if(allTokenInfo){
state.accounts[account].all_token_info = allTokenInfo;
}
2 . UserWallet.jsx
Token을 표시하는 부분에서 Pending Token 정보를 추가하였고 버튼 클릭 시 Claim 되도록 함수를 추가하였습니다.
if(account.has('all_token_balances'))
{
const allTokenBalances = account.get('all_token_balances').toJS();
const allTokenStatus = account.get('all_token_status');
const allTokenInfo = account.get('all_token_info');
// added by realmankwon (2019-06-18) sort by alphabet asc
allTokenBalances.sort(
(a, b) =>
a.symbol >
b.symbol
? 1
: -1
);
for (let v = 0; v < allTokenBalances.length; ++v) {
const tokenBalance = allTokenBalances[v];
// added by realmankwon (2019-06-18) except LIQUID_TOKEN_UPPERCASE
if(tokenBalance.symbol === LIQUID_TOKEN_UPPERCASE) continue;
const pendingToken = allTokenStatus.getIn([tokenBalance.symbol, 'pending_token']);
const tokenPrecision = allTokenInfo.getIn([tokenBalance.symbol, 'precision']);
let tokenReward = 0;
if(pendingToken && tokenPrecision ){
tokenReward = pendingToken / Math.pow(10, tokenPrecision);
}
const tokenFormattedStr =
<FormattedAssetToken
balance={tokenBalance.balance}
stake={tokenBalance.stake}
delegationsIn={tokenBalance.delegationsIn}
delegationsOut={tokenBalance.delegationsOut}
reward={tokenReward}
symbol={tokenBalance.symbol}
/>;
const tokenBalanceStr = isMyAccount && tokenReward > 0
? <span key={tokenBalance.symbol} onClick = {e => {
this.handleClaimTokenRewards(account, tokenBalance.symbol);
}}>
{tokenFormattedStr}
</span>
: <span key={tokenBalance.symbol} >
{tokenFormattedStr}
</span>;
all_token_balances_list.push(
tokenBalanceStr
);
}
이 부분이 Claim을 처리하는 함수 부분입니다.
handleClaimTokenRewards = (account, token) => {
this.props.claimTokenRewards(account, token);
};
claimTokenRewards: (account, token) => {
const username = account.get('name');
const successCallback = () => {
dispatch(
globalActions.getState({ url: `@${username}/transfers` })
);
};
const operation = {
id: 'scot_claim_token',
required_posting_auths: [username],
json: JSON.stringify({
symbol: token,
}),
};
dispatch(
transactionActions.broadcastOperation({
type: 'custom_json',
operation,
successCallback,
})
);
},
3 . FormattedAssetToken.jsx
const FormattedAssetToken = ({ balance, stake, delegationsIn, delegationsOut, reward, symbol }) => {
if (balance && typeof balance === 'string') {
balance = parsePayoutAmount(balance);
}
const bal = formatDecimal(balance);
let isStake = false;
let isDelegationIn = false;
let isDelegationOut = false;
let isReward = false;
if (stake && typeof stake === 'string') {
stake = parsePayoutAmount(stake);
if(stake > 0)
isStake = true;
}
const stk = formatDecimal(stake);
if (delegationsIn && typeof delegationsIn === 'string') {
delegationsIn = parsePayoutAmount(delegationsIn);
if(delegationsIn > 0)
isDelegationIn = true;
}
const dIn = formatDecimal(delegationsIn || 0) ;
if (delegationsOut && typeof delegationsOut === 'string') {
delegationsOut = parsePayoutAmount(delegationsOut);
if(delegationsOut > 0)
isDelegationOut = true;
}
const dOut = formatDecimal(delegationsOut || 0) ;
if(reward > 0)
isReward = true;
const rew = formatDecimal(reward || 0) ;
const tokenClassName = isReward ? `FormattedAssetTokenReward` : `FormattedAssetToken`;
return isStake || isDelegationIn || isDelegationOut ? (
<span className={tokenClassName}>
<span className="prefix">{symbol} </span>
<span className="integer">{bal[0]}</span>
<span className="decimal">{bal[1]}</span>
{ isReward && ` + `}
{ isReward &&
<span className="integer">{rew[0]}</span>
}
{ isReward &&
<span className="decimal">{rew[1]}</span>
}
{'('}
<span className="integer">{stk[0]}</span>
<span className="decimal">{stk[1]}</span>{', +'}
<span className="integer">{dIn[0]}</span>
<span className="decimal">{dIn[1]}</span>{', -'}
<span className="integer">{dOut[0]}</span>
<span className="decimal">{dOut[1]}</span>{')'}
</span>
) : (
<span className={tokenClassName}>
<span className="prefix">{symbol} </span>
<span className="integer">{bal[0]}</span>
<span className="decimal">{bal[1]}</span>
{ isReward && ` + `}
{ isReward &&
<span className="integer">{rew[0]}</span>
}
{ isReward &&
<span className="decimal">{rew[1]}</span>
}
</span>
);
});