Skip to content
This repository has been archived by the owner on Jan 30, 2024. It is now read-only.

Added Transaction details page. #148

Merged
merged 11 commits into from
Jan 20, 2023
3 changes: 2 additions & 1 deletion config-overrides.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path')
const { copySync } = require('fs-extra')

copySync(__dirname + '/types', __dirname + '/src/types', { overwrite: true })
copySync(__dirname + '/versel', __dirname + '/src', { overwrite: true })

module.exports = function override(config, env) {
module.exports = function override(config) {
//do stuff with the webpack config...
return {
...config,
Expand Down
66 changes: 61 additions & 5 deletions electron/ironfish/IronFishManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BoxKeyPair, Asset } from '@ironfish/rust-nodejs'
import { sizeVarBytes } from 'bufio'
import {
AccountValue,
IronfishNode,
Expand Down Expand Up @@ -274,11 +275,18 @@ class TransactionManager implements IIronfishTransactionManager {
notes: notes.map(n => ({
value: n.note.value(),
memo: n.note.memo(),
sender: n.note.sender(),
})),
spends: spends.map(spend => ({
commitment: spend.commitment.toString('hex'),
nullifier: spend.nullifier.toString('hex'),
size: spend.size,
})),
spends,
creator: !!creator,
from: creator ? account.publicAddress : '',
to: creator ? '' : account.publicAddress,
blockHash: transaction.blockHash.toString('hex'),
size: sizeVarBytes(transaction.transaction),
from: creator ? account.publicAddress : notes.at(0)?.note?.sender(),
to: creator ? notes.map(n => n.note.sender()) : [account.publicAddress],
created: created?.header?.timestamp || new Date(),
amount: CurrencyUtils.renderIron(
notes
Expand Down Expand Up @@ -311,7 +319,7 @@ class TransactionManager implements IIronfishTransactionManager {
transaction =>
!search ||
transaction.from.toLowerCase().includes(search) ||
transaction.to.toLowerCase().includes(search) ||
transaction.to.find(a => a.toLowerCase().includes(search)) ||
transaction.notes.find(note =>
note.memo?.toLowerCase().includes(search)
) ||
Expand All @@ -326,9 +334,57 @@ class TransactionManager implements IIronfishTransactionManager {
}

async findByAddress(address: string, searchTerm?: string, sort?: SortType) {
const transactions: Transaction[] = await Promise.resolve([])
const transactions: Transaction[] = []
const accounts: Account[] = this.node.wallet.listAccounts()

for (const account of accounts) {
const head = await account.getHead()
for await (const transaction of account.getTransactions()) {
let creatorNote
for await (const spend of transaction?.transaction?.spends) {
const noteHash = await account.getNoteHash(spend.nullifier)

if (noteHash) {
const decryptedNote = await account.getDecryptedNote(noteHash)
creatorNote = decryptedNote
break
}
}
const notes = transaction
? await account.getTransactionNotes(transaction.transaction)
: []
if (
creatorNote?.note.sender() === address ||
notes.find(n => n.note.sender() === address)
) {
transactions.push(
await this.resolveTransactionFields(
account,
head.sequence,
transaction
)
)
}
}
}

return transactions
.filter(
transaction =>
!searchTerm ||
transaction.from.toLowerCase().includes(searchTerm) ||
transaction.to.find(a => a.toLowerCase().includes(searchTerm)) ||
transaction.notes.find(note =>
note.memo?.toLowerCase().includes(searchTerm)
) ||
transaction.amount.toString().includes(searchTerm)
)
.sort((t1, t2) => {
const date1: number = (t1.created || new Date()).getTime()
const date2: number = (t2.created || new Date()).getTime()

return sort === SortType.ASC ? date1 - date2 : date2 - date1
})
}
}

Expand Down
1 change: 1 addition & 0 deletions forge.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require('path')

const COMMON_CONFIG = {
Expand Down
1 change: 1 addition & 0 deletions package-scripts.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { crossEnv } = require('nps-utils')

module.exports = {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@electron-forge/publisher-github": "6.0.0-beta.66",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/user-event": "^14.2.0",
"@types/byte-size": "^8.1.0",
"@types/lodash": "^4.14.182",
"@types/react": "18.0.1",
"@types/react-dom": "18.0.0",
Expand All @@ -41,6 +42,8 @@
"@visx/xychart": "2.12.2",
"aws-sdk": "^2.1233.0",
"bip39": "^3.0.4",
"bufio": "^1.2.0",
"byte-size": "^8.1.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.0.0",
Expand Down
7 changes: 6 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import CreateAccount from 'Routes/Onboarding/CreateAccount'
import ImportAccount from 'Routes/Onboarding/ImportAccount'
import AddressBook from 'Routes/AddressBook'
import AccountDetails from 'Routes/Accounts/AccountDetails'
import Miner from 'Routes/Miner'
// import Miner from 'Routes/Miner'
import Send from 'Routes/Send/Send'
import AddressDetails from 'Routes/AddressBook/AddressDetails'
import NodeOverview from 'Routes/NodeOverview/NodeOverview'
import ReceiveMoney from 'Routes/Receive/ReceiveMoney'
import { DataSyncProvider } from './providers/DataSyncProvider'
import ElectronThemeChangeHandler from 'Components/ElectronThemeChangeHandler'
import Initializing from 'Routes/Initializing'
import TransactionOverview from 'Routes/Transaction/TransactionOverview'

const breakpoints = {
xs: '46.875rem', //750px
Expand Down Expand Up @@ -51,6 +52,10 @@ function App() {
<Route path={ROUTES.RECEIVE} element={<ReceiveMoney />} />
<Route path={ROUTES.SEND} element={<Send />} />
<Route path={ROUTES.ADDRESS_BOOK} element={<AddressBook />} />
<Route
path={ROUTES.TRANSACTION}
element={<TransactionOverview />}
/>
<Route
path={ROUTES.ADDRESS_BOOK_DETAILS}
element={<AddressDetails />}
Expand Down
13 changes: 8 additions & 5 deletions src/components/BackButtonLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import {
LinkProps,
Flex,
} from '@ironfish/ui-kit'
import { Link as RouterLink } from 'react-router-dom'
import {
Link as RouterLink,
LinkProps as RouterLinkProps,
} from 'react-router-dom'

interface BackButtonLinkProps extends LinkProps {
interface BackButtonLinkProps
extends LinkProps,
Omit<RouterLinkProps, 'color'> {
label: string
to: string
}

const BackButtonLink: FC<BackButtonLinkProps> = ({ to, label, ...rest }) => {
const BackButtonLink: FC<BackButtonLinkProps> = ({ label, ...rest }) => {
const $colors = useColorModeValue(
{
chevron: NAMED_COLORS.BLACK,
Expand All @@ -36,7 +40,6 @@ const BackButtonLink: FC<BackButtonLinkProps> = ({ to, label, ...rest }) => {
alignItems="center"
cursor="pointer"
as={RouterLink}
to={to}
{...rest}
_hover={{
color: $colors.linkHover,
Expand Down
112 changes: 112 additions & 0 deletions src/components/ContactsPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import {
CopyValueToClipboard,
Flex,
IconEye,
chakra,
Box,
NAMED_COLORS,
Modal,
ModalHeader,
ModalOverlay,
ModalContent,
ModalCloseButton,
ModalBody,
} from '@ironfish/ui-kit'
import useContact from 'Hooks/addressBook/useContactByAddress'
import { FC, useState } from 'react'
import { Note } from 'Types/Transaction'
import { truncateHash } from 'Utils/hash'
import { formatOreToTronWithLanguage } from 'Utils/number'
import SimpleTable from './SimpleTable'

const ContactPreview: FC<{ address: string }> = ({ address }) => {
const contact = useContact(address)
return (
<CopyValueToClipboard
label={
<chakra.h5>
{contact?.data ? contact.data.name : truncateHash(address, 2)}
</chakra.h5>
}
value={address}
copyTooltipText={'Copy address'}
copiedTooltipText={'Address successfuly copied'}
iconButtonProps={{
color: NAMED_COLORS.GREY,
}}
/>
)
}

const ContactsPreview: FC<{ addresses?: string[]; notes?: Note[] }> = ({
addresses = [],
notes = [],
}) => {
const [open, setOpen] = useState<boolean>(false)
if (addresses.length === 0) {
return null
}

if (addresses.length === 1) {
return <ContactPreview address={addresses[0]} />
}

return (
<>
<Flex
alignItems="center"
cursor="pointer"
onClick={e => {
setOpen(true)
e.stopPropagation()
}}
>
<Box mr="0.5rem">
<chakra.h5>{addresses.length + ' Addresses'}</chakra.h5>
</Box>
<IconEye color={NAMED_COLORS.GREY} crossed={true} />
</Flex>
<Modal isOpen={open} onClose={() => setOpen(false)}>
<ModalOverlay background="rgba(0,0,0,0.75)" />
<ModalContent p="4rem" minW="40rem">
<ModalHeader>Transaction Addresses</ModalHeader>
<ModalCloseButton
color={NAMED_COLORS.GREY}
borderRadius="50%"
borderColor={NAMED_COLORS.LIGHT_GREY}
border="0.0125rem solid"
mt="1.5rem"
mr="1.5rem"
/>
<ModalBody>
<SimpleTable
data={notes}
columns={[
{
key: 'to-address',
label: 'To',
render: (note: Note) => (
<ContactPreview address={note.sender} />
),
},
{
key: 'amount',
label: '$IRON',
render: (note: Note) =>
formatOreToTronWithLanguage(note.value),
},
{
key: 'memo',
label: 'MEMO',
render: (note: Note) => note.memo,
},
]}
/>
</ModalBody>
</ModalContent>
</Modal>
</>
)
}

export default ContactsPreview
50 changes: 28 additions & 22 deletions src/components/Navbar/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,37 @@ import { FC } from 'react'
import { chakra, List, ListItem, StyleProps } from '@ironfish/ui-kit'

import { NavItemProps, NavItem } from './NavItem'
import { NavLink } from 'react-router-dom'
import { NavLink, useLocation } from 'react-router-dom'

export interface NavProps extends StyleProps {
list: NavItemProps[]
}

export const Nav: FC<NavProps> = ({ list, ...props }) => (
<chakra.nav {...props}>
<List spacing={3} maxWidth="16.5rem">
{list.map(({ to, label, icon, hotkey }) => (
<ListItem key={to}>
<NavLink to={to}>
{({ isActive }) => (
<NavItem
to={to}
label={label}
icon={icon}
hotkey={hotkey}
active={isActive}
/>
)}
</NavLink>
</ListItem>
))}
</List>
</chakra.nav>
)
export const Nav: FC<NavProps> = ({ list, ...props }) => {
const location = useLocation()
return (
<chakra.nav {...props}>
<List spacing={3} maxWidth="16.5rem">
{list.map(({ to, label, icon, hotkey, aliases }) => (
<ListItem key={to}>
<NavLink to={to}>
{({ isActive }) => (
<NavItem
to={to}
label={label}
icon={icon}
hotkey={hotkey}
active={
isActive ||
!!aliases.find(alias => alias.includes(location.pathname))
}
/>
)}
</NavLink>
</ListItem>
))}
</List>
</chakra.nav>
)
}
export default Nav
8 changes: 7 additions & 1 deletion src/components/Navbar/NavItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ export type NavItemProps = {
label: string
icon: FC<SVGProps>
hotkey: string
aliases: string[]
}

export const NavItem: FC<NavItemProps> = ({ active, label, icon, hotkey }) => {
export const NavItem: FC<Omit<NavItemProps, 'aliases'>> = ({
active,
label,
icon,
hotkey,
}) => {
const Icon = icon as FC<SVGProps>
const $colors = useColorModeValue(
{
Expand Down
Loading