Skip to content

Commit db65ec2

Browse files
committed
transaction page
1 parent 17d131a commit db65ec2

File tree

2 files changed

+155
-55
lines changed

2 files changed

+155
-55
lines changed

packages/mobile-app/app/send/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,8 @@ export default function Send() {
325325
appearance="filled"
326326
style={styles.confirmButton}
327327
onPress={() => {
328-
router.push(`/transaction/${sentTxHash}`);
328+
setTransactionState("idle");
329+
router.replace(`/transaction/${sentTxHash}`);
329330
}}
330331
>
331332
View Transaction

packages/mobile-app/app/transaction/[hash].tsx

Lines changed: 153 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { StatusBar } from "expo-status-bar";
22
import { StyleSheet, View, Linking } from "react-native";
33
import { useLocalSearchParams, Stack } from "expo-router";
4-
import React, { useState } from "react";
4+
import React from "react";
55
import {
66
Layout,
77
Text,
@@ -14,12 +14,42 @@ import {
1414
import { useFacade } from "../../data/facades";
1515
import { CurrencyUtils } from "@ironfish/sdk";
1616
import { useQueries } from "@tanstack/react-query";
17-
import { IRON_ASSET_ID_HEX } from "../../data/constants";
17+
import { setStringAsync } from "expo-clipboard";
1818

1919
const ExternalLinkIcon = (props: IconProps) => (
2020
<Icon {...props} name="external-link-outline" />
2121
);
2222

23+
const formatTimestamp = (date: Date) => {
24+
return date.toLocaleString(undefined, {
25+
year: "numeric",
26+
month: "short",
27+
day: "numeric",
28+
hour: "2-digit",
29+
minute: "2-digit",
30+
});
31+
};
32+
33+
const CopyableText = ({ text, style }: { text: string; style?: any }) => {
34+
const copyToClipboard = async () => {
35+
await setStringAsync(text);
36+
};
37+
38+
return (
39+
<View style={styles.copyContainer}>
40+
<Text style={[style, styles.copyText]} selectable>
41+
{text}
42+
</Text>
43+
<Button
44+
appearance="ghost"
45+
accessoryLeft={(props) => <Icon {...props} name="copy-outline" />}
46+
onPress={copyToClipboard}
47+
style={styles.copyButton}
48+
/>
49+
</View>
50+
);
51+
};
52+
2353
export default function TransactionDetails() {
2454
const { hash } = useLocalSearchParams<{ hash: string }>();
2555
const facade = useFacade();
@@ -51,7 +81,33 @@ export default function TransactionDetails() {
5181
if (transactionQuery.isLoading || assetQueries.some((q) => q.isLoading)) {
5282
return (
5383
<Layout style={styles.container}>
54-
<Spinner size="large" />
84+
<Stack.Screen
85+
options={{
86+
headerTitle: "",
87+
headerTransparent: true,
88+
}}
89+
/>
90+
91+
{/* Header Section Skeleton */}
92+
<View style={styles.headerSection}>
93+
<Spinner size="large" style={styles.spinner} />
94+
<Text category="s1" appearance="hint">
95+
Loading transaction details...
96+
</Text>
97+
</View>
98+
99+
{/* Details Section Skeleton */}
100+
<View style={styles.detailsSection}>
101+
{[1, 2, 3, 4].map((i) => (
102+
<React.Fragment key={i}>
103+
<View style={styles.section}>
104+
<View style={styles.skeletonLabel} />
105+
<View style={styles.skeletonValue} />
106+
</View>
107+
<Divider style={styles.divider} />
108+
</React.Fragment>
109+
))}
110+
</View>
55111
</Layout>
56112
);
57113
}
@@ -65,9 +121,6 @@ export default function TransactionDetails() {
65121
}
66122

67123
const transaction = transactionQuery.data;
68-
console.log(`########################`);
69-
console.log("transaction", transaction);
70-
console.log(`########################`);
71124

72125
// Create a map of assetId to asset data
73126
const assetMap = new Map();
@@ -77,33 +130,27 @@ export default function TransactionDetails() {
77130
}
78131
});
79132

80-
// Calculate total amount for each asset
81-
const assetAmounts = transaction.assetBalanceDeltas.reduce(
82-
(acc, delta) => {
83-
const asset = assetMap.get(delta.assetId);
84-
console.log("asset", asset);
85-
const assetName =
86-
asset?.verification.status === "verified"
87-
? asset.verification.symbol
88-
: (asset?.name ?? delta.assetId);
89-
90-
return {
91-
...acc,
92-
[assetName]: (acc[assetName] || 0n) + BigInt(delta.delta),
93-
};
94-
},
95-
{} as Record<string, bigint>,
96-
);
97-
98-
console.log(`$$$$$$`);
99-
console.log("assetAmounts", assetAmounts);
100-
console.log(`$$$$$$`);
101-
102-
// Get the main asset and transaction type
103-
const mainAssetAmount = Object.entries(assetAmounts)[0] || [];
104-
const [mainAssetName, mainAmount] = mainAssetAmount;
133+
// TEMPORARY: Currently assuming the first balance delta represents the main transaction amount
134+
const mainDelta = transaction.assetBalanceDeltas[0];
135+
const asset = assetMap.get(mainDelta.assetId);
136+
const mainAssetName =
137+
asset?.verification.status === "verified"
138+
? asset.verification.symbol
139+
: (asset?.name ?? mainDelta.assetId);
140+
const mainAmount = BigInt(mainDelta.delta);
105141
const isReceived = mainAmount > 0n;
106142

143+
if (transactionQuery.error) {
144+
return (
145+
<Layout style={[styles.container, styles.centerContent]}>
146+
<Text category="h6" style={styles.errorText}>
147+
Failed to load transaction
148+
</Text>
149+
<Button onPress={() => transactionQuery.refetch()}>Try Again</Button>
150+
</Layout>
151+
);
152+
}
153+
107154
return (
108155
<Layout style={styles.container}>
109156
<Stack.Screen
@@ -115,19 +162,35 @@ export default function TransactionDetails() {
115162

116163
{/* Header Section */}
117164
<View style={styles.headerSection}>
118-
<Text category="h3" style={styles.transactionType}>
119-
{isReceived ? "Received" : "Sent"}
120-
</Text>
121-
<Text category="h2" style={styles.mainAmount}>
122-
{CurrencyUtils.render(
123-
(mainAmount < 0n ? -mainAmount : mainAmount).toString(),
124-
false,
125-
)}{" "}
126-
{mainAssetName}
127-
</Text>
128-
<Text category="s1" appearance="hint" style={styles.timestamp}>
129-
{new Date(transaction.timestamp).toLocaleString()}
130-
</Text>
165+
{transaction.notes[0]?.sender === transaction.notes[0]?.owner ? (
166+
<>
167+
<Text category="h3" style={styles.transactionType}>
168+
Self Transaction
169+
</Text>
170+
<Text category="s1" appearance="hint" style={styles.timestamp}>
171+
Balances may not be accurately shown
172+
</Text>
173+
<Text category="s1" appearance="hint" style={styles.timestamp}>
174+
{formatTimestamp(new Date(transaction.timestamp))}
175+
</Text>
176+
</>
177+
) : (
178+
<>
179+
<Text category="h3" style={styles.transactionType}>
180+
{isReceived ? "Received" : "Sent"}
181+
</Text>
182+
<Text category="h2" style={styles.mainAmount}>
183+
{CurrencyUtils.render(
184+
(mainAmount < 0n ? -mainAmount : mainAmount).toString(),
185+
false,
186+
)}{" "}
187+
{mainAssetName}
188+
</Text>
189+
<Text category="s1" appearance="hint" style={styles.timestamp}>
190+
{formatTimestamp(new Date(transaction.timestamp))}
191+
</Text>
192+
</>
193+
)}
131194
</View>
132195

133196
{/* Transaction Details */}
@@ -136,11 +199,14 @@ export default function TransactionDetails() {
136199
<Text category="s1" style={styles.label}>
137200
{isReceived ? "From" : "To"}
138201
</Text>
139-
<Text style={styles.value} selectable>
140-
{isReceived
141-
? transaction.notes[0]?.sender
142-
: transaction.notes[0]?.owner}
143-
</Text>
202+
<CopyableText
203+
text={
204+
isReceived
205+
? transaction.notes[0]?.sender
206+
: transaction.notes[0]?.owner
207+
}
208+
style={styles.value}
209+
/>
144210
</View>
145211

146212
<Divider style={styles.divider} />
@@ -149,9 +215,7 @@ export default function TransactionDetails() {
149215
<Text category="s1" style={styles.label}>
150216
Transaction Hash
151217
</Text>
152-
<Text style={styles.value} selectable>
153-
{hash}
154-
</Text>
218+
<CopyableText text={hash} style={styles.value} />
155219
</View>
156220

157221
<Divider style={styles.divider} />
@@ -215,8 +279,8 @@ const styles = StyleSheet.create({
215279
flex: 1,
216280
},
217281
headerSection: {
218-
padding: 32,
219-
paddingTop: 80,
282+
padding: 24,
283+
paddingTop: 100,
220284
alignItems: "center",
221285
backgroundColor: "#f5f5f5",
222286
},
@@ -248,4 +312,39 @@ const styles = StyleSheet.create({
248312
explorerButton: {
249313
marginTop: 24,
250314
},
315+
copyContainer: {
316+
flexDirection: "row",
317+
alignItems: "center",
318+
},
319+
copyText: {
320+
flex: 1,
321+
},
322+
copyButton: {
323+
padding: 0,
324+
marginLeft: 8,
325+
},
326+
centerContent: {
327+
justifyContent: "center",
328+
alignItems: "center",
329+
},
330+
errorText: {
331+
marginBottom: 16,
332+
textAlign: "center",
333+
},
334+
spinner: {
335+
marginBottom: 16,
336+
},
337+
skeletonLabel: {
338+
height: 20,
339+
width: 100,
340+
backgroundColor: "#f5f5f5",
341+
borderRadius: 4,
342+
marginBottom: 8,
343+
},
344+
skeletonValue: {
345+
height: 24,
346+
backgroundColor: "#f5f5f5",
347+
borderRadius: 4,
348+
width: "100%",
349+
},
251350
});

0 commit comments

Comments
 (0)