Skip to content

Commit

Permalink
refactor(wallet-mobile): new tx review state (#3729)
Browse files Browse the repository at this point in the history
Signed-off-by: banklesss <[email protected]>
  • Loading branch information
banklesss authored Nov 9, 2024
1 parent 6053994 commit c696a53
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ export const ReviewTxProvider = ({
unsignedTxChanged: (unsignedTx: ReviewTxState['unsignedTx']) =>
dispatch({type: ReviewTxActionType.UnsignedTxChanged, unsignedTx}),
cborChanged: (cbor: ReviewTxState['cbor']) => dispatch({type: ReviewTxActionType.CborChanged, cbor}),
operationsChanged: (operations: ReviewTxState['operations']) =>
dispatch({type: ReviewTxActionType.OperationsChanged, operations}),
customReceiverTitleChanged: (customReceiverTitle: ReviewTxState['customReceiverTitle']) =>
dispatch({type: ReviewTxActionType.CustomReceiverTitleChanged, customReceiverTitle}),
detailsChanged: (details: ReviewTxState['details']) => dispatch({type: ReviewTxActionType.DetailsChanged, details}),
reset: () => dispatch({type: ReviewTxActionType.Reset}),
}).current

Expand All @@ -52,24 +47,9 @@ const reviewTxReducer = (state: ReviewTxState, action: ReviewTxAction) => {
draft.cbor = action.cbor
break

case ReviewTxActionType.OperationsChanged:
draft.operations = action.operations
break

case ReviewTxActionType.CustomReceiverTitleChanged:
draft.customReceiverTitle = action.customReceiverTitle
break

case ReviewTxActionType.DetailsChanged:
draft.details = action.details
break

case ReviewTxActionType.Reset:
draft.unsignedTx = castDraft(defaultState.unsignedTx)
draft.cbor = defaultState.cbor
draft.operations = defaultState.operations
draft.customReceiverTitle = defaultState.customReceiverTitle
draft.details = defaultState.details
break

default:
Expand All @@ -87,45 +67,24 @@ type ReviewTxAction =
type: ReviewTxActionType.CborChanged
cbor: ReviewTxState['cbor']
}
| {
type: ReviewTxActionType.OperationsChanged
operations: ReviewTxState['operations']
}
| {
type: ReviewTxActionType.CustomReceiverTitleChanged
customReceiverTitle: ReviewTxState['customReceiverTitle']
}
| {
type: ReviewTxActionType.DetailsChanged
details: ReviewTxState['details']
}
| {
type: ReviewTxActionType.Reset
}

export type ReviewTxState = {
unsignedTx: YoroiUnsignedTx | null
cbor: string | null
operations: Array<React.ReactNode> | null
customReceiverTitle: React.ReactNode | null
details: {title: string; component: React.ReactNode} | null
}

type ReviewTxActions = {
unsignedTxChanged: (unsignedTx: ReviewTxState['unsignedTx']) => void
cborChanged: (cbor: ReviewTxState['cbor']) => void
operationsChanged: (operations: ReviewTxState['operations']) => void
customReceiverTitleChanged: (customReceiverTitle: ReviewTxState['customReceiverTitle']) => void
detailsChanged: (details: ReviewTxState['details']) => void
reset: () => void
}

const defaultState: ReviewTxState = Object.freeze({
unsignedTx: null,
cbor: null,
operations: null,
customReceiverTitle: null,
details: null,
})

function missingInit() {
Expand All @@ -136,18 +95,12 @@ const initialReviewTxContext: ReviewTxContext = {
...defaultState,
unsignedTxChanged: missingInit,
cborChanged: missingInit,
operationsChanged: missingInit,
customReceiverTitleChanged: missingInit,
detailsChanged: missingInit,
reset: missingInit,
}

enum ReviewTxActionType {
UnsignedTxChanged = 'unsignedTxChanged',
CborChanged = 'cborChanged',
OperationsChanged = 'operationsChanged',
CustomReceiverTitleChanged = 'customReceiverTitleChanged',
DetailsChanged = 'detailsChanged',
Reset = 'reset',
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const formatMetadata = async (
const metadata674 = await generalTransactionMetadata?.get(await csl.BigNum.fromStr('674'))
if (metadata674) {
const decodedMetadata = await csl.decodeMetadatumToJsonStr(metadata674, MetadataJsonSchema.BasicConversions)
const msg = JSON.parse(decodedMetadata)?.msg ?? ''
const msg = [parseMsg(JSON.parse(decodedMetadata)?.msg ?? [''])]
metadata = {msg}
}

Expand All @@ -42,6 +42,18 @@ export const formatMetadata = async (
}
}

const parseMsg = (msg: Array<string>) => {
if (msg.length > 1) {
const message = msg.join('')
try {
return JSON.parse(message)
} catch {
return message
}
}
return msg[0]
}

export const useFormattedMetadata = ({
unsignedTx,
cbor,
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 @@ -62,7 +62,7 @@ export type FormattedTx = {

export type FormattedMetadata = {
hash: string | null
metadata: {msg: Array<unknown>} | null
metadata: {msg: Array<string>} | null
}

type AssertEqual<T, Expected> = T extends Expected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@ import {Accordion} from '../../../common/Accordion'
import {CopiableText} from '../../../common/CopiableText'
import {useStrings} from '../../../common/hooks/useStrings'
import {useOperations} from '../../../common/operations'
import {ReviewTxState, useReviewTx} from '../../../common/ReviewTxProvider'
import {TokenItem} from '../../../common/TokenItem'
import {FormattedOutputs, FormattedTx} from '../../../common/types'
import {WalletBalance} from '../../../common/WalletBalance'

export const OverviewTab = ({
tx,
extraOperations,
receiverCustomTitle,
details,
}: {
tx: FormattedTx
extraOperations: ReviewTxState['operations']
details: ReviewTxState['details']
extraOperations?: Array<React.ReactNode>
receiverCustomTitle?: React.ReactNode
details?: {title: string; component: React.ReactNode}
}) => {
const {styles} = useStyles()

Expand All @@ -45,7 +46,12 @@ export const OverviewTab = ({

<Divider verticalSpace="lg" />

<SenderSection tx={tx} notOwnedOutputs={notOwnedOutputs} ownedOutputs={ownedOutputs} />
<SenderSection
tx={tx}
notOwnedOutputs={notOwnedOutputs}
ownedOutputs={ownedOutputs}
receiverCustomTitle={receiverCustomTitle}
/>

<OperationsSection tx={tx} extraOperations={extraOperations} />

Expand Down Expand Up @@ -112,10 +118,12 @@ const SenderSection = ({
tx,
notOwnedOutputs,
ownedOutputs,
receiverCustomTitle,
}: {
tx: FormattedTx
notOwnedOutputs: FormattedOutputs
ownedOutputs: FormattedOutputs
receiverCustomTitle?: React.ReactNode
}) => {
const strings = useStrings()
const {styles} = useStyles()
Expand All @@ -135,7 +143,9 @@ const SenderSection = ({

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

{notOwnedOutputs.length === 1 && <ReceiverSection notOwnedOutputs={notOwnedOutputs} />}
{notOwnedOutputs.length === 1 && (
<ReceiverSection receiverCustomTitle={receiverCustomTitle} notOwnedOutputs={notOwnedOutputs} />
)}
</Accordion>
)
}
Expand Down Expand Up @@ -196,25 +206,30 @@ const SenderSectionLabel = () => {
)
}

const ReceiverSection = ({notOwnedOutputs}: {notOwnedOutputs: FormattedOutputs}) => {
const ReceiverSection = ({
notOwnedOutputs,
receiverCustomTitle,
}: {
notOwnedOutputs: FormattedOutputs
receiverCustomTitle?: React.ReactNode
}) => {
const address = notOwnedOutputs[0]?.rewardAddress ?? notOwnedOutputs[0]?.address ?? '-'
const {styles} = useStyles()
const strings = useStrings()
const {customReceiverTitle} = useReviewTx()

return (
<>
<Space height="sm" />

<View style={styles.receiverAddress}>
<Text>
{notOwnedOutputs[0]?.addressKind === CredKind.Script && customReceiverTitle == null
{notOwnedOutputs[0]?.addressKind === CredKind.Script && receiverCustomTitle == null
? strings.receiveToScriptLabel
: strings.receiveToLabel}
:
</Text>

{customReceiverTitle ?? (
{receiverCustomTitle ?? (
<CopiableText textToCopy={address}>
<Text style={[styles.addressText, styles.receiverSectionAddress]} numberOfLines={1} ellipsizeMode="middle">
{address}
Expand All @@ -226,10 +241,10 @@ const ReceiverSection = ({notOwnedOutputs}: {notOwnedOutputs: FormattedOutputs})
)
}

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

if (extraOperations === null && tx.certificates === null) return null
if (extraOperations == null && tx.certificates == null) return null

return (
<View>
Expand All @@ -254,7 +269,7 @@ const OperationsSection = ({tx, extraOperations}: {tx: FormattedTx; extraOperati
)
}

const Details = ({details}: {details: ReviewTxState['details']}) => {
const Details = ({details}: {details?: {title: string; component: React.ReactNode}}) => {
const {openModal} = useModal()
const {styles} = useStyles()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const MaterialTab = createMaterialTopTabNavigator()
export const ReviewTxScreen = () => {
const {styles} = useStyles()
const strings = useStrings()
const {unsignedTx, operations, details, cbor} = useReviewTx()
const {unsignedTx, cbor} = useReviewTx()
const params = useUnsafeParams<ReviewTxRoutes['review-tx']>()

if (unsignedTx == null && cbor == null) throw new Error('ReviewTxScreen: missing cbor and unsignedTx')
Expand All @@ -47,7 +47,7 @@ export const ReviewTxScreen = () => {

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

const tabsData = [
[strings.overviewTab, 'overview'],
Expand Down Expand Up @@ -80,7 +80,12 @@ export const ReviewTxScreen = () => {
{() => (
/* TODO: make scrollview general to use button border */
<ScrollView style={styles.root}>
<OverviewTab tx={formatedTx} extraOperations={operations} details={details} />
<OverviewTab
tx={formatedTx}
extraOperations={params?.operations}
details={params?.details}
receiverCustomTitle={params?.receiverCustomTitle}
/>
</ScrollView>
)}
</MaterialTab.Screen>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const ManageCollateralScreen = () => {
const strings = useStrings()
const balances = useBalances(wallet)
const {navigateToTxReview} = useWalletNavigation()
const {unsignedTxChanged, operationsChanged} = useReviewTx()
const {unsignedTxChanged} = useReviewTx()
const lockedAmount = asQuantity(wallet.primaryBreakdown.lockedAsStorageCost.toString())

const params = useUnsafeParams<SettingsStackRoutes['manage-collateral']>()
Expand Down Expand Up @@ -87,8 +87,7 @@ export const ManageCollateralScreen = () => {
createUnsignedTx([createCollateralEntry(wallet)], {
onSuccess: (yoroiUnsignedTx) => {
unsignedTxChanged(yoroiUnsignedTx)
operationsChanged([<Operation key="0" />])
navigateToTxReview({onSuccess})
navigateToTxReview({onSuccess, operations: [<Operation key="0" />]})
},
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const ReviewSwap = () => {
const {track} = useMetrics()
const {height: deviceHeight} = useWindowDimensions()
const {navigateToTxReview} = useWalletNavigation()
const {unsignedTxChanged, customReceiverTitleChanged, detailsChanged} = useReviewTx()
const {unsignedTxChanged} = useReviewTx()

const {unsignedTx, orderData} = useSwap()
const sellTokenInfo = orderData.amounts.sell?.info
Expand Down Expand Up @@ -77,10 +77,12 @@ export const ReviewSwap = () => {
)
}

if (liquidityPool != null) customReceiverTitleChanged(liquidityPool)
detailsChanged({component: <TransactionSummary orderData={orderData} />, title: strings.swapDetailsTitle})
unsignedTxChanged(unsignedTx)
navigateToTxReview({onSuccess: onSwapTxSuccess})
navigateToTxReview({
onSuccess: onSwapTxSuccess,
receiverCustomTitle: liquidityPool ?? undefined,
details: {component: <TransactionSummary orderData={orderData} />, title: strings.swapDetailsTitle},
})
}

const isButtonDisabled = couldReceiveNoAssets
Expand Down
3 changes: 3 additions & 0 deletions apps/wallet-mobile/src/kernel/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ export type PortfolioRoutes = {

export type ReviewTxRoutes = {
'review-tx'?: {
operations?: Array<React.ReactNode>
receiverCustomTitle?: React.ReactNode
details?: {title: string; component: React.ReactNode}
onConfirm?: () => void
onCancel?: () => void
onSuccess?: (signedTx: YoroiSignedTx) => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ export const WithdrawStakingRewards = ({wallet}: Props) => {
const strings = useWithdrawStakingRewardsStrings()
const {closeModal} = useModal()
const {navigateToTxReview} = useWalletNavigation()
const {unsignedTxChanged, operationsChanged} = useReviewTx()
const {unsignedTxChanged} = useReviewTx()

const handleOnConfirm = (withdrawalTx: YoroiUnsignedTx) => {
closeModal()

unsignedTxChanged(withdrawalTx)
operationsChanged([<StakeRewardsWithdrawalOperation key="0" />])
navigateToTxReview()
navigateToTxReview({operations: [<StakeRewardsWithdrawalOperation key="0" />]})
}

return (
Expand Down
Loading

0 comments on commit c696a53

Please sign in to comment.