@akta/algomd-rn 0.0.1-canary
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +46 -0
- package/src/components/ASA.tsx +221 -0
- package/src/components/ASASearch.tsx +79 -0
- package/src/components/Account.tsx +150 -0
- package/src/components/AccountSearch.tsx +75 -0
- package/src/components/AuctionListing.tsx +220 -0
- package/src/components/NFDProfile.tsx +173 -0
- package/src/components/NFDSearch.tsx +81 -0
- package/src/components/NFTListing.tsx +181 -0
- package/src/components/NFTSearch.tsx +68 -0
- package/src/components/Poll.tsx +234 -0
- package/src/components/PollSearch.tsx +79 -0
- package/src/components/RaffleListing.tsx +204 -0
- package/src/components/SelfFetching.tsx +358 -0
- package/src/components/TradeOffer.tsx +245 -0
- package/src/components/TradeSearch.tsx +103 -0
- package/src/components/TransactionDetails.tsx +242 -0
- package/src/components/TransactionSearch.tsx +102 -0
- package/src/components/index.ts +30 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useAlgomdData.ts +445 -0
- package/src/hooks/useAlgorandClient.ts +39 -0
- package/src/index.ts +83 -0
- package/src/provider/AlgomdProvider.tsx +45 -0
- package/src/provider/context.ts +18 -0
- package/src/provider/imageResolver.ts +12 -0
- package/src/provider/index.ts +5 -0
- package/src/provider/networks.ts +23 -0
- package/src/provider/types.ts +17 -0
- package/src/types/algorand.ts +205 -0
- package/src/types/index.ts +1 -0
- package/src/ui/CopyButton.tsx +40 -0
- package/src/ui/DataStates.tsx +41 -0
- package/src/ui/ProgressBar.tsx +42 -0
- package/src/ui/SearchSheet.tsx +134 -0
- package/src/ui/SizeContainer.tsx +25 -0
- package/src/ui/StatusBadge.tsx +51 -0
- package/src/ui/index.ts +6 -0
- package/src/utils/format.ts +70 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/search.ts +65 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import type { TradeOffer, ASA, AlgorandAccount } from '../types/algorand';
|
|
4
|
+
import { formatAddress, formatRelativeTime } from '../utils/format';
|
|
5
|
+
import { SearchSheet } from '../ui/SearchSheet';
|
|
6
|
+
|
|
7
|
+
function isASA(item: ASA | AlgorandAccount): item is ASA {
|
|
8
|
+
return 'unitName' in item;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function summarizeAssets(assets: (ASA | AlgorandAccount)[]): string {
|
|
12
|
+
const asas = assets.filter(isASA);
|
|
13
|
+
const accounts = assets.filter((a) => !isASA(a));
|
|
14
|
+
const parts: string[] = [];
|
|
15
|
+
if (asas.length > 0) parts.push(`${asas.length} asset${asas.length > 1 ? 's' : ''}`);
|
|
16
|
+
if (accounts.length > 0) parts.push(`${accounts.length} account${accounts.length > 1 ? 's' : ''}`);
|
|
17
|
+
return parts.join(', ') || 'Empty';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface TradeSearchProps {
|
|
21
|
+
data: TradeOffer[];
|
|
22
|
+
onSelect?: (offer: TradeOffer) => void;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function TradeSearch({
|
|
28
|
+
data,
|
|
29
|
+
onSelect,
|
|
30
|
+
placeholder = 'Search trade offers...',
|
|
31
|
+
className,
|
|
32
|
+
}: TradeSearchProps) {
|
|
33
|
+
const [selected, setSelected] = useState<TradeOffer | null>(null);
|
|
34
|
+
|
|
35
|
+
const handleSelect = useCallback(
|
|
36
|
+
(offer: TradeOffer) => {
|
|
37
|
+
setSelected(offer);
|
|
38
|
+
onSelect?.(offer);
|
|
39
|
+
},
|
|
40
|
+
[onSelect],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const renderItem = useCallback(
|
|
44
|
+
(offer: TradeOffer) => {
|
|
45
|
+
const statusColor =
|
|
46
|
+
offer.status === 'pending'
|
|
47
|
+
? 'text-blue-400'
|
|
48
|
+
: offer.status === 'accepted'
|
|
49
|
+
? 'text-green-400'
|
|
50
|
+
: offer.status === 'rejected'
|
|
51
|
+
? 'text-red-400'
|
|
52
|
+
: 'text-zinc-400';
|
|
53
|
+
|
|
54
|
+
const statusBg =
|
|
55
|
+
offer.status === 'pending'
|
|
56
|
+
? 'bg-blue-500/20'
|
|
57
|
+
: offer.status === 'accepted'
|
|
58
|
+
? 'bg-green-500/20'
|
|
59
|
+
: offer.status === 'rejected'
|
|
60
|
+
? 'bg-red-500/20'
|
|
61
|
+
: 'bg-zinc-700';
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View className="flex-row items-center gap-3">
|
|
65
|
+
<View className="w-8 h-8 rounded-lg bg-zinc-800 items-center justify-center">
|
|
66
|
+
<Text className="text-xs text-zinc-400">TR</Text>
|
|
67
|
+
</View>
|
|
68
|
+
<View className="flex-1">
|
|
69
|
+
<Text className="text-sm font-medium text-white" numberOfLines={1}>
|
|
70
|
+
{summarizeAssets(offer.offering)} for {summarizeAssets(offer.requesting)}
|
|
71
|
+
</Text>
|
|
72
|
+
<Text className="text-xs text-zinc-400">
|
|
73
|
+
From {formatAddress(offer.creator)} - {formatRelativeTime(offer.createdAt)}
|
|
74
|
+
</Text>
|
|
75
|
+
</View>
|
|
76
|
+
<View className={`px-2 py-0.5 rounded-full ${statusBg}`}>
|
|
77
|
+
<Text className={`text-xs font-medium ${statusColor}`}>
|
|
78
|
+
{offer.status.charAt(0).toUpperCase() + offer.status.slice(1)}
|
|
79
|
+
</Text>
|
|
80
|
+
</View>
|
|
81
|
+
</View>
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
[],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<View className={className}>
|
|
89
|
+
<SearchSheet
|
|
90
|
+
data={data}
|
|
91
|
+
placeholder={placeholder}
|
|
92
|
+
onSelect={handleSelect}
|
|
93
|
+
renderItem={renderItem}
|
|
94
|
+
/>
|
|
95
|
+
{selected && (
|
|
96
|
+
<View className="mt-3 p-3 rounded-lg bg-zinc-800 border border-zinc-700">
|
|
97
|
+
<Text className="text-xs font-medium text-zinc-400 mb-1">Selected Trade</Text>
|
|
98
|
+
{renderItem(selected)}
|
|
99
|
+
</View>
|
|
100
|
+
)}
|
|
101
|
+
</View>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import Animated, { FadeInDown } from 'react-native-reanimated';
|
|
4
|
+
import Svg, { Path } from 'react-native-svg';
|
|
5
|
+
import type { TransactionDetails as TransactionDetailsType, ComponentSize } from '../types/algorand';
|
|
6
|
+
import { formatAddress, formatCurrency, formatRelativeTime } from '../utils/format';
|
|
7
|
+
import { CopyButton } from '../ui/CopyButton';
|
|
8
|
+
import { SizeContainer } from '../ui/SizeContainer';
|
|
9
|
+
import { StatusBadge } from '../ui/StatusBadge';
|
|
10
|
+
|
|
11
|
+
function ArrowRightIcon({ size = 16, color = '#71717a' }: { size?: number; color?: string }) {
|
|
12
|
+
return (
|
|
13
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
14
|
+
<Path d="M5 12h14" />
|
|
15
|
+
<Path d="m12 5 7 7-7 7" />
|
|
16
|
+
</Svg>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DollarIcon({ size = 16, color = '#a1a1aa' }: { size?: number; color?: string }) {
|
|
21
|
+
return (
|
|
22
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
23
|
+
<Path d="M12 2v20" />
|
|
24
|
+
<Path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
|
25
|
+
</Svg>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ZapIcon({ size = 16, color = '#a1a1aa' }: { size?: number; color?: string }) {
|
|
30
|
+
return (
|
|
31
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
32
|
+
<Path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z" />
|
|
33
|
+
</Svg>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function CheckCircleIcon({ size = 12, color = '#34d399' }: { size?: number; color?: string }) {
|
|
38
|
+
return (
|
|
39
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
40
|
+
<Path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
41
|
+
<Path d="m9 11 3 3L22 4" />
|
|
42
|
+
</Svg>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ClockIcon({ size = 12, color = '#facc15' }: { size?: number; color?: string }) {
|
|
47
|
+
return (
|
|
48
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
49
|
+
<Path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z" />
|
|
50
|
+
<Path d="M12 6v6l4 2" />
|
|
51
|
+
</Svg>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const TYPE_LABELS: Record<string, string> = {
|
|
56
|
+
payment: 'Payment',
|
|
57
|
+
'asset-transfer': 'Asset Transfer',
|
|
58
|
+
'application-call': 'App Call',
|
|
59
|
+
'asset-config': 'Asset Config',
|
|
60
|
+
'key-registration': 'Key Registration',
|
|
61
|
+
'asset-freeze': 'Asset Freeze',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const CONTEXT_LABELS: Record<string, string> = {
|
|
65
|
+
'nft-purchase': 'NFT Purchase',
|
|
66
|
+
'auction-won': 'Auction Won',
|
|
67
|
+
'raffle-entry': 'Raffle Entry',
|
|
68
|
+
'trade-offer': 'Trade Offer',
|
|
69
|
+
vote: 'Vote Cast',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
interface TransactionDetailsProps {
|
|
73
|
+
data: TransactionDetailsType;
|
|
74
|
+
showContext?: boolean;
|
|
75
|
+
showFee?: boolean;
|
|
76
|
+
compact?: boolean;
|
|
77
|
+
size?: ComponentSize;
|
|
78
|
+
className?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function TransactionDetailsComponent({
|
|
82
|
+
data: transaction,
|
|
83
|
+
showContext = true,
|
|
84
|
+
showFee = true,
|
|
85
|
+
compact = false,
|
|
86
|
+
size = 'full',
|
|
87
|
+
className,
|
|
88
|
+
}: TransactionDetailsProps) {
|
|
89
|
+
const typeLabel = TYPE_LABELS[transaction.type] || 'Transaction';
|
|
90
|
+
const contextLabel = transaction.context ? CONTEXT_LABELS[transaction.context.type] || 'Event' : null;
|
|
91
|
+
|
|
92
|
+
function getTypeIcon() {
|
|
93
|
+
switch (transaction.type) {
|
|
94
|
+
case 'payment':
|
|
95
|
+
return <DollarIcon size={16} color="#a1a1aa" />;
|
|
96
|
+
case 'application-call':
|
|
97
|
+
return <ZapIcon size={16} color="#a1a1aa" />;
|
|
98
|
+
default:
|
|
99
|
+
return <ArrowRightIcon size={16} color="#a1a1aa" />;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (compact) {
|
|
104
|
+
return (
|
|
105
|
+
<View className={`flex-row items-center gap-1.5 px-2 py-1 rounded-full bg-zinc-800/60 ${className ?? ''}`}>
|
|
106
|
+
{transaction.confirmed ? (
|
|
107
|
+
<CheckCircleIcon size={12} color="#34d399" />
|
|
108
|
+
) : (
|
|
109
|
+
<ClockIcon size={12} color="#facc15" />
|
|
110
|
+
)}
|
|
111
|
+
<Text className="text-xs font-mono text-white" numberOfLines={1}>
|
|
112
|
+
{formatAddress(transaction.id)}
|
|
113
|
+
</Text>
|
|
114
|
+
<CopyButton value={transaction.id} size={12} color="#a1a1aa" />
|
|
115
|
+
</View>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const isFullscreen = size === 'fullscreen';
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<SizeContainer size={size} className={className}>
|
|
123
|
+
<Animated.View
|
|
124
|
+
entering={FadeInDown.duration(400).springify()}
|
|
125
|
+
className={`rounded-2xl ${isFullscreen ? 'border border-zinc-800' : 'bg-zinc-900/80'}`}
|
|
126
|
+
>
|
|
127
|
+
<View className="p-5">
|
|
128
|
+
{/* Header */}
|
|
129
|
+
<View className="mb-4">
|
|
130
|
+
<View className="flex-row items-center gap-2 mb-2 flex-wrap">
|
|
131
|
+
{getTypeIcon()}
|
|
132
|
+
<Text className="text-sm font-medium text-zinc-400">{typeLabel}</Text>
|
|
133
|
+
<StatusBadge
|
|
134
|
+
label={transaction.confirmed ? 'Confirmed' : 'Pending'}
|
|
135
|
+
variant={transaction.confirmed ? 'success' : 'warning'}
|
|
136
|
+
icon={
|
|
137
|
+
transaction.confirmed ? (
|
|
138
|
+
<CheckCircleIcon size={10} color="#34d399" />
|
|
139
|
+
) : (
|
|
140
|
+
<ClockIcon size={10} color="#facc15" />
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
/>
|
|
144
|
+
{contextLabel && (
|
|
145
|
+
<StatusBadge label={contextLabel} variant="info" />
|
|
146
|
+
)}
|
|
147
|
+
</View>
|
|
148
|
+
|
|
149
|
+
<Text className="text-sm font-mono font-medium text-white mb-1">Transaction Details</Text>
|
|
150
|
+
<Text className="text-xs font-mono text-zinc-400 mb-2">
|
|
151
|
+
{formatAddress(transaction.id)}
|
|
152
|
+
</Text>
|
|
153
|
+
<CopyButton value={transaction.id} size={14} color="#a1a1aa" />
|
|
154
|
+
</View>
|
|
155
|
+
|
|
156
|
+
{/* Transaction Flow */}
|
|
157
|
+
<View className="flex-row items-center gap-3 mb-4">
|
|
158
|
+
<View className="flex-1">
|
|
159
|
+
<Text className="text-xs font-medium text-zinc-400 mb-1">From</Text>
|
|
160
|
+
<Text className="text-sm font-mono text-white" numberOfLines={1}>
|
|
161
|
+
{formatAddress(transaction.from)}
|
|
162
|
+
</Text>
|
|
163
|
+
</View>
|
|
164
|
+
<ArrowRightIcon size={16} color="#71717a" />
|
|
165
|
+
<View className="flex-1">
|
|
166
|
+
<Text className="text-xs font-medium text-zinc-400 mb-1">To</Text>
|
|
167
|
+
<Text className="text-sm font-mono text-white" numberOfLines={1}>
|
|
168
|
+
{transaction.to ? formatAddress(transaction.to) : 'N/A'}
|
|
169
|
+
</Text>
|
|
170
|
+
</View>
|
|
171
|
+
</View>
|
|
172
|
+
|
|
173
|
+
{/* Amount */}
|
|
174
|
+
{transaction.amount !== undefined && (
|
|
175
|
+
<View className="mb-4">
|
|
176
|
+
<Text className="text-2xl font-bold text-white">
|
|
177
|
+
{transaction.asset
|
|
178
|
+
? formatCurrency(transaction.amount, transaction.asset.unitName)
|
|
179
|
+
: formatCurrency(transaction.amount / 1_000_000, 'ALGO')}
|
|
180
|
+
</Text>
|
|
181
|
+
<Text className="text-xs text-zinc-400">
|
|
182
|
+
{transaction.asset
|
|
183
|
+
? `${transaction.asset.name || transaction.asset.unitName} Transfer`
|
|
184
|
+
: 'ALGO Payment'}
|
|
185
|
+
</Text>
|
|
186
|
+
</View>
|
|
187
|
+
)}
|
|
188
|
+
|
|
189
|
+
{/* Context */}
|
|
190
|
+
{showContext && transaction.context && (
|
|
191
|
+
<View className="mb-4 bg-blue-500/5 rounded-xl px-3 py-2.5">
|
|
192
|
+
<Text className="text-sm font-medium text-blue-400 mb-2">{contextLabel}</Text>
|
|
193
|
+
{transaction.context.metadata &&
|
|
194
|
+
Object.entries(transaction.context.metadata).map(([key, value]) => (
|
|
195
|
+
<View key={key} className="flex-row justify-between mb-1">
|
|
196
|
+
<Text className="text-xs text-blue-300 capitalize">
|
|
197
|
+
{key.replace('_', ' ')}
|
|
198
|
+
</Text>
|
|
199
|
+
<Text className="text-xs font-medium text-white">
|
|
200
|
+
{typeof value === 'number' ? formatCurrency(value) : String(value)}
|
|
201
|
+
</Text>
|
|
202
|
+
</View>
|
|
203
|
+
))}
|
|
204
|
+
</View>
|
|
205
|
+
)}
|
|
206
|
+
|
|
207
|
+
{/* Details Grid */}
|
|
208
|
+
<View className="flex-row gap-6 mb-3">
|
|
209
|
+
<View>
|
|
210
|
+
<Text className="text-base font-semibold text-white">
|
|
211
|
+
#{transaction.round.toLocaleString()}
|
|
212
|
+
</Text>
|
|
213
|
+
<Text className="text-xs text-zinc-400">Round</Text>
|
|
214
|
+
</View>
|
|
215
|
+
{showFee && (
|
|
216
|
+
<View>
|
|
217
|
+
<Text className="text-base font-semibold text-white">
|
|
218
|
+
{formatCurrency(transaction.fee / 1_000_000, 'ALGO')}
|
|
219
|
+
</Text>
|
|
220
|
+
<Text className="text-xs text-zinc-400">Fee</Text>
|
|
221
|
+
</View>
|
|
222
|
+
)}
|
|
223
|
+
<View>
|
|
224
|
+
<Text className="text-base font-semibold text-white">
|
|
225
|
+
{formatRelativeTime(transaction.timestamp)}
|
|
226
|
+
</Text>
|
|
227
|
+
<Text className="text-xs text-zinc-400">Time</Text>
|
|
228
|
+
</View>
|
|
229
|
+
</View>
|
|
230
|
+
|
|
231
|
+
{/* Note */}
|
|
232
|
+
{transaction.note && (
|
|
233
|
+
<View className="p-3 rounded-lg bg-zinc-800/50">
|
|
234
|
+
<Text className="text-xs font-medium text-zinc-400 mb-1">Note</Text>
|
|
235
|
+
<Text className="text-sm text-zinc-300">{transaction.note}</Text>
|
|
236
|
+
</View>
|
|
237
|
+
)}
|
|
238
|
+
</View>
|
|
239
|
+
</Animated.View>
|
|
240
|
+
</SizeContainer>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import Svg, { Path } from 'react-native-svg';
|
|
4
|
+
import type { TransactionDetails } from '../types/algorand';
|
|
5
|
+
import { formatAddress, formatCurrency, formatRelativeTime } from '../utils/format';
|
|
6
|
+
import { SearchSheet } from '../ui/SearchSheet';
|
|
7
|
+
|
|
8
|
+
function CheckCircleIcon({ size = 12, color = '#34d399' }: { size?: number; color?: string }) {
|
|
9
|
+
return (
|
|
10
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
11
|
+
<Path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
12
|
+
<Path d="m9 11 3 3L22 4" />
|
|
13
|
+
</Svg>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ClockIcon({ size = 12, color = '#facc15' }: { size?: number; color?: string }) {
|
|
18
|
+
return (
|
|
19
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
|
20
|
+
<Path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z" />
|
|
21
|
+
<Path d="M12 6v6l4 2" />
|
|
22
|
+
</Svg>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const TYPE_LABELS: Record<string, string> = {
|
|
27
|
+
payment: 'Payment',
|
|
28
|
+
'asset-transfer': 'Asset Transfer',
|
|
29
|
+
'application-call': 'App Call',
|
|
30
|
+
'asset-config': 'Asset Config',
|
|
31
|
+
'key-registration': 'Key Registration',
|
|
32
|
+
'asset-freeze': 'Asset Freeze',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
interface TransactionSearchProps {
|
|
36
|
+
data: TransactionDetails[];
|
|
37
|
+
onSelect?: (transaction: TransactionDetails) => void;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function TransactionSearch({
|
|
43
|
+
data,
|
|
44
|
+
onSelect,
|
|
45
|
+
placeholder = 'Search transactions...',
|
|
46
|
+
className,
|
|
47
|
+
}: TransactionSearchProps) {
|
|
48
|
+
const [selected, setSelected] = useState<TransactionDetails | null>(null);
|
|
49
|
+
|
|
50
|
+
const handleSelect = useCallback(
|
|
51
|
+
(transaction: TransactionDetails) => {
|
|
52
|
+
setSelected(transaction);
|
|
53
|
+
onSelect?.(transaction);
|
|
54
|
+
},
|
|
55
|
+
[onSelect],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const renderItem = useCallback(
|
|
59
|
+
(transaction: TransactionDetails) => (
|
|
60
|
+
<View className="flex-row items-center gap-3">
|
|
61
|
+
{transaction.confirmed ? (
|
|
62
|
+
<CheckCircleIcon size={16} color="#34d399" />
|
|
63
|
+
) : (
|
|
64
|
+
<ClockIcon size={16} color="#facc15" />
|
|
65
|
+
)}
|
|
66
|
+
<View className="flex-1">
|
|
67
|
+
<Text className="text-sm font-mono text-white" numberOfLines={1}>
|
|
68
|
+
{formatAddress(transaction.id)}
|
|
69
|
+
</Text>
|
|
70
|
+
<Text className="text-xs text-zinc-400">
|
|
71
|
+
{TYPE_LABELS[transaction.type] || 'Transaction'} - {formatRelativeTime(transaction.timestamp)}
|
|
72
|
+
</Text>
|
|
73
|
+
</View>
|
|
74
|
+
{transaction.amount !== undefined && (
|
|
75
|
+
<Text className="text-xs font-medium text-white">
|
|
76
|
+
{transaction.asset
|
|
77
|
+
? formatCurrency(transaction.amount, transaction.asset.unitName)
|
|
78
|
+
: formatCurrency(transaction.amount / 1_000_000, 'ALGO')}
|
|
79
|
+
</Text>
|
|
80
|
+
)}
|
|
81
|
+
</View>
|
|
82
|
+
),
|
|
83
|
+
[],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<View className={className}>
|
|
88
|
+
<SearchSheet
|
|
89
|
+
data={data}
|
|
90
|
+
placeholder={placeholder}
|
|
91
|
+
onSelect={handleSelect}
|
|
92
|
+
renderItem={renderItem}
|
|
93
|
+
/>
|
|
94
|
+
{selected && (
|
|
95
|
+
<View className="mt-3 p-3 rounded-lg bg-zinc-800 border border-zinc-700">
|
|
96
|
+
<Text className="text-xs font-medium text-zinc-400 mb-1">Selected Transaction</Text>
|
|
97
|
+
{renderItem(selected)}
|
|
98
|
+
</View>
|
|
99
|
+
)}
|
|
100
|
+
</View>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Self-fetching display components (backward compatible — accept data or self-fetch props)
|
|
2
|
+
export { Account } from './SelfFetching';
|
|
3
|
+
export { ASA } from './SelfFetching';
|
|
4
|
+
export { NFTListing } from './SelfFetching';
|
|
5
|
+
export { NFDProfile } from './SelfFetching';
|
|
6
|
+
export { TransactionDetails } from './SelfFetching';
|
|
7
|
+
export { Poll } from './SelfFetching';
|
|
8
|
+
export { RaffleListing } from './SelfFetching';
|
|
9
|
+
export { AuctionListing } from './SelfFetching';
|
|
10
|
+
export { TradeOffer } from './SelfFetching';
|
|
11
|
+
|
|
12
|
+
// Display-only components (no self-fetch, for internal/advanced use)
|
|
13
|
+
export { Account as AccountDisplay } from './Account';
|
|
14
|
+
export { ASAComponent as ASADisplay } from './ASA';
|
|
15
|
+
export { NFTListingComponent as NFTListingDisplay } from './NFTListing';
|
|
16
|
+
export { NFDProfileComponent as NFDProfileDisplay } from './NFDProfile';
|
|
17
|
+
export { TransactionDetailsComponent as TransactionDetailsDisplay } from './TransactionDetails';
|
|
18
|
+
export { PollComponent as PollDisplay } from './Poll';
|
|
19
|
+
export { RaffleListingComponent as RaffleListingDisplay } from './RaffleListing';
|
|
20
|
+
export { AuctionListingComponent as AuctionListingDisplay } from './AuctionListing';
|
|
21
|
+
export { TradeOfferComponent as TradeOfferDisplay } from './TradeOffer';
|
|
22
|
+
|
|
23
|
+
// Search Components
|
|
24
|
+
export { AccountSearch } from './AccountSearch';
|
|
25
|
+
export { ASASearch } from './ASASearch';
|
|
26
|
+
export { NFTSearch } from './NFTSearch';
|
|
27
|
+
export { NFDSearch } from './NFDSearch';
|
|
28
|
+
export { TransactionSearch } from './TransactionSearch';
|
|
29
|
+
export { PollSearch } from './PollSearch';
|
|
30
|
+
export { TradeSearch } from './TradeSearch';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { useAlgorandClient, useIndexerClient } from './useAlgorandClient'
|
|
2
|
+
export {
|
|
3
|
+
useAccountData,
|
|
4
|
+
useASAData,
|
|
5
|
+
useTransactionData,
|
|
6
|
+
useNFDProfileData,
|
|
7
|
+
usePollData,
|
|
8
|
+
useRaffleData,
|
|
9
|
+
useAuctionData,
|
|
10
|
+
useNFTListingData,
|
|
11
|
+
useTradeOfferData,
|
|
12
|
+
} from './useAlgomdData'
|