-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAccountHeader.tsx
More file actions
130 lines (124 loc) · 5.69 KB
/
Copy pathAccountHeader.tsx
File metadata and controls
130 lines (124 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { useState } from 'react';
import { Check, Copy, ExternalLink, LogOut } from 'lucide-react';
import { Button } from '@/components/base/button';
import { Card, CardContent } from '@/components/base/card';
import { Input } from '@/components/base/input';
import { explorerAccountUrl, explorerHistoryUrl, explorerTokensUrl } from '@/solana';
import { useAccount } from '@/state/account';
import { NetworkToggle } from './NetworkToggle';
export function AccountHeader() {
const { account, address, scalarHex, scalarBase58, network, rpcOverride, setRpcOverride, resetSession } =
useAccount();
const [copied, setCopied] = useState<'address' | 'scalar-hex' | 'scalar-b58' | null>(null);
if (!account) return null;
const truncated = `${address.slice(0, 6)}…${address.slice(-6)}`;
const copy = async (value: string, kind: 'address' | 'scalar-hex' | 'scalar-b58') => {
try {
await navigator.clipboard.writeText(value);
setCopied(kind);
setTimeout(() => setCopied(c => (c === kind ? null : c)), 1500);
} catch {
// best-effort copy
}
};
return (
<Card>
<CardContent className="para:flex para:flex-col para:items-start para:gap-4">
<NetworkToggle />
<div className="para:flex para:w-full para:flex-wrap para:items-center para:justify-between para:gap-4">
<div className="para:flex para:flex-col para:gap-1">
<span className="para:text-xs para:uppercase para:tracking-wide para:text-muted-foreground">Address</span>
<div className="para:flex para:items-center para:gap-2">
<code className="para:font-mono para:text-base">{truncated}</code>
<Button
size="icon"
variant="ghost"
onClick={() => copy(address, 'address')}
title="Copy full address"
>
{copied === 'address' ? <Check className="para:size-4" /> : <Copy className="para:size-4" />}
</Button>
</div>
</div>
<div className="para:flex para:flex-wrap para:items-center para:gap-2">
<Button
variant="outline"
size="sm"
onClick={() => window.open(explorerAccountUrl(address, network), '_blank', 'noreferrer')}
>
<ExternalLink className="para:size-4" />
View on Solscan
</Button>
<Button variant="outline" size="sm" onClick={resetSession}>
<LogOut className="para:size-4" />
Forget key
</Button>
</div>
</div>
<details className="para:w-full">
<summary className="para:cursor-pointer para:text-xs para:font-medium para:text-muted-foreground para:hover:text-foreground">
Custom RPC (advanced)
</summary>
<div className="para:mt-3 para:flex para:flex-col para:gap-2">
<p className="para:text-xs para:text-muted-foreground para:leading-relaxed">
Override the default {network} RPC. Use an Alchemy, Helius, or QuickNode URL when the default endpoint
rate-limits you or doesn't return SPL token holdings.
</p>
<Input
id="rpc-override"
type="text"
spellCheck={false}
autoComplete="off"
placeholder="leave blank for the bundled default"
value={rpcOverride}
onChange={(e) => setRpcOverride(e.target.value)}
/>
</div>
</details>
<details className="para:w-full">
<summary className="para:cursor-pointer para:text-xs para:font-medium para:text-muted-foreground para:hover:text-foreground">
Key details
</summary>
<div className="para:mt-3 para:flex para:flex-col para:gap-3 para:font-mono para:text-xs">
<div className="para:flex para:flex-col para:gap-1">
<span className="para:text-muted-foreground">Full address</span>
<code className="para:break-all">{address}</code>
</div>
<div className="para:flex para:flex-col para:gap-1">
<span className="para:text-muted-foreground">Scalar (hex)</span>
<code className="para:break-all">{scalarHex}</code>
<Button size="sm" variant="outline" onClick={() => copy(scalarHex, 'scalar-hex')}>
{copied === 'scalar-hex' ? 'Copied' : 'Copy hex'}
</Button>
</div>
<div className="para:flex para:flex-col para:gap-1">
<span className="para:text-muted-foreground">Scalar (base58)</span>
<code className="para:break-all">{scalarBase58}</code>
<Button size="sm" variant="outline" onClick={() => copy(scalarBase58, 'scalar-b58')}>
{copied === 'scalar-b58' ? 'Copied' : 'Copy base58'}
</Button>
</div>
<div className="para:flex para:flex-wrap para:gap-x-4 para:gap-y-1 para:text-xs">
<a
href={explorerHistoryUrl(address, network)}
target="_blank"
rel="noreferrer"
className="para:underline para:underline-offset-2 para:text-muted-foreground para:hover:text-foreground"
>
Transaction history ↗
</a>
<a
href={explorerTokensUrl(address, network)}
target="_blank"
rel="noreferrer"
className="para:underline para:underline-offset-2 para:text-muted-foreground para:hover:text-foreground"
>
Token accounts ↗
</a>
</div>
</div>
</details>
</CardContent>
</Card>
);
}