Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Running balance in Transaction List #149

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { BurnEvent, MintEvent } from "@liftedinit/many-js"
import { Flex, Text } from "@liftedinit/ui"
import { amountFormatter, Box, Flex, Text } from "@liftedinit/ui"
import { useMintBurnTxn } from "./hooks"
import { BaseTxnListItem } from "./base-txn-list-item"

export function MintBurnTxnListItem({
txn,
address,
balance,
}: {
txn: MintEvent | BurnEvent
address: string
balance?: bigint
}) {
const time = txn.time
const { TxnIcon, iconColor, title, displayAmount, symbol } = useMintBurnTxn({
Expand All @@ -22,12 +24,22 @@
txnTypeName={title}
txnTime={time}
txnDetails={
<Flex gap={2} justifyContent="flex-end">
<Text fontWeight="medium" color={iconColor} justifySelf="flex-end">
{displayAmount}
</Text>
<Text>{symbol}</Text>
</Flex>
<Box>
<Flex gap={2} justifyContent="flex-end">
<Text fontWeight="medium" color={iconColor} justifySelf="flex-end">
{displayAmount}
</Text>
<Text>{symbol}</Text>
</Flex>
{balance && (
<Flex gap={2} justifyContent="flex-end">

Check warning on line 35 in src/features/transactions/components/txn-list/txn-list-item/mint-burn-txn-list-item.tsx

View check run for this annotation

Codecov / codecov/patch

src/features/transactions/components/txn-list/txn-list-item/mint-burn-txn-list-item.tsx#L35

Added line #L35 was not covered by tests
<Text fontWeight="light" color="gray.300" justifySelf="flex-end">
{amountFormatter(balance)}
</Text>
<Text color="gray.300">{symbol}</Text>
</Flex>
)}
</Box>
}
/>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { SendEvent } from "@liftedinit/many-js"
import { Flex, Text } from "@liftedinit/ui"
import { amountFormatter, Box, Flex, Text } from "@liftedinit/ui"
import { useSendTxn } from "./hooks"
import { BaseTxnListItem } from "./base-txn-list-item"

export function SendTxnListItem({
txn,
address,
balance,
}: {
txn: SendEvent
address: string
balance?: bigint
}) {
const time = txn.time
const {
Expand All @@ -34,12 +36,22 @@ export function SendTxnListItem({
actorName={contactName}
actorAddress={toOrFromAddress}
txnDetails={
<Flex gap={2} justifyContent="flex-end">
<Text fontWeight="medium" color={iconColor} justifySelf="flex-end">
{displayAmount}
</Text>
<Text>{symbol}</Text>
</Flex>
<Box>
<Flex gap={2} justifyContent="flex-end">
<Text fontWeight="medium" color={iconColor} justifySelf="flex-end">
{displayAmount}
</Text>
<Text>{symbol}</Text>
</Flex>
{balance && (
<Flex gap={2} justifyContent="flex-end">
<Text fontWeight="light" color="gray.300" justifySelf="flex-end">
{amountFormatter(balance)}
</Text>
<Text color="gray.300">{symbol}</Text>
</Flex>
)}
</Box>
}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,21 @@ type EditRolesEvent = AddRolesEvent | RemoveRolesEvent
export function TxnListItem({
transaction,
address,
balance,
}: {
transaction: Event
address: string
balance?: bigint
}) {
const txnTypeName = transaction.type as EventType
if (txnTypeName === EventType.send) {
return <SendTxnListItem txn={transaction as SendEvent} address={address} />
return (
<SendTxnListItem
txn={transaction as SendEvent}
address={address}
balance={balance}
/>
)
} else if (txnTypeName === EventType.accountCreate) {
return <CreateAccountTxnListItem txn={transaction as CreateAccountEvent} />
} else if (txnTypeName === EventType.accountAddFeatures) {
Expand All @@ -48,7 +56,11 @@ export function TxnListItem({
return <EditRolesTxnListItem txn={transaction as EditRolesEvent} />
} else if (txnTypeName === EventType.mint || txnTypeName === EventType.burn) {
return (
<MintBurnTxnListItem txn={transaction as MintEvent} address={address} />
<MintBurnTxnListItem
txn={transaction as MintEvent}
address={address}
balance={balance}
/>
)
} else if (isMultisigTxnType(transaction.type)) {
return <MultisigTxnListItem txn={transaction as MultisigEvent} />
Expand Down
19 changes: 18 additions & 1 deletion src/features/transactions/components/txn-list/txn-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ import {
TableContainer,
Text,
} from "@liftedinit/ui"
import { useTransactionsList } from "features/transactions/queries"
import {
calculateBalances,
useAllTransactionsList,
useTransactionsList,
} from "features/transactions/queries"
import { TxnListItem } from "./txn-list-item"
import { TxnExport } from "./txn-export"
import { useBalances } from "features/balances"

export function TxnList({
address,
Expand All @@ -38,6 +43,17 @@ export function TxnList({
} = queryData

const { count, transactions } = data
const { data: allTxns } = useAllTransactionsList({ accounts })
const { data: balances } = useBalances({ address })

let txnBalances = new Map()
if (allTxns && balances) {
txnBalances = calculateBalances(
allTxns.transactions,
balances.ownedAssetsWithBalance,
address,
)
}

if (isError && error) {
return (
Expand Down Expand Up @@ -71,6 +87,7 @@ export function TxnList({
transaction={t}
key={t._id + t.time}
address={address}
balance={txnBalances.get(t._id)}
/>
)
})}
Expand Down
51 changes: 50 additions & 1 deletion src/features/transactions/queries.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { useNetworkContext } from "features/network"
import { BoundType, Event, ListOrderType, Network } from "@liftedinit/many-js"
import {
BoundType,
BurnEvent,
Event,
ListOrderType,
MintEvent,
Network,
SendEvent,
} from "@liftedinit/many-js"
import { useMutation, useQuery, useQueryClient } from "react-query"
import { useMemo, useState } from "react"
import { arrayBufferToBase64 } from "@liftedinit/ui"
import { Asset } from "features/balances"

const PAGE_SIZE = 11
const MAX_PAGE_SIZE = 100
Expand Down Expand Up @@ -304,3 +313,43 @@
},
}
}

export function calculateBalances(
transactions: ProcessedEvent[],
balances: Asset[],
address: string,
): Map<string, bigint> {
const txnBalances = new Map()
const running = new Map()

balances.forEach(({ identity, balance }) => running.set(identity, balance))
transactions.forEach(txn => {
if (["send", "mint", "burn"].includes(txn.type)) {
const { id, symbolAddress } = txn as SendEvent
const balance = running.get(symbolAddress) ?? BigInt(0)
txnBalances.set(arrayBufferToBase64(id), balance)
let updated

switch (txn.type) {
case "send": {
const { amount, from } = txn as SendEvent
updated = from === address ? balance + amount : balance - amount
break
}
case "mint": {
const { amounts } = txn as MintEvent
updated = balance + amounts[address]
break

Check warning on line 342 in src/features/transactions/queries.ts

View check run for this annotation

Codecov / codecov/patch

src/features/transactions/queries.ts#L339-L342

Added lines #L339 - L342 were not covered by tests
}
case "burn": {
const { amounts } = txn as BurnEvent
updated = balance - amounts[address]
break

Check warning on line 347 in src/features/transactions/queries.ts

View check run for this annotation

Codecov / codecov/patch

src/features/transactions/queries.ts#L344-L347

Added lines #L344 - L347 were not covered by tests
}
}

running.set(symbolAddress, updated)
}
})
return txnBalances
}
26 changes: 11 additions & 15 deletions src/views/home/__tests__/home.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const mockListData = {
const mockBalanceData = {
balances: new Map([
["mabc", BigInt(1000000)],
["mghi", BigInt(5000000)],
["mghi", BigInt(6000000)],
]),
}

Expand Down Expand Up @@ -114,14 +114,14 @@ describe("home page", () => {
expect(assetsTab).toBeInTheDocument()
expect(activityTab).toBeInTheDocument()
})
it("should list the balances of each token the account holds", async function () {
it("should list the balances of each token the account holds", async function() {
setupHome()

expect(screen.getByText(/abc/i)).toBeInTheDocument()
expect(screen.getAllByText(/abc/i)[0]).toBeInTheDocument()
expect(screen.getByText("0.001")).toBeInTheDocument()

expect(screen.getByText(/ghi/i)).toBeInTheDocument()
expect(screen.getByText("0.005")).toBeInTheDocument()
expect(screen.getAllByText(/ghi/i)[0]).toBeInTheDocument()
expect(screen.getByText("0.006")).toBeInTheDocument()
})
it("should display an error message", async () => {
mockNetwork.ledger.balance = jest.fn().mockImplementation(async arg => {
Expand Down Expand Up @@ -164,14 +164,12 @@ describe("home page", () => {
const assets = await screen.findAllByLabelText(/asset list item/i)
userEvent.click(assets[0])
expect(screen.getByText(/0.001 abc/i)).toBeInTheDocument()
await waitFor(() =>
expect(mockNetwork.events.list).toHaveBeenCalledTimes(1),
)
await waitFor(() => expect(mockNetwork.events.list).toHaveBeenCalled())
const rows = await screen.findAllByRole("row")
expect(rows.length).toBe(2)

const firstTxn = within(rows[0])
expect(firstTxn.getByText(/abc/i)).toBeInTheDocument()
expect(firstTxn.getAllByText(/abc/i)[0]).toBeInTheDocument()
expect(firstTxn.getByText(/send/i)).toBeInTheDocument()
expect(firstTxn.getByText(/-0.000000001/i)).toBeInTheDocument()
expect(firstTxn.getByText(/2023/i)).toBeInTheDocument()
Expand All @@ -180,7 +178,7 @@ describe("home page", () => {
expect(firstTxn.getByText(/8:03:00/i)).toBeInTheDocument()

const secondTxn = within(rows[1])
expect(secondTxn.getByText(/abc/i)).toBeInTheDocument()
expect(secondTxn.getAllByText(/abc/i)[0]).toBeInTheDocument()
expect(secondTxn.getByText(/receive/i)).toBeInTheDocument()
expect(secondTxn.getByText(/\+0.000000003/i)).toBeInTheDocument()
expect(secondTxn.getByText(/2022/i)).toBeInTheDocument()
Expand All @@ -192,9 +190,7 @@ describe("home page", () => {
const { activityTab } = setupHome()
userEvent.click(activityTab)

await waitFor(() =>
expect(mockNetwork.events.list).toHaveBeenCalledTimes(1),
)
await waitFor(() => expect(mockNetwork.events.list).toHaveBeenCalled())

const rows = screen.getAllByRole("row")

Expand All @@ -205,15 +201,15 @@ describe("home page", () => {
expect(screen.getByText(/9/i)).toBeInTheDocument()
expect(screen.getByText(/8:03:00/i)).toBeInTheDocument()
expect(screen.getByText(/-0.000000001/i)).toBeInTheDocument()
expect(screen.getByText(/abc/i)).toBeInTheDocument()
expect(screen.getAllByText(/abc/i)[0]).toBeInTheDocument()

expect(screen.getByText(/receive/i)).toBeInTheDocument()
expect(screen.getByText(/2022/i)).toBeInTheDocument()
expect(screen.getByText(/4/i)).toBeInTheDocument()
expect(screen.getByText(/9/i)).toBeInTheDocument()
expect(screen.getByText(/8:01:00/i)).toBeInTheDocument()
expect(screen.getByText(/\+0.000000003/i)).toBeInTheDocument()
expect(screen.getByText(/ghi/i)).toBeInTheDocument()
expect(screen.getAllByText(/ghi/i)[0]).toBeInTheDocument()
})
})

Expand Down