Skip to content

Commit

Permalink
Merge pull request #584 from chatch/claimable-balance-views
Browse files Browse the repository at this point in the history
Claimable balance views
  • Loading branch information
chatch authored Jan 20, 2024
2 parents 4403d35 + 3c2c0a3 commit f211b34
Show file tree
Hide file tree
Showing 20 changed files with 588 additions and 15 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ module.exports = {
files: ['*.ts', '*.tsx', '*.js', 'json'],
parser: '@typescript-eslint/parser',
},
{
files: ['e2e/*.spec.ts'],
rules: {
// NOTE: testing-library and playwright have the same functions (e.g. getByRole), so disable rules for testing-library.
'testing-library/prefer-screen-queries': 'off',
},
},
],
rules: {
'react/jsx-wrap-multilines': 'off',
Expand Down
29 changes: 21 additions & 8 deletions app/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import { useIntl } from 'react-intl'
import { searchStrToPath } from './lib/search'
import { isSecretKey } from './lib/stellar/utils'
import { useState } from 'react'
import { useNavigate } from '@remix-run/react'
import { useLocation, useNavigate } from '@remix-run/react'

import searchSvg from '../public/search.svg'
import infoCircleSvg from '../public/info-circle.svg'
import { isPathClaimableBalance } from './lib/utilities'

const HelpModal = ({
showHelp,
Expand Down Expand Up @@ -143,15 +144,28 @@ export default function SearchBox() {
const [showHelp, setShowHelp] = useState(false)

const navigate = useNavigate()
const location = useLocation()

const placeHolderI18nId = isPathClaimableBalance(location.pathname)
? 'search.placeHolder.claimableBalance'
: 'search.placeHolder'

const showMessage = () => {
return isPathClaimableBalance(location.pathname) ? (
<p>{formatMessage({ id: 'search.message.claimableBalance' })}</p>
) : (
''
)
}

const handleCloseFn = () => setShowHelp(false)
const handleClickFn = () => setShowHelp(true)

const searchHandler = (event: any) => {
const searchHandler = (event: any, pathName: string) => {
console.log(`searchHandler entry [${searchStr}]`)
event.preventDefault()

const matchPath = searchStrToPath(searchStr)
const matchPath = searchStrToPath(searchStr, pathName)
console.log(`matchPath ${matchPath}`)

// #62 security: clear search box if user put the secret key there
Expand All @@ -168,21 +182,19 @@ export default function SearchBox() {
<Container>
<Row>
<Col id="search-container">
<form onSubmit={searchHandler}>
<form onSubmit={(event) => searchHandler(event, location.pathname)}>
<InputGroup>
<FormControl
type="text"
onChange={(e) => setSearchStr(e.target.value)}
placeholder={formatMessage({
id: 'search.placeHolder',
})}
placeholder={formatMessage({ id: placeHolderI18nId })}
value={searchStr}
/>
<InputGroup.Text>
<img
src={searchSvg}
style={{ color: '#4c5667', height: 16, width: 16 }}
onClick={searchHandler}
onClick={(event) => searchHandler(event, location.pathname)}
/>
</InputGroup.Text>
<InputGroup.Text
Expand All @@ -206,6 +218,7 @@ export default function SearchBox() {
<HelpModal handleCloseFn={handleCloseFn} showHelp={showHelp} />
)}
</Col>
{showMessage()}
</Row>
</Container>
)
Expand Down
92 changes: 92 additions & 0 deletions app/components/ClaimableBalanceTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { ClaimableBalanceProps } from './operations/ClaimableBalances'
import { FormattedDate, FormattedMessage, FormattedTime } from 'react-intl'

interface ClaimableBalanceTableProps {
records: ReadonlyArray<ClaimableBalanceProps>
horizonURL?: string
isClaimant: boolean
}

interface ClaimableBalanceRowProps extends ClaimableBalanceProps {
idx: number
}

const shortenString = (str: string): string => {
return `${str.substring(0, 4)}...${str.substring(str.length - 4)}`
}

const getAsset = (asset: string) => {
const [assetName, assetId] = asset.split(':')
return { assetName, assetId }
}

export const ClaimableBalanceRow = ({
id,
idx,
amount,
sponsor,
asset,
lastModifiedTime,
}: ClaimableBalanceRowProps) => {
const { assetName } = getAsset(asset)
return (
<tr>
<td>{idx + 1}</td>
<td className="d-flex">
<span>{parseFloat(amount)}</span>
<p className="text-orange px-2">{assetName}</p>
</td>
<td>
<a href={`/account/${sponsor}`}>{shortenString(sponsor)}</a>
</td>
<td>
<a href={`/claimable-balance/${id}`}>{shortenString(id)}</a>
</td>
<td>
<FormattedDate value={lastModifiedTime} />
&nbsp;
<FormattedTime value={lastModifiedTime} />
</td>
</tr>
)
}

export default function ClaimableBalanceTable({
records,
isClaimant,
}: ClaimableBalanceTableProps) {
return (
<table className="table table-striped table-bordered table-sm">
<thead>
<tr>
<th>#</th>
<th>
<FormattedMessage id="amount" />
</th>
<th>
{isClaimant ? (
<FormattedMessage id="sponsor" />
) : (
<FormattedMessage id="claimant" />
)}
</th>
<th>
<FormattedMessage id="balance" />
</th>
<th>
<FormattedMessage id="updated" />
</th>
</tr>
</thead>
{records.length ? (
<tbody>
{records.map((record, idx) => (
<ClaimableBalanceRow key={record.id} idx={idx} {...record} />
))}
</tbody>
) : (
'No claimable balances found.'
)}
</table>
)
}
5 changes: 5 additions & 0 deletions app/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export default function Header({
<FormattedMessage id="liquidity-pools" />
</NavDropdown.Item>
</LinkContainer>
<LinkContainer to="/claimable-balances">
<NavDropdown.Item>
<FormattedMessage id="claimable-balances" />
</NavDropdown.Item>
</LinkContainer>
</NavDropdown>
</Nav>
<Nav className="ms-auto">
Expand Down
12 changes: 12 additions & 0 deletions app/components/operations/ClaimableBalances.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import type { PropsWithChildren } from 'react'
import AccountLink from '../shared/AccountLink'
import Asset from '../shared/Asset'
import type { HorizonApi } from 'stellar-sdk/lib/horizon'

export interface ClaimableBalanceProps extends PropsWithChildren {
id: string
pagingToken: string
asset: string
amount: string
sponsor: string
claimants: HorizonApi.Claimant[]
lastModifiedTime: string
}

const CreateClaimableBalanceOperation = ({ amount, sponsor, asset }: any) => {
const [assetCode, assetIssuer] = asset.split(':')
Expand Down
14 changes: 13 additions & 1 deletion app/lib/__tests__/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,30 @@ describe('searchStrToPath', () => {
expect(
searchStrToPath(
'GDCX3FBHR7IKHSDFDCA4XY65NF6B2WMF5WR67FIXN5JXURJ3YDGSU2BS',
'/operations', // should be other than '/claimable-balance'
),
).toEqual(
'/account/GDCX3FBHR7IKHSDFDCA4XY65NF6B2WMF5WR67FIXN5JXURJ3YDGSU2BS',
)
})

it('returns /account for federated addresses', () => {
expect(searchStrToPath('steexp*fed.network')).toEqual(
expect(searchStrToPath('steexp*fed.network', '/operations')).toEqual(
'/account/steexp*fed.network',
)
})

it('returns /claimable-balances when path starts with /claimable-balance', () => {
expect(
searchStrToPath(
'GDCX3FBHR7IKHSDFDCA4XY65NF6B2WMF5WR67FIXN5JXURJ3YDGSU2BS',
'/claimable-balance',
),
).toEqual(
'/claimable-balances/GDCX3FBHR7IKHSDFDCA4XY65NF6B2WMF5WR67FIXN5JXURJ3YDGSU2BS',
)
})

it('returns /account/<issuer> for matching asset codes', () => {
expect(searchStrToPath('MOBI')).toEqual('/asset/MOBI')
expect(searchStrToPath('JPY')).toEqual('/asset/JPY')
Expand Down
7 changes: 7 additions & 0 deletions app/lib/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"bought": "Bought",
"buy": "Buy",
"buyer": "Buyer",
"claimable-balances": "Claimable Balances",
"claimable-balance": "Claimable Balance",
"claimant": "Claimant",
"code": "Code",
"contact.address.label": "Address",
"contract": "Contract",
Expand Down Expand Up @@ -153,6 +156,8 @@
"remove": "Remove",
"save": "Save",
"search.placeHolder": "Search by Account / Transaction / Contract / ...",
"search.placeHolder.claimableBalance": "Search by Account",
"search.message.claimableBalance": "Enter Stellar address to check claimable balances",
"sell": "Sell",
"seller": "Seller",
"sell.offers": "Sell offers",
Expand All @@ -168,6 +173,7 @@
"signing": "Signing",
"sold": "Sold",
"source.account": "Source",
"sponsor": "Sponsor",
"stellar.address": "Stellar Address",
"stellar": "Stellar",
"subentry.count": "Subentry Count",
Expand All @@ -189,6 +195,7 @@
"trust": "Trust",
"trust.limit": "Trust Limit",
"type": "Type",
"updated-at": "Updated",
"value": "Value",
"version": "Version",
"view.all": "View All",
Expand Down
9 changes: 8 additions & 1 deletion app/lib/languages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"bought": "購入済み",
"buy": "購入",
"buyer": "バイヤー",
"claimable-balances": "請求可能残高",
"claimable-balance": "請求可能残高",
"claimant": "請求者",
"code": "コード",
"contact.address.label": "アドレス",
"created": "作成日",
Expand Down Expand Up @@ -138,7 +141,9 @@
"recipient": "受領者",
"remove": "外す",
"save": "保存",
"search.placeHolder": "口座/取引/アンカー/...で検索",
"search.placeHolder": "アカウント/取引/アンカー/...で検索",
"search.placeHolder.claimableBalance": "アカウントで検索",
"search.message.claimableBalance": "請求可能残高を検索するために、アドレスを入力して下さい",
"sell": "売る",
"seller": "売手",
"sell.offers": "売るオファー",
Expand All @@ -154,6 +159,7 @@
"signing": "署名",
"sold": "売約済み",
"source.account": "ソース",
"sponsor": "スポンサー",
"stellar.address": "Stellar アドレス",
"stellar": "Stellar",
"subentry.count": "サブエントリ回数",
Expand All @@ -175,6 +181,7 @@
"trust": "信頼",
"trust.limit": "信託制限",
"type": "タイプ",
"updated-at": "更新日時",
"value": "",
"version": "バージョン",
"view.all": "全てを表示",
Expand Down
1 change: 1 addition & 0 deletions app/lib/loader-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type ServerReqFnName =
| 'trades'
| 'transactions'
| 'liquidityPools'
| 'claimableBalances'

export function horizonRecordsLoader<RecordsType>(
serverReqFnName: ServerReqFnName,
Expand Down
9 changes: 7 additions & 2 deletions app/lib/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isTxHash,
} from './stellar/utils'
import directory from '../data/directory'
import { isPathClaimableBalance } from './utilities'

const { anchors, assets } = directory

Expand All @@ -36,7 +37,7 @@ const searchAnchorName = (name) =>
lcIncludes(anchors[key].displayName, name),
)

const searchStrToPath = (searchStr) => {
const searchStrToPath = (searchStr, pathName) => {
if (!isString(searchStr) || searchStr.trim() === '') return null

const str = searchStr.trim()
Expand All @@ -46,7 +47,11 @@ const searchStrToPath = (searchStr) => {
// see also stellar-base new Contract(address or bytes)
//
if (isPublicKey(str) || isFederatedAddress(str) || isMuxedAddress(str)) {
return `/account/${str}`
if (isPathClaimableBalance(pathName)) {
return `/claimable-balances/${str}`
} else {
return `/account/${str}`
}
} else if (isTxHash(str)) {
return `/tx/${str}`
} else if (isContractAddress(str)) {
Expand Down
Loading

0 comments on commit f211b34

Please sign in to comment.