Skip to content

Commit

Permalink
Updated abbreviate values component (#267)
Browse files Browse the repository at this point in the history
* updated abbreviate values component

* sync with latest from interceptor
  • Loading branch information
jubalm authored Jun 6, 2024
1 parent 1e87a9b commit 9bbf442
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 33 deletions.
39 changes: 16 additions & 23 deletions app/ts/components/AbbreviatedValue.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
export const AbbreviatedValue = ({ floatValue }: { floatValue: number }) => {
const prefixes = [
{ value: 1e9, symbol: 'G' },
{ value: 1e6, symbol: 'M' },
{ value: 1e3, symbol: 'k' },
];
import type { ComponentChild } from 'preact'
import { bigintToNumberFormatParts } from '../library/bigint.utils.js'

for (const prefix of prefixes) {
if (floatValue >= prefix.value) {
return <>{toFixedLengthDigits(floatValue / prefix.value) + prefix.symbol}</>
}
}
export const AbbreviatedValue = ({ amount, decimals = 18n }: { amount: bigint, decimals?: bigint }) => {
const numberParts = bigintToNumberFormatParts(amount, decimals)
const domElement: ComponentChild[] = []

// if value is a fraction of 1
if (floatValue && floatValue % 1 === floatValue) {
const [coefficient, exponent] = floatValue.toExponential().split('e')
const leadingZerosCount = Math.abs(parseInt(exponent)) - 1
const significantDigits = coefficient.replace('.', '')
return <>0.<small>{'0'.repeat(leadingZerosCount)}</small>{significantDigits}</>
for (const [type, value] of numberParts) {
if (type === 'fraction') {
const significantDigits = `${ Number(value) }`
const zeroPad = value.replace(significantDigits, '')
if (zeroPad.length) {
domElement.push(<><small>{ zeroPad }</small>{ significantDigits }</>)
continue
}
}
domElement.push([value])
}

return <>{toFixedLengthDigits(floatValue)}</>
}

function toFixedLengthDigits(num: number, max: number = 5) {
const formatter = new Intl.NumberFormat('en-US', { maximumSignificantDigits: max, useGrouping: false })
return formatter.format(num)
return <>{ domElement }</>
}
9 changes: 2 additions & 7 deletions app/ts/components/TokenPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { batch, Signal, useComputed, useSignal, useSignalEffect } from '@preact/signals'
import { Contract, formatEther, formatUnits } from 'ethers'
import { Contract } from 'ethers'
import { useRef } from 'preact/hooks'
import { useTokenManager } from '../context/TokenManager.js'
import { useTransfer } from '../context/Transfer.js'
Expand Down Expand Up @@ -180,12 +180,7 @@ const AssetBalance = ({ token }: { token?: ERC20Token }) => {
case 'rejected':
return <div>error</div>
case 'resolved':
const stringValue = token ? formatUnits(query.value.value, token.decimals) : formatEther(query.value.value)
const symbol = token ? token.symbol : 'ETH'
const numericValue = parseFloat(stringValue)
return (
<><AbbreviatedValue floatValue={numericValue} /> {symbol}</>
)
return <><AbbreviatedValue amount={query.value.value} decimals={token?.decimals} /> {token ? token.symbol : 'ETH'}</>
}
}

Expand Down
5 changes: 2 additions & 3 deletions app/ts/components/TransferHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useComputed } from '@preact/signals'
import { formatUnits } from 'ethers'
import { useWallet } from '../context/Wallet.js'
import { useTransferHistory } from '../context/TransferHistory.js'
import { Transfer } from '../schema.js'
import { TimeAgo } from './TimeAgo.js'
import { AbbreviatedValue } from './AbbreviatedValue.js'

export const TransferHistory = () => {
const history = useTransferHistory()
Expand Down Expand Up @@ -38,11 +38,10 @@ const HistoryList = ({ transfers }: { transfers: Transfer[] }) => {
<div>
{transfers.map(transfer => {
const timeStamp = new Date(transfer.date).getTime()
const amount = formatUnits(transfer.amount, transfer.token?.decimals)
return (
<a class='block bg-white/10 px-4 py-3 mb-1 hover:bg-white/20' href={`#tx/${transfer.hash}`}>
<div class='overflow-hidden text-ellipsis whitespace-nowrap'>
Sent {amount} {transfer.token?.name || 'ETH'} to {transfer.to}
Sent <AbbreviatedValue amount={transfer.amount} decimals={transfer.token?.decimals} /> {transfer.token?.name || 'ETH'} to {transfer.to}
</div>
<div class='text-sm text-white/50'>
<TimeAgo since={timeStamp} />
Expand Down
40 changes: 40 additions & 0 deletions app/ts/library/bigint.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ethers } from "ethers"

export const bigintToNumberFormatParts = (amount: bigint, decimals = 18n, maximumSignificantDigits = 4) => {
const floatValue = Number(ethers.formatUnits(amount, decimals))

let formatterOptions: Intl.NumberFormatOptions = { useGrouping: false, maximumFractionDigits: 3 }

// maintain accuracy if value is a fraction of 1 ex 0.00001
if (floatValue % 1 === floatValue) formatterOptions.maximumSignificantDigits = maximumSignificantDigits

// apply only compacting with prefixes for values >= 10k or values <= -10k
if (Math.abs(floatValue) >= 1e4) {
formatterOptions = { minimumFractionDigits: 0, notation: 'compact' }
}

const formatter = new Intl.NumberFormat('en-US', formatterOptions)
const parts = formatter.formatToParts(floatValue)
const partsMap = new Map<Intl.NumberFormatPartTypes, string>()

for (const part of parts) {
if (part.type === 'compact') {
// replace American format with Metric prefixes https://www.ibiblio.org/units/prefixes.html
const prefix = part.value.replace('K', 'k').replace('B', 'G')
partsMap.set(part.type, prefix)
continue
}
partsMap.set(part.type, part.value)
}

return partsMap
}

export const bigintToRoundedPrettyDecimalString = (amount: bigint, decimals?: bigint, maximumSignificantDigits = 4) => {
const numberParts = bigintToNumberFormatParts(amount, decimals, maximumSignificantDigits)
let numberString = ''

for (const [_type, value] of numberParts) numberString += value

return numberString
}

0 comments on commit 9bbf442

Please sign in to comment.