Skip to content

Commit

Permalink
fix(wallet-mobile): new tx review improvements (#3730)
Browse files Browse the repository at this point in the history
Signed-off-by: banklesss <[email protected]>
Signed-off-by: Juliano Lazzarotto <[email protected]>
Co-authored-by: Juliano Lazzarotto <[email protected]>
  • Loading branch information
banklesss and stackchain authored Nov 9, 2024
1 parent c696a53 commit 7855269
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 55 deletions.
4 changes: 2 additions & 2 deletions apps/wallet-mobile/src/features/ReviewTx/common/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {Icon} from '../../../components/Icon'

export const Accordion = ({label, children}: {label: string; children: React.ReactNode}) => {
const {styles, colors} = useStyles()
const [isOpen, setIsOpen] = React.useState(false)
const animatedHeight = React.useRef(new Animated.Value(0)).current
const [isOpen, setIsOpen] = React.useState(true)
const animatedHeight = React.useRef(new Animated.Value(1)).current

const toggleSection = () => {
setIsOpen(!isOpen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const formatInputs = async (
address,
addressKind: addressKind ?? null,
rewardAddress,
ownAddress: address != null && isOwnedAddress(wallet, address),
ownAddress: address != null ? isOwnedAddress(wallet, address) : null,
txIndex: input.index,
txHash: input.transaction_id,
}
Expand Down
84 changes: 50 additions & 34 deletions apps/wallet-mobile/src/features/ReviewTx/common/operations.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {FullPoolInfo} from '@emurgo/yoroi-lib'
import {useTheme} from '@yoroi/theme'
import {Balance} from '@yoroi/types'
import * as React from 'react'
import {StyleSheet, Text, useWindowDimensions, View} from 'react-native'
import {TouchableOpacity} from 'react-native-gesture-handler'
Expand All @@ -10,13 +11,13 @@ import {Space} from '../../../components/Space/Space'
import {wrappedCsl} from '../../../yoroi-wallets/cardano/wrappedCsl'
import {usePoolInfo} from '../../../yoroi-wallets/hooks'
import {formatTokenWithText} from '../../../yoroi-wallets/utils/format'
import {asQuantity} from '../../../yoroi-wallets/utils/utils'
import {asQuantity, Quantities} from '../../../yoroi-wallets/utils/utils'
import {useSelectedWallet} from '../../WalletManager/common/hooks/useSelectedWallet'
import {useStrings} from './hooks/useStrings'
import {PoolDetails} from './PoolDetails'
import {CertificateType, FormattedTx} from './types'

export const StakeRegistrationOperation = () => {
export const StakeRegistrationOperation = ({fee}: {fee: Balance.Quantity}) => {
const {styles} = useStyles()
const strings = useStrings()
const {wallet} = useSelectedWallet()
Expand All @@ -27,9 +28,7 @@ export const StakeRegistrationOperation = () => {

<Space width="lg" />

<Text style={styles.operationValue}>
{formatTokenWithText(asQuantity(wallet.protocolParams.keyDeposit), wallet.portfolioPrimaryTokenInfo)}
</Text>
<Text style={styles.operationValue}>{formatTokenWithText(fee, wallet.portfolioPrimaryTokenInfo)}</Text>
</View>
)
}
Expand Down Expand Up @@ -128,36 +127,53 @@ export const VoteDelegationOperation = ({drepID}: {drepID: string}) => {
}

export const useOperations = (certificates: FormattedTx['certificates']) => {
if (certificates === null) return []

return certificates.reduce<React.ReactNode[]>((acc, certificate, index) => {
switch (certificate.type) {
case CertificateType.StakeRegistration:
return [...acc, <StakeRegistrationOperation key={index} />]

case CertificateType.StakeDeregistration:
return [...acc, <StakeDeregistrationOperation key={index} />]

case CertificateType.StakeDelegation: {
const poolKeyHash = certificate.value.pool_keyhash ?? null
if (poolKeyHash == null) return acc
return [...acc, <StakeDelegateOperation key={index} poolId={poolKeyHash} />]
}

case CertificateType.VoteDelegation: {
const drep = certificate.value.drep

if (drep === 'AlwaysAbstain') return [...acc, <AbstainOperation key={index} />]
if (drep === 'AlwaysNoConfidence') return [...acc, <NoConfidenceOperation key={index} />]

const drepId = ('KeyHash' in drep ? drep.KeyHash : drep.ScriptHash) ?? ''
return [...acc, <VoteDelegationOperation key={index} drepID={drepId} />]
const {wallet} = useSelectedWallet()
if (certificates === null) return {components: [], totalFee: Quantities.zero}

return certificates.reduce<{components: React.ReactNode[]; totalFee: Balance.Quantity}>(
(acc, certificate, index) => {
switch (certificate.type) {
case CertificateType.StakeRegistration: {
const fee = asQuantity(wallet.protocolParams.keyDeposit)
return {
components: [...acc.components, <StakeRegistrationOperation fee={fee} key={index} />],
totalFee: Quantities.sum([fee, acc.totalFee]),
}
}

case CertificateType.StakeDeregistration:
return {components: [...acc.components, <StakeDeregistrationOperation key={index} />], totalFee: acc.totalFee}

case CertificateType.StakeDelegation: {
const poolKeyHash = certificate.value.pool_keyhash ?? null
if (poolKeyHash == null) return acc
return {
components: [...acc.components, <StakeDelegateOperation key={index} poolId={poolKeyHash} />],
totalFee: acc.totalFee,
}
}

case CertificateType.VoteDelegation: {
const drep = certificate.value.drep

if (drep === 'AlwaysAbstain')
return {components: [...acc.components, <AbstainOperation key={index} />], totalFee: acc.totalFee}
if (drep === 'AlwaysNoConfidence')
return {components: [...acc.components, <NoConfidenceOperation key={index} />], totalFee: acc.totalFee}

const drepId = ('KeyHash' in drep ? drep.KeyHash : drep.ScriptHash) ?? ''
return {
components: [...acc.components, <VoteDelegationOperation key={index} drepID={drepId} />],
totalFee: acc.totalFee,
}
}

default:
return acc
}

default:
return acc
}
}, [])
},
{components: [], totalFee: Quantities.zero},
)
}

export const getDrepBech32Id = async (poolId: string) => {
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet-mobile/src/features/ReviewTx/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type FormattedInput = {
address: string | undefined
addressKind: CredKind | null
rewardAddress: string | null
ownAddress: boolean
ownAddress: boolean | null
txIndex: number
txHash: string
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import {CredKind} from '@emurgo/cross-csl-core'
import {Blockies} from '@yoroi/identicon'
import {useTheme} from '@yoroi/theme'
import {Balance} from '@yoroi/types'
import * as React from 'react'
import {Linking, StyleSheet, Text, TouchableOpacity, useWindowDimensions, View} from 'react-native'

Expand Down Expand Up @@ -34,6 +35,7 @@ export const OverviewTab = ({
details?: {title: string; component: React.ReactNode}
}) => {
const {styles} = useStyles()
const operations = useOperations(tx.certificates)

const notOwnedOutputs = React.useMemo(() => tx.outputs.filter((output) => !output.ownAddress), [tx.outputs])
const ownedOutputs = React.useMemo(() => tx.outputs.filter((output) => output.ownAddress), [tx.outputs])
Expand All @@ -51,9 +53,10 @@ export const OverviewTab = ({
notOwnedOutputs={notOwnedOutputs}
ownedOutputs={ownedOutputs}
receiverCustomTitle={receiverCustomTitle}
operationsFee={operations.totalFee}
/>

<OperationsSection tx={tx} extraOperations={extraOperations} />
<OperationsSection operations={operations.components} extraOperations={extraOperations} />

<Details details={details} />
</View>
Expand Down Expand Up @@ -119,11 +122,13 @@ const SenderSection = ({
notOwnedOutputs,
ownedOutputs,
receiverCustomTitle,
operationsFee,
}: {
tx: FormattedTx
notOwnedOutputs: FormattedOutputs
ownedOutputs: FormattedOutputs
receiverCustomTitle?: React.ReactNode
operationsFee: Balance.Quantity
}) => {
const strings = useStrings()
const {styles} = useStyles()
Expand All @@ -141,7 +146,7 @@ const SenderSection = ({

<Space height="sm" />

<SenderTokens tx={tx} notOwnedOutputs={notOwnedOutputs} />
<SenderTokens tx={tx} notOwnedOutputs={notOwnedOutputs} operationsFee={operationsFee} />

{notOwnedOutputs.length === 1 && (
<ReceiverSection receiverCustomTitle={receiverCustomTitle} notOwnedOutputs={notOwnedOutputs} />
Expand All @@ -151,7 +156,15 @@ const SenderSection = ({
}

// 🚧 TODO: ADD MULTIRECEIVER SUPPORT 🚧
const SenderTokens = ({tx, notOwnedOutputs}: {tx: FormattedTx; notOwnedOutputs: FormattedOutputs}) => {
const SenderTokens = ({
tx,
notOwnedOutputs,
operationsFee,
}: {
tx: FormattedTx
notOwnedOutputs: FormattedOutputs
operationsFee: Balance.Quantity
}) => {
const {styles} = useStyles()

const {wallet} = useSelectedWallet()
Expand All @@ -164,8 +177,8 @@ const SenderTokens = ({tx, notOwnedOutputs}: {tx: FormattedTx; notOwnedOutputs:
[notOwnedOutputs],
)
const totalPrimaryTokenSpent = React.useMemo(
() => Quantities.sum([totalPrimaryTokenSent, tx.fee.quantity]),
[totalPrimaryTokenSent, tx.fee.quantity],
() => Quantities.sum([totalPrimaryTokenSent, tx.fee.quantity, operationsFee]),
[totalPrimaryTokenSent, tx.fee.quantity, operationsFee],
)
const totalPrimaryTokenSpentLabel = formatTokenWithText(totalPrimaryTokenSpent, wallet.portfolioPrimaryTokenInfo)

Expand Down Expand Up @@ -241,10 +254,14 @@ const ReceiverSection = ({
)
}

const OperationsSection = ({tx, extraOperations}: {tx: FormattedTx; extraOperations?: Array<React.ReactNode>}) => {
const operations = useOperations(tx.certificates)

if (extraOperations == null && tx.certificates == null) return null
const OperationsSection = ({
operations,
extraOperations,
}: {
operations: Array<React.ReactNode>
extraOperations?: Array<React.ReactNode>
}) => {
if (extraOperations == null && operations?.length === 0) return null

return (
<View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const ReviewTxScreen = () => {
})

const txBody = useTxBody({cbor, unsignedTx})

const formatedTx = useFormattedTx(txBody)
const formattedMetadata = useFormattedMetadata({txBody, unsignedTx, cbor})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const Inputs = ({inputs}: {inputs: FormattedInputs}) => {

const Input = ({input}: {input: FormattedInput}) => {
const {styles} = useStyles()
if (input?.address === undefined) throw new Error('UTxOsTab: input invalid address')

return (
<View>
Expand All @@ -48,8 +47,8 @@ const Input = ({input}: {input: FormattedInput}) => {

<Space height="lg" />

<CopiableText textToCopy={input.address}>
<Text style={styles.addressText}>{input.address}</Text>
<CopiableText textToCopy={input.address ?? '-'}>
<Text style={styles.addressText}>{input.address ?? '-'}</Text>
</CopiableText>

<Space height="sm" />
Expand Down Expand Up @@ -81,7 +80,6 @@ const Outputs = ({outputs}: {outputs: FormattedOutputs}) => {

const Output = ({output}: {output: FormattedOutput}) => {
const {styles} = useStyles()
if (output?.address === undefined) throw new Error('UTxOsTab: input invalid address')

return (
<View>
Expand All @@ -92,8 +90,8 @@ const Output = ({output}: {output: FormattedOutput}) => {

<Space height="lg" />

<CopiableText textToCopy={output.address}>
<Text style={styles.addressText}>{output.address}</Text>
<CopiableText textToCopy={output.address ?? '-'}>
<Text style={styles.addressText}>{output.address ?? '-'}</Text>
</CopiableText>
</View>

Expand Down Expand Up @@ -133,11 +131,12 @@ const Fee = ({fee}: {fee: string}) => {
)
}

const UtxoTitle = ({isInput, isOwnAdddress}: {isOwnAdddress: boolean; isInput: boolean}) => {
const UtxoTitle = ({isInput, isOwnAdddress}: {isOwnAdddress: boolean | null; isInput: boolean}) => {
const {styles} = useStyles()
const strings = useStrings()

const label = isOwnAdddress ? strings.utxosYourAddressLabel : strings.utxosForeignAddressLabel
const label =
isOwnAdddress != null ? (isOwnAdddress ? strings.utxosYourAddressLabel : strings.utxosForeignAddressLabel) : '-'

return (
<View style={styles.utxoTitle}>
Expand Down

0 comments on commit 7855269

Please sign in to comment.