Skip to content

Commit

Permalink
Show multi-asset in transaction history and send
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidTranDucVL committed Feb 24, 2021
1 parent c6c3b73 commit 137cf5b
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 23 deletions.
91 changes: 91 additions & 0 deletions app/frontend/components/common/searchableSelect.tsx
@@ -0,0 +1,91 @@
import {h} from 'preact'
import {connect} from '../../helpers/connect'
import actions from '../../actions'
import {State} from '../../state'
import {useEffect, useRef, useState} from 'preact/hooks'

interface Props<T> {
label: string
defaultItem: T
items: T[]
displaySelectedItem: (t: T) => string
displaySelectedItemClassName: string
displayItem: (t: T) => any
onSelect: (t: T) => void
searchPredicate: (query: string, t: T) => boolean
searchPlaceholder: string
}

// <T extends {}> is workaround for <T> being recognized as JSX element instead of generics
const SearchableSelect = <T extends {}>({
label,
defaultItem,
items,
displaySelectedItem,
displaySelectedItemClassName,
displayItem,
onSelect,
searchPredicate,
searchPlaceholder,
}: Props<T>) => {
const inputEl = useRef<HTMLInputElement>(null)
const dropdownEl = useRef<HTMLDivElement>(null)
const [visible, setVisible] = useState(false)
const [value, setValue] = useState(defaultItem)
const [search, setSearch] = useState('')
const shouldShowItem = (item: T) => searchPredicate(search, item)
const showDropdown = (bool: boolean) => {
setVisible(bool)
setSearch('')
if (bool) {
inputEl.current.focus()
}
}

useEffect(() => {
dropdownEl.current.scrollTop = 0
})

return (
<div className="searchable-select-wrapper" onMouseLeave={() => showDropdown(false)}>
<div className="searchable-select-label">{label}</div>
<div
className={`searchable-select ${displaySelectedItemClassName}`}
onMouseUp={() => showDropdown(!visible)}
>
{displaySelectedItem(value)}
</div>
<div ref={dropdownEl} className={`searchable-select-dropdown ${visible ? '' : 'hide'}`}>
<input
ref={inputEl}
type="text"
className="searchable-select-input"
value={search}
onInput={(event: any) => setSearch(event.target.value)}
placeholder={searchPlaceholder}
/>
<div>
{items &&
items.map((item, i) => (
<div
className={`searchable-select-item ${shouldShowItem(item) ? '' : 'hide'}`}
key={i}
onMouseUp={() => {
setVisible(false)
setValue(item)
onSelect(item)
}}
>
{displayItem(item)}
</div>
))}
</div>
</div>
</div>
)
}

export default connect(
(state: State) => ({}),
actions
)(SearchableSelect)
7 changes: 7 additions & 0 deletions app/frontend/components/common/svg.tsx
Expand Up @@ -295,6 +295,12 @@ const DropdownCaret = () => (
</svg>
)

const StarIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#E72076" viewBox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.283.95l-3.523 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z" />
</svg>
)

export {
LedgerLogoWhite,
TrezorLogoWhite,
Expand All @@ -310,4 +316,5 @@ export {
RefreshIcon,
DownloadIcon,
DropdownCaret,
StarIcon,
}
2 changes: 1 addition & 1 deletion app/frontend/components/pages/dashboard/dashboardPage.tsx
Expand Up @@ -79,7 +79,7 @@ const SendingPage = ({
</div>
) : (
<div className="dashboard desktop">
<div className="dashboard-column">
<div className="dashboard-column shrinkable">
<Balance />
<TransactionHistory />
</div>
Expand Down
78 changes: 77 additions & 1 deletion app/frontend/components/pages/sendAda/sendAdaPage.tsx
Expand Up @@ -10,12 +10,16 @@ import DonateThanksModal from './donateThanksModal'
import DonationButtons from './donationButtons'
import CustomDonationInput from './customDonationInput'
import Conversions from '../../common/conversions'
import SearchableSelect from '../../common/searchableSelect'
import {ADALITE_CONFIG} from '../../../config'
import {toCoins} from '../../../helpers/adaConverters'

import tooltip from '../../common/tooltip'
import AccountDropdown from '../accounts/accountDropdown'
import {getSourceAccountInfo, State} from '../../../state'
import {useCallback} from 'preact/hooks'
import {Lovelace} from '../../../../frontend/types'
import {StarIcon} from '../../common/svg'

const {ADALITE_MIN_DONATION_VALUE} = ADALITE_CONFIG

Expand Down Expand Up @@ -68,6 +72,58 @@ interface Props {
switchSourceAndTargetAccounts: any
}

type MultiAsset = {
name: string
hash?: string
amount: number
star: boolean
}

// mock
const multiAssets: MultiAsset[] = [
{
name: 'ADA',
amount: 10000000,
star: true,
},
{
name: 'Testcoin',
hash: '95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39',
amount: 10000000,
star: true,
},
{
name: 'Testcoin 2',
hash: '95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39',
amount: 10000000,
star: false,
},
...[...Array(43).keys()].map((i) => ({
name: `Random coin ${i}`,
hash: [...Array(56).keys()].map(() => Math.floor(Math.random() * 15).toString(16)).join(''),
amount: Math.random() * 100000000,
star: Math.random() < 0.5,
})),
]

const showMultiAsset = ({star, name, hash, amount}: MultiAsset) => (
<div className="multi-asset-item">
<div className="multi-asset-name-amount">
<div className="multi-asset-name">
{star && <StarIcon />}
{name}
</div>
<div className="multi-asset-amount">{printAda(Math.abs(amount) as Lovelace)}</div>
</div>
{hash && (
<div className="multi-asset-hash">
<span className="ellipsis">{hash.slice(0, -6)}</span>
<span>{hash.slice(-6)}</span>
</div>
)}
</div>
)

class SendAdaPage extends Component<Props> {
amountField: HTMLInputElement
submitTxBtn: HTMLButtonElement
Expand Down Expand Up @@ -116,6 +172,13 @@ class SendAdaPage extends Component<Props> {
await confirmTransaction('send')
}

const searchPredicate = useCallback(
(query: string, multiAsset: MultiAsset): boolean =>
multiAsset.name.toLowerCase().includes(query.toLowerCase()) ||
(multiAsset.hash && multiAsset.hash.toLowerCase().includes(query.toLowerCase())),
[]
)

return (
<div className="send card">
<h2 className={`card-title ${isModal ? 'show' : ''}`}>{title}</h2>
Expand Down Expand Up @@ -145,6 +208,19 @@ class SendAdaPage extends Component<Props> {
<AccountDropdown accountIndex={targetAccountIndex} setAccountFunc={setTargetAccount} />
</div>
)}
<SearchableSelect
label="Select asset"
defaultItem={multiAssets[0]}
displaySelectedItem={(multiAsset: MultiAsset) => `${multiAsset.name}`}
displaySelectedItemClassName="input"
items={multiAssets}
displayItem={(multiAsset: MultiAsset) => showMultiAsset(multiAsset)}
onSelect={() => {
return
}}
searchPredicate={searchPredicate}
searchPlaceholder={`Search from ${multiAssets.length} tokens by name or hash`}
/>
<div className="send-values">
<label className="ada-label amount" htmlFor={`${isModal ? 'account' : ''}send-amount`}>
Amount
Expand Down Expand Up @@ -241,7 +317,7 @@ class SendAdaPage extends Component<Props> {
SendAdaPage.defaultProps = {
showDonationFields: true,
isModal: false,
title: 'Send ADA',
title: 'Send',
}

export default connect(
Expand Down
56 changes: 49 additions & 7 deletions app/frontend/components/pages/txHistory/transactionHistory.tsx
@@ -1,4 +1,4 @@
import {h} from 'preact'
import {h, Fragment} from 'preact'
import printAda from '../../../helpers/printAda'
import {getActiveAccountInfo, State} from '../../../state'
import {ADALITE_CONFIG} from '../../../config'
Expand All @@ -14,6 +14,7 @@ import {
Lovelace,
TxSummaryEntry,
} from '../../../types'
import {StarIcon} from '../../common/svg'
import moment = require('moment')

const FormattedAmount = ({amount}: {amount: Lovelace}): h.JSX.Element => {
Expand All @@ -28,7 +29,7 @@ const FormattedAmount = ({amount}: {amount: Lovelace}): h.JSX.Element => {

const FormattedFee = ({fee}: {fee: Lovelace}): h.JSX.Element => {
const value = printAda(fee)
return <div className="transaction-fee">{`Fee: ${value}`}</div>
return <div className="transaction-fee nowrap">{`Fee: ${value}`}</div>
}

const FormattedTransaction = ({txid}: {txid: HexString}): h.JSX.Element => (
Expand Down Expand Up @@ -88,6 +89,31 @@ const FormattedTransaction = ({txid}: {txid: HexString}): h.JSX.Element => (
</div>
)

type MultiAssetProps = {
star: boolean
name: string
hash: string
amount: number
}

const MultiAsset = ({star, name, hash, amount}: MultiAssetProps) => (
<Fragment>
<div className="row">
<div className="multi-asset-name">
{star && <StarIcon />}
{name}
</div>
<div className={`multi-asset-amount ${amount > 0 ? 'credit' : 'debit'}`}>
{printAda(Math.abs(amount) as Lovelace)}
</div>
</div>
<div className="multi-asset-hash">
<span className="ellipsis">{hash.slice(0, -6)}</span>
{hash.slice(-6)}
</div>
</Fragment>
)

interface Props {
transactionHistory: Array<TxSummaryEntry>
stakingHistory: Array<StakingHistoryObject>
Expand Down Expand Up @@ -222,12 +248,28 @@ const TransactionHistory = ({transactionHistory, stakingHistory}: Props): h.JSX.
<ul className="transactions-content">
{transactionHistory.map((transaction: TxSummaryEntry) => (
<li key={transaction.ctbId} className="transaction-item">
<div className="transaction-date">
{toLocalDate(new Date(transaction.ctbTimeIssued * 1000))}
<div className="row">
<div className="transaction-date">
{toLocalDate(new Date(transaction.ctbTimeIssued * 1000))}
</div>
<FormattedAmount amount={transaction.effect} />
</div>
<div className="row">
<FormattedTransaction txid={transaction.ctbId} />
<FormattedFee fee={transaction.fee} />
</div>
<FormattedAmount amount={transaction.effect} />
<FormattedTransaction txid={transaction.ctbId} />
<FormattedFee fee={transaction.fee} />
<MultiAsset
star
name="Testcoin"
hash="95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39"
amount={transaction.effect} // mock, just to show something
/>
<MultiAsset
star={false}
name="Testcoin 2"
hash="95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39"
amount={transaction.effect} // mock, just to show something
/>
</li>
))}
</ul>
Expand Down
12 changes: 0 additions & 12 deletions app/public/css/0-767px.css
Expand Up @@ -656,18 +656,6 @@
align-self: center;
}

.transaction-amount {
order: 2;
}

.transaction-date {
order: 3;
}

.transaction-fee {
order: 4;
}

/* TOOLTIP */

[data-balloon]::after {
Expand Down

0 comments on commit 137cf5b

Please sign in to comment.