Skip to content

Commit 3d325cc

Browse files
committed
fix 13.0.1
1 parent 1e63170 commit 3d325cc

File tree

7 files changed

+323
-67
lines changed

7 files changed

+323
-67
lines changed

src/pages/Block.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/pages/BlockDetails.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { fetchBlockDetails } from '../utils/api';
55
import { BlockInfo, WebSocketTransaction } from '../types';
66
import { Skeleton, SkeletonText } from '../components/ui/Skeleton';
77
import { formatBitcoinValue } from '../utils/api';
8+
import { isBlockHash } from '../utils/validation';
89

910
export const BlockDetails: React.FC = () => {
1011
const { hashOrHeight } = useParams<{ hashOrHeight: string }>();
@@ -22,6 +23,13 @@ export const BlockDetails: React.FC = () => {
2223
return;
2324
}
2425

26+
// Validate block hash if it's not a height
27+
if (!/^\d+$/.test(hashOrHeight) && !isBlockHash(hashOrHeight)) {
28+
setError('Invalid block hash format');
29+
setIsLoading(false);
30+
return;
31+
}
32+
2533
setIsLoading(true);
2634
setError(null);
2735

@@ -42,8 +50,10 @@ export const BlockDetails: React.FC = () => {
4250
fetchBlock();
4351
}, [hashOrHeight]);
4452

45-
const copyToClipboard = (text: string) => {
46-
navigator.clipboard.writeText(text);
53+
const copyToClipboard = (text: string | undefined | null) => {
54+
if (text) {
55+
navigator.clipboard.writeText(text);
56+
}
4757
};
4858

4959
const formatTime = (timestamp: number): string => {

src/pages/BlockExplorer.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React, { useEffect, useState } from 'react';
22
import { Link } from 'react-router-dom';
3-
import { Zap, Clock, Database, ArrowRight, Calendar, Hash } from 'lucide-react';
3+
import { Zap, Clock, Database, ArrowRight, Calendar, Hash, Copy } from 'lucide-react';
44
import websocketService from '../services/websocketService';
55
import { fetchLatestBlocks } from '../utils/api';
66
import { BlockData } from '../types';
77
import { Card, CardContent, CardHeader, CardTitle, Container, PageHeader, Spinner, Skeleton } from '../components/ui';
8+
import { isBlockHash } from '../utils/validation';
89

910
export const BlockExplorer: React.FC = () => {
1011
const [blocks, setBlocks] = useState<BlockData[]>([]);
@@ -116,6 +117,10 @@ export const BlockExplorer: React.FC = () => {
116117
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
117118
};
118119

120+
const copyToClipboard = (text: string) => {
121+
navigator.clipboard.writeText(text);
122+
};
123+
119124
return (
120125
<Container>
121126
<PageHeader
@@ -203,7 +208,28 @@ export const BlockExplorer: React.FC = () => {
203208
<td>
204209
<div className="flex items-center">
205210
<Hash className="w-4 h-4 text-gray-500 mr-2 flex-shrink-0" />
206-
<span className="font-mono">{block.hash.substring(0, 16)}...</span>
211+
<div className="flex-1 min-w-0">
212+
{isBlockHash(block.hash) ? (
213+
<>
214+
<Link
215+
to={`/block/${block.hash}`}
216+
className="font-mono text-sm text-orange-500 hover:text-orange-400 block truncate"
217+
title={block.hash}
218+
>
219+
{block.hash}
220+
</Link>
221+
<button
222+
onClick={() => copyToClipboard(block.hash)}
223+
className="p-1 hover:bg-gray-800 rounded ml-2 flex-shrink-0"
224+
title="Copy block hash"
225+
>
226+
<Copy className="w-4 h-4" />
227+
</button>
228+
</>
229+
) : (
230+
<span className="text-red-500">Invalid block hash</span>
231+
)}
232+
</div>
207233
</div>
208234
</td>
209235
<td>

src/pages/LiveTransactions.tsx

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -289,16 +289,25 @@ export const LiveTransactions: React.FC = () => {
289289
input.prev_out?.addr && (
290290
<div key={`${tx.hash}-input-${index}`} className="flex flex-col sm:flex-row sm:items-center gap-2">
291291
<div className="min-w-0 flex-1">
292-
<Link
293-
to={`/address/${input.prev_out.addr}`}
294-
className="text-orange-500 hover:text-orange-400 font-mono text-sm break-all"
295-
style={{
296-
wordBreak: 'break-word',
297-
overflowWrap: 'break-word'
298-
}}
299-
>
300-
{input.prev_out.addr}
301-
</Link>
292+
<div className="flex items-center gap-2">
293+
<Link
294+
to={`/address/${input.prev_out.addr}`}
295+
className="text-orange-500 hover:text-orange-400 font-mono text-sm break-all flex-1"
296+
style={{
297+
wordBreak: 'break-word',
298+
overflowWrap: 'break-word'
299+
}}
300+
>
301+
{input.prev_out.addr}
302+
</Link>
303+
<button
304+
onClick={() => input.prev_out?.addr && copyToClipboard(input.prev_out.addr)}
305+
className="p-1 hover:bg-gray-800 rounded flex-shrink-0"
306+
title="Copy address"
307+
>
308+
<Copy className="w-4 h-4" />
309+
</button>
310+
</div>
302311
</div>
303312
<div className="text-sm text-gray-400 flex-shrink-0 whitespace-nowrap">
304313
{formatValue(input.prev_out.value)} BTC
@@ -328,16 +337,25 @@ export const LiveTransactions: React.FC = () => {
328337
output.addr && (
329338
<div key={`${tx.hash}-output-${index}`} className="flex flex-col sm:flex-row sm:items-center gap-2">
330339
<div className="min-w-0 flex-1">
331-
<Link
332-
to={`/address/${output.addr}`}
333-
className="text-green-500 hover:text-green-400 font-mono text-sm break-all"
334-
style={{
335-
wordBreak: 'break-word',
336-
overflowWrap: 'break-word'
337-
}}
338-
>
339-
{output.addr}
340-
</Link>
340+
<div className="flex items-center gap-2">
341+
<Link
342+
to={`/address/${output.addr}`}
343+
className="text-green-500 hover:text-green-400 font-mono text-sm break-all flex-1"
344+
style={{
345+
wordBreak: 'break-word',
346+
overflowWrap: 'break-word'
347+
}}
348+
>
349+
{output.addr}
350+
</Link>
351+
<button
352+
onClick={() => output.addr && copyToClipboard(output.addr)}
353+
className="p-1 hover:bg-gray-800 rounded flex-shrink-0"
354+
title="Copy address"
355+
>
356+
<Copy className="w-4 h-4" />
357+
</button>
358+
</div>
341359
</div>
342360
<div className="text-sm text-gray-400 flex-shrink-0 whitespace-nowrap">
343361
{formatValue(output.value)} BTC

src/pages/MempoolExplorer.tsx

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useState } from 'react';
22
import { Link } from 'react-router-dom';
3-
import { Clock, Filter, DollarSign, ArrowRight, Zap, BarChart2 } from 'lucide-react';
3+
import { Clock, Filter, DollarSign, ArrowRight, Zap, BarChart2, Copy } from 'lucide-react';
44
import websocketService from '../services/websocketService';
55
import { WebSocketTransaction } from '../types';
66

@@ -196,6 +196,10 @@ export const MempoolExplorer: React.FC = () => {
196196
})
197197
: transactions;
198198

199+
const copyToClipboard = (text: string) => {
200+
navigator.clipboard.writeText(text);
201+
};
202+
199203
return (
200204
<div className="max-w-7xl mx-auto space-y-6">
201205
<div className="flex items-center justify-between mb-6">
@@ -344,13 +348,26 @@ export const MempoolExplorer: React.FC = () => {
344348
<div className="space-y-1">
345349
{tx.inputs.slice(0, 2).map((input, idx) => (
346350
input.prev_out?.addr && (
347-
<div key={idx} className="text-sm truncate">
348-
<Link
349-
to={`/address/${input.prev_out.addr}`}
350-
className="text-blue-400 hover:text-blue-300 font-mono"
351-
>
352-
{input.prev_out.addr}
353-
</Link>
351+
<div key={idx} className="text-sm">
352+
<div className="flex items-center gap-2">
353+
<Link
354+
to={`/address/${input.prev_out.addr}`}
355+
className="text-blue-400 hover:text-blue-300 font-mono break-all flex-1"
356+
style={{
357+
wordBreak: 'break-word',
358+
overflowWrap: 'break-word'
359+
}}
360+
>
361+
{input.prev_out.addr}
362+
</Link>
363+
<button
364+
onClick={() => input.prev_out?.addr && copyToClipboard(input.prev_out.addr)}
365+
className="p-1 hover:bg-gray-800 rounded flex-shrink-0"
366+
title="Copy address"
367+
>
368+
<Copy className="w-4 h-4" />
369+
</button>
370+
</div>
354371
</div>
355372
)
356373
))}
@@ -371,13 +388,26 @@ export const MempoolExplorer: React.FC = () => {
371388
<div className="space-y-1">
372389
{tx.out.slice(0, 2).map((output, idx) => (
373390
output.addr && (
374-
<div key={idx} className="text-sm truncate">
375-
<Link
376-
to={`/address/${output.addr}`}
377-
className="text-green-400 hover:text-green-300 font-mono"
378-
>
379-
{output.addr}
380-
</Link>
391+
<div key={idx} className="text-sm">
392+
<div className="flex items-center gap-2">
393+
<Link
394+
to={`/address/${output.addr}`}
395+
className="text-green-400 hover:text-green-300 font-mono break-all flex-1"
396+
style={{
397+
wordBreak: 'break-word',
398+
overflowWrap: 'break-word'
399+
}}
400+
>
401+
{output.addr}
402+
</Link>
403+
<button
404+
onClick={() => output.addr && copyToClipboard(output.addr)}
405+
className="p-1 hover:bg-gray-800 rounded flex-shrink-0"
406+
title="Copy address"
407+
>
408+
<Copy className="w-4 h-4" />
409+
</button>
410+
</div>
381411
</div>
382412
)
383413
))}

src/pages/Transaction.tsx

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,25 @@ export const Transaction: React.FC = () => {
221221
<div className="min-w-0 flex-1">
222222
{input.addresses && input.addresses.length > 0 ? (
223223
input.addresses.map((addr) => (
224-
<Link
225-
key={addr}
226-
to={`/address/${addr}`}
227-
className="block text-orange-500 hover:text-orange-400 font-mono text-sm break-all"
228-
style={{
229-
wordBreak: 'break-word',
230-
overflowWrap: 'break-word'
231-
}}
232-
>
233-
{addr}
234-
</Link>
224+
<div key={addr} className="flex items-center gap-2">
225+
<Link
226+
to={`/address/${addr}`}
227+
className="block text-orange-500 hover:text-orange-400 font-mono text-sm break-all flex-1"
228+
style={{
229+
wordBreak: 'break-word',
230+
overflowWrap: 'break-word'
231+
}}
232+
>
233+
{addr}
234+
</Link>
235+
<button
236+
onClick={() => copyToClipboard(addr)}
237+
className="p-1 hover:bg-gray-800 rounded flex-shrink-0"
238+
title="Copy address"
239+
>
240+
<Copy className="w-4 h-4" />
241+
</button>
242+
</div>
235243
))
236244
) : (
237245
<span className="text-gray-500 italic">No address data available</span>
@@ -279,17 +287,25 @@ export const Transaction: React.FC = () => {
279287
<div className="min-w-0 flex-1">
280288
{output.addresses && output.addresses.length > 0 ? (
281289
output.addresses.map((addr) => (
282-
<Link
283-
key={addr}
284-
to={`/address/${addr}`}
285-
className="block text-green-500 hover:text-green-400 font-mono text-sm break-all"
286-
style={{
287-
wordBreak: 'break-word',
288-
overflowWrap: 'break-word'
289-
}}
290-
>
291-
{addr}
292-
</Link>
290+
<div key={addr} className="flex items-center gap-2">
291+
<Link
292+
to={`/address/${addr}`}
293+
className="block text-green-500 hover:text-green-400 font-mono text-sm break-all flex-1"
294+
style={{
295+
wordBreak: 'break-word',
296+
overflowWrap: 'break-word'
297+
}}
298+
>
299+
{addr}
300+
</Link>
301+
<button
302+
onClick={() => copyToClipboard(addr)}
303+
className="p-1 hover:bg-gray-800 rounded flex-shrink-0"
304+
title="Copy address"
305+
>
306+
<Copy className="w-4 h-4" />
307+
</button>
308+
</div>
293309
))
294310
) : (
295311
<span className="text-gray-500 italic">No address data available</span>

0 commit comments

Comments
 (0)