feat: sitewide Solana wallet connect in navbar
This commit is contained in:
parent
e854944704
commit
4fdd4aaf1c
32 changed files with 857 additions and 198 deletions
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/js/blog.js"></script>
|
<script src="/js/blog.js"></script>
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
288
css/style.css
288
css/style.css
|
|
@ -328,6 +328,265 @@ a:hover { color: #fff; text-shadow: none; }
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === NAV WALLET BUTTON === */
|
||||||
|
.nav-wallet-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
padding: 0.35rem 0.85rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
color: var(--warning);
|
||||||
|
background: rgba(201, 162, 39, 0.08);
|
||||||
|
border: 1px solid rgba(201, 162, 39, 0.3);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-btn:hover {
|
||||||
|
background: rgba(201, 162, 39, 0.15);
|
||||||
|
border-color: rgba(201, 162, 39, 0.5);
|
||||||
|
color: #e0c040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-btn.connected {
|
||||||
|
color: #14F195;
|
||||||
|
background: rgba(20, 241, 149, 0.06);
|
||||||
|
border-color: rgba(20, 241, 149, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-btn.connected:hover {
|
||||||
|
background: rgba(20, 241, 149, 0.12);
|
||||||
|
border-color: rgba(20, 241, 149, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-icon {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-dot {
|
||||||
|
color: #14F195;
|
||||||
|
font-size: 0.55rem;
|
||||||
|
text-shadow: 0 0 6px rgba(20, 241, 149, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-label {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wallet Dropdown */
|
||||||
|
.nav-wallet-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 8px);
|
||||||
|
right: 0;
|
||||||
|
min-width: 280px;
|
||||||
|
max-width: 340px;
|
||||||
|
background: rgba(14, 14, 14, 0.98);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-top: 2px solid #14F195;
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: 1100;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
animation: nwDropIn 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-dropdown.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes nwDropIn {
|
||||||
|
from { opacity: 0; transform: translateY(-6px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown labels */
|
||||||
|
.nw-dd-label {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 0.6rem;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
padding: 0.5rem 0.85rem 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-label-dim {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connected section */
|
||||||
|
.nw-dd-section {
|
||||||
|
padding: 0.5rem 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-addr {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 0.6rem;
|
||||||
|
margin: 0.35rem 0;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-addr:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border-color: rgba(20, 241, 149, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-addr-text {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.6rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-copy-icon {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-via {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.55rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown actions */
|
||||||
|
.nw-dd-actions {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.6rem 0.85rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.65rem;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-action:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-action:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
color: #fff;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-disconnect:hover {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wallet picker items */
|
||||||
|
.nw-dd-wallet {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.55rem 0.85rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.65rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet-icon {
|
||||||
|
font-size: 1rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 1.4rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet-name {
|
||||||
|
flex: 1;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet-tag {
|
||||||
|
font-size: 0.5rem;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #14F195;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet-install .nw-dd-wallet-tag {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-wallet-install .nw-dd-wallet-name {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-error {
|
||||||
|
color: var(--danger) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-divider {
|
||||||
|
height: 1px;
|
||||||
|
background: var(--border);
|
||||||
|
margin: 0.35rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-empty {
|
||||||
|
padding: 1rem 0.85rem;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.65rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nw-dd-empty-hint {
|
||||||
|
font-size: 0.55rem;
|
||||||
|
color: #14F195;
|
||||||
|
}
|
||||||
|
|
||||||
/* === HERO SECTION === */
|
/* === HERO SECTION === */
|
||||||
/* ============================
|
/* ============================
|
||||||
HERO HUD GRID LAYOUT
|
HERO HUD GRID LAYOUT
|
||||||
|
|
@ -1629,6 +1888,35 @@ a:hover { color: #fff; text-shadow: none; }
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wallet button mobile */
|
||||||
|
.nav-wallet-wrap {
|
||||||
|
position: static;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wallet-dropdown {
|
||||||
|
position: static;
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
box-shadow: none;
|
||||||
|
backdrop-filter: none;
|
||||||
|
background: rgba(14, 14, 14, 0.95);
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* HUD grid stacks on mobile */
|
/* HUD grid stacks on mobile */
|
||||||
.hero-hud-grid {
|
.hero-hud-grid {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="/js/contraband.js"></script>
|
<script src="/js/contraband.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
<div class="footer-right"><span class="footer-signal">SIGNAL ████<span class="signal-flicker">█</span></span></div>
|
<div class="footer-right"><span class="footer-signal">SIGNAL ████<span class="signal-flicker">█</span></span></div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="/js/awesomelist.js?v=20260404"></script>
|
<script src="/js/awesomelist.js?v=20260404"></script>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="/js/changelog.js"></script>
|
<script src="/js/changelog.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -582,6 +582,7 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/js/main.js"></script>
|
<script src="/js/main.js"></script>
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/chat.js"></script>
|
<script src="/js/chat.js"></script>
|
||||||
<script src="/js/globe.js"></script>
|
<script src="/js/globe.js"></script>
|
||||||
|
|
|
||||||
223
js/nav.js
223
js/nav.js
|
|
@ -1,5 +1,6 @@
|
||||||
/* ─── Dynamic Navigation with Dropdown Submenus ─── */
|
/* ─── Dynamic Navigation with Dropdown Submenus ─── */
|
||||||
/* Fetches nav items from /api/navigation and renders dropdowns */
|
/* Fetches nav items from /api/navigation and renders dropdowns */
|
||||||
|
/* Includes sitewide wallet connect button via window.solWallet */
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
@ -84,6 +85,9 @@
|
||||||
|
|
||||||
// Bind mobile dropdown toggles after rendering
|
// Bind mobile dropdown toggles after rendering
|
||||||
initMobileDropdowns();
|
initMobileDropdowns();
|
||||||
|
|
||||||
|
// Inject wallet button after nav loads
|
||||||
|
injectWalletButton();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Nav load failed, keeping existing:', err);
|
console.warn('Nav load failed, keeping existing:', err);
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +115,6 @@
|
||||||
parent.classList.add('dropdown-open');
|
parent.classList.add('dropdown-open');
|
||||||
} else {
|
} else {
|
||||||
// Dropdown already open — allow navigation or close
|
// Dropdown already open — allow navigation or close
|
||||||
// If tapped again on same parent link, close dropdown
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
parent.classList.remove('dropdown-open');
|
parent.classList.remove('dropdown-open');
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +150,224 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Wallet Button Injection ────────────────────────────────
|
||||||
|
function injectWalletButton() {
|
||||||
|
const navContainer = document.querySelector('.nav-container');
|
||||||
|
const navStatus = document.querySelector('.nav-status');
|
||||||
|
if (!navContainer) return;
|
||||||
|
|
||||||
|
// Don't inject twice
|
||||||
|
if (document.getElementById('navWalletWrap')) return;
|
||||||
|
|
||||||
|
// Create wallet wrapper
|
||||||
|
const wrap = document.createElement('div');
|
||||||
|
wrap.id = 'navWalletWrap';
|
||||||
|
wrap.className = 'nav-wallet-wrap';
|
||||||
|
wrap.innerHTML = `
|
||||||
|
<button class="nav-wallet-btn" id="navWalletBtn">
|
||||||
|
<span class="nav-wallet-icon">◆</span>
|
||||||
|
<span class="nav-wallet-label">CONNECT</span>
|
||||||
|
</button>
|
||||||
|
<div class="nav-wallet-dropdown hidden" id="navWalletDropdown"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Insert before nav-status (or append to container)
|
||||||
|
if (navStatus) {
|
||||||
|
navContainer.insertBefore(wrap, navStatus);
|
||||||
|
} else {
|
||||||
|
navContainer.appendChild(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind click
|
||||||
|
const btn = document.getElementById('navWalletBtn');
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleWalletDropdown();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close dropdown on outside click
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!e.target.closest('#navWalletWrap')) {
|
||||||
|
closeWalletDropdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for wallet events
|
||||||
|
window.addEventListener('wallet-connected', () => updateNavWalletUI());
|
||||||
|
window.addEventListener('wallet-disconnected', () => updateNavWalletUI());
|
||||||
|
|
||||||
|
// Set initial state
|
||||||
|
updateNavWalletUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNavWalletUI() {
|
||||||
|
const btn = document.getElementById('navWalletBtn');
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
const sw = window.solWallet;
|
||||||
|
if (sw && sw.connected && sw.address) {
|
||||||
|
btn.className = 'nav-wallet-btn connected';
|
||||||
|
btn.innerHTML = `
|
||||||
|
<span class="nav-wallet-dot">●</span>
|
||||||
|
<span class="nav-wallet-label">${sw.truncAddr(sw.address)}</span>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
btn.className = 'nav-wallet-btn';
|
||||||
|
btn.innerHTML = `
|
||||||
|
<span class="nav-wallet-icon">◆</span>
|
||||||
|
<span class="nav-wallet-label">CONNECT</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close dropdown on state change
|
||||||
|
closeWalletDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWalletDropdown() {
|
||||||
|
const dd = document.getElementById('navWalletDropdown');
|
||||||
|
if (!dd) return;
|
||||||
|
|
||||||
|
if (dd.classList.contains('hidden')) {
|
||||||
|
openWalletDropdown();
|
||||||
|
} else {
|
||||||
|
closeWalletDropdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openWalletDropdown() {
|
||||||
|
const dd = document.getElementById('navWalletDropdown');
|
||||||
|
if (!dd) return;
|
||||||
|
|
||||||
|
const sw = window.solWallet;
|
||||||
|
if (!sw) {
|
||||||
|
dd.innerHTML = '<div class="nw-dd-empty">WALLET MODULE NOT LOADED</div>';
|
||||||
|
dd.classList.remove('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sw.connected && sw.address) {
|
||||||
|
// ── Connected dropdown: address, copy, solscan, disconnect ──
|
||||||
|
const addr = sw.address;
|
||||||
|
const solscan = 'https://solscan.io/account/' + addr;
|
||||||
|
dd.innerHTML = `
|
||||||
|
<div class="nw-dd-section">
|
||||||
|
<div class="nw-dd-label">CONNECTED WALLET</div>
|
||||||
|
<div class="nw-dd-addr" id="nwCopyAddr" title="Click to copy">
|
||||||
|
<span class="nw-dd-addr-text">${addr}</span>
|
||||||
|
<span class="nw-dd-copy-icon">⧉</span>
|
||||||
|
</div>
|
||||||
|
<div class="nw-dd-via">VIA ${sw.walletName || 'WALLET'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="nw-dd-actions">
|
||||||
|
<a href="${solscan}" target="_blank" rel="noopener" class="nw-dd-action">
|
||||||
|
<span>SOLSCAN</span><span>↗</span>
|
||||||
|
</a>
|
||||||
|
<button class="nw-dd-action nw-dd-disconnect" id="nwDisconnect">
|
||||||
|
<span>DISCONNECT</span><span>✕</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Copy address
|
||||||
|
dd.querySelector('#nwCopyAddr').addEventListener('click', () => {
|
||||||
|
navigator.clipboard.writeText(addr).then(() => {
|
||||||
|
const el = dd.querySelector('#nwCopyAddr .nw-dd-copy-icon');
|
||||||
|
if (el) { el.textContent = '✓'; setTimeout(() => el.textContent = '⧉', 1500); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disconnect
|
||||||
|
dd.querySelector('#nwDisconnect').addEventListener('click', () => {
|
||||||
|
sw.disconnect();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// ── Disconnected dropdown: wallet picker ──
|
||||||
|
// Short delay for wallet detection
|
||||||
|
setTimeout(() => {
|
||||||
|
const available = sw.getAvailableWallets();
|
||||||
|
const detectedNames = new Set(available.map(w => w.name));
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
if (available.length > 0) {
|
||||||
|
html += '<div class="nw-dd-label">SELECT WALLET</div>';
|
||||||
|
html += available.map(w => `
|
||||||
|
<button class="nw-dd-wallet" data-wallet="${w.name}">
|
||||||
|
<span class="nw-dd-wallet-icon">${w.icon}</span>
|
||||||
|
<span class="nw-dd-wallet-name">${w.name}</span>
|
||||||
|
<span class="nw-dd-wallet-tag">DETECTED</span>
|
||||||
|
</button>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show install links for missing wallets
|
||||||
|
const missing = sw.KNOWN_WALLETS.filter(w => !detectedNames.has(w.name));
|
||||||
|
if (missing.length > 0 && available.length < 3) {
|
||||||
|
html += '<div class="nw-dd-divider"></div>';
|
||||||
|
html += '<div class="nw-dd-label nw-dd-label-dim">INSTALL</div>';
|
||||||
|
html += missing.slice(0, 3).map(w => `
|
||||||
|
<a href="${w.url}" target="_blank" rel="noopener" class="nw-dd-wallet nw-dd-wallet-install">
|
||||||
|
<span class="nw-dd-wallet-icon">${w.icon}</span>
|
||||||
|
<span class="nw-dd-wallet-name">${w.name}</span>
|
||||||
|
<span class="nw-dd-wallet-tag">INSTALL ↗</span>
|
||||||
|
</a>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (available.length === 0) {
|
||||||
|
html = `
|
||||||
|
<div class="nw-dd-empty">
|
||||||
|
NO WALLETS DETECTED<br>
|
||||||
|
<span class="nw-dd-empty-hint">Install a Solana wallet extension</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
// Still show install links
|
||||||
|
html += sw.KNOWN_WALLETS.slice(0, 4).map(w => `
|
||||||
|
<a href="${w.url}" target="_blank" rel="noopener" class="nw-dd-wallet nw-dd-wallet-install">
|
||||||
|
<span class="nw-dd-wallet-icon">${w.icon}</span>
|
||||||
|
<span class="nw-dd-wallet-name">${w.name}</span>
|
||||||
|
<span class="nw-dd-wallet-tag">INSTALL ↗</span>
|
||||||
|
</a>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.innerHTML = html;
|
||||||
|
|
||||||
|
// Bind detected wallet clicks
|
||||||
|
dd.querySelectorAll('.nw-dd-wallet[data-wallet]').forEach(el => {
|
||||||
|
el.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const name = el.dataset.wallet;
|
||||||
|
const wallet = available.find(w => w.name === name);
|
||||||
|
if (!wallet) return;
|
||||||
|
|
||||||
|
// Show connecting state
|
||||||
|
el.querySelector('.nw-dd-wallet-tag').textContent = 'CONNECTING...';
|
||||||
|
try {
|
||||||
|
await sw.connect(wallet);
|
||||||
|
} catch (err) {
|
||||||
|
el.querySelector('.nw-dd-wallet-tag').textContent = 'REJECTED';
|
||||||
|
el.querySelector('.nw-dd-wallet-tag').classList.add('nw-dd-error');
|
||||||
|
setTimeout(() => {
|
||||||
|
if (el.querySelector('.nw-dd-wallet-tag')) {
|
||||||
|
el.querySelector('.nw-dd-wallet-tag').textContent = 'DETECTED';
|
||||||
|
el.querySelector('.nw-dd-wallet-tag').classList.remove('nw-dd-error');
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWalletDropdown() {
|
||||||
|
const dd = document.getElementById('navWalletDropdown');
|
||||||
|
if (dd) dd.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
// Run on DOM ready
|
// Run on DOM ready
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
|
||||||
248
js/soldomains.js
248
js/soldomains.js
|
|
@ -234,167 +234,39 @@ async function doReverse() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── WALLET ─────────────────────────────────────────────────
|
// ─── WALLET (uses global window.solWallet from wallet-connect.js) ────────────
|
||||||
let connectedProvider = null;
|
|
||||||
|
|
||||||
// ── Wallet Standard Discovery ──
|
// Sync local walletAddress from global wallet state
|
||||||
// MetaMask Solana and modern wallets register via the Wallet Standard protocol
|
function syncWalletState() {
|
||||||
const walletStandardWallets = [];
|
const sw = window.solWallet;
|
||||||
let walletStandardReady = false;
|
if (sw && sw.connected && sw.address) {
|
||||||
|
walletAddress = sw.address;
|
||||||
function initWalletStandard() {
|
} else {
|
||||||
// Listen for wallets that register AFTER us
|
walletAddress = null;
|
||||||
window.addEventListener('wallet-standard:register-wallet', (event) => {
|
|
||||||
try {
|
|
||||||
const callback = event.detail;
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback({
|
|
||||||
register: (...wallets) => {
|
|
||||||
for (const w of wallets) {
|
|
||||||
if (!walletStandardWallets.some(existing => existing.name === w.name)) {
|
|
||||||
walletStandardWallets.push(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch(e) { console.warn('WS register error:', e); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Announce that the app is ready — catches wallets that loaded BEFORE us
|
|
||||||
try {
|
|
||||||
const appReadyEvent = new CustomEvent('wallet-standard:app-ready', {
|
|
||||||
detail: {
|
|
||||||
register: (...wallets) => {
|
|
||||||
for (const w of wallets) {
|
|
||||||
if (!walletStandardWallets.some(existing => existing.name === w.name)) {
|
|
||||||
walletStandardWallets.push(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.dispatchEvent(appReadyEvent);
|
|
||||||
} catch(e) { console.warn('WS app-ready error:', e); }
|
|
||||||
|
|
||||||
walletStandardReady = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise Wallet Standard immediately
|
|
||||||
initWalletStandard();
|
|
||||||
|
|
||||||
function getAvailableWallets() {
|
|
||||||
const found = [];
|
|
||||||
const seen = new Set();
|
|
||||||
|
|
||||||
function add(name, icon, provider, url, isWalletStandard) {
|
|
||||||
if (!provider || seen.has(name)) return;
|
|
||||||
seen.add(name);
|
|
||||||
found.push({ name, icon, provider, url, isWalletStandard: !!isWalletStandard });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 1. Wallet Standard wallets (MetaMask Solana, etc.) ──
|
|
||||||
for (const w of walletStandardWallets) {
|
|
||||||
try {
|
|
||||||
const hasConnect = w.features?.['standard:connect'];
|
|
||||||
if (!hasConnect) continue;
|
|
||||||
const name = (w.name || 'WALLET').toUpperCase();
|
|
||||||
// Use wallet's icon (base64 data URI) or fallback emoji
|
|
||||||
let icon = '◈';
|
|
||||||
if (name.includes('METAMASK')) icon = '🦊';
|
|
||||||
else if (name.includes('PHANTOM')) icon = '👻';
|
|
||||||
else if (name.includes('SOLFLARE')) icon = '🔆';
|
|
||||||
else if (name.includes('BACKPACK')) icon = '🎒';
|
|
||||||
else if (name.includes('COINBASE')) icon = '🔵';
|
|
||||||
else if (name.includes('TRUST')) icon = '🛡️';
|
|
||||||
else if (name.includes('JUPITER')) icon = '🪐';
|
|
||||||
const url = w.url || '#';
|
|
||||||
add(name, icon, w, url, true);
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 2. Legacy injection points ──
|
|
||||||
|
|
||||||
// Phantom
|
|
||||||
try {
|
|
||||||
const p = window.phantom?.solana;
|
|
||||||
if (p && p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app');
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// Solflare
|
|
||||||
try {
|
|
||||||
const s = window.solflare;
|
|
||||||
if (s && s.isSolflare) add('SOLFLARE', '🔆', s, 'https://solflare.com');
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// Backpack
|
|
||||||
try {
|
|
||||||
const b = window.backpack;
|
|
||||||
if (b && b.isBackpack) add('BACKPACK', '🎒', b, 'https://backpack.app');
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// Coinbase
|
|
||||||
try {
|
|
||||||
if (window.coinbaseSolana) add('COINBASE', '🔵', window.coinbaseSolana, 'https://coinbase.com/wallet');
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// Trust
|
|
||||||
try {
|
|
||||||
const t = window.trustwallet?.solana;
|
|
||||||
if (t) add('TRUST', '🛡️', t, 'https://trustwallet.com');
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// ── 3. Generic window.solana ──
|
|
||||||
try {
|
|
||||||
const ws = window.solana;
|
|
||||||
if (ws && !seen.has('SOLANA WALLET')) {
|
|
||||||
if (ws.isJupiter && !seen.has('JUPITER')) add('JUPITER', '🪐', ws, 'https://jup.ag');
|
|
||||||
else if (ws.isMetaMask && !seen.has('METAMASK')) add('METAMASK', '🦊', ws, 'https://metamask.io');
|
|
||||||
else if (ws.isPhantom && !seen.has('PHANTOM')) { /* skip */ }
|
|
||||||
else if (ws.isSolflare && !seen.has('SOLFLARE')){ /* skip */ }
|
|
||||||
else if (ws.isBackpack && !seen.has('BACKPACK')){ /* skip */ }
|
|
||||||
else if (!seen.has('PHANTOM') && !seen.has('METAMASK')) add('SOLANA WALLET', '◈', ws, '#');
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// ── 4. Jupiter specific ──
|
|
||||||
try {
|
|
||||||
const j = window.jupiter?.solana;
|
|
||||||
if (j) add('JUPITER', '🪐', j, 'https://jup.ag');
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// ── 5. Multi-provider array ──
|
|
||||||
try {
|
|
||||||
const providers = window.solana?.providers;
|
|
||||||
if (Array.isArray(providers)) {
|
|
||||||
for (const p of providers) {
|
|
||||||
if (p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app');
|
|
||||||
else if (p.isJupiter) add('JUPITER', '🪐', p, 'https://jup.ag');
|
|
||||||
else if (p.isSolflare) add('SOLFLARE', '🔆', p, 'https://solflare.com');
|
|
||||||
else if (p.isBackpack) add('BACKPACK', '🎒', p, 'https://backpack.app');
|
|
||||||
else if (p.isMetaMask) add('METAMASK', '🦊', p, 'https://metamask.io');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KNOWN_WALLETS = [
|
|
||||||
{ name: 'PHANTOM', icon: '👻', url: 'https://phantom.app' },
|
|
||||||
{ name: 'JUPITER', icon: '🪐', url: 'https://jup.ag' },
|
|
||||||
{ name: 'SOLFLARE', icon: '🔆', url: 'https://solflare.com' },
|
|
||||||
{ name: 'BACKPACK', icon: '🎒', url: 'https://backpack.app' },
|
|
||||||
{ name: 'COINBASE', icon: '🔵', url: 'https://coinbase.com/wallet' },
|
|
||||||
{ name: 'TRUST', icon: '🛡️', url: 'https://trustwallet.com' },
|
|
||||||
{ name: 'METAMASK', icon: '🦊', url: 'https://metamask.io' },
|
|
||||||
];
|
|
||||||
|
|
||||||
function initWallet() {
|
function initWallet() {
|
||||||
const btn = $('#wallet-btn');
|
const btn = $('#wallet-btn');
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
btn.addEventListener('click', toggleWallet);
|
btn.addEventListener('click', toggleWallet);
|
||||||
createWalletModal();
|
createWalletModal();
|
||||||
|
|
||||||
|
// Listen for global wallet events
|
||||||
|
window.addEventListener('wallet-connected', (e) => {
|
||||||
|
walletAddress = e.detail.address;
|
||||||
|
updateWalletUI();
|
||||||
|
checkPendingRegistration();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('wallet-disconnected', () => {
|
||||||
|
walletAddress = null;
|
||||||
|
updateWalletUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync initial state (wallet may have auto-reconnected already)
|
||||||
|
syncWalletState();
|
||||||
|
updateWalletUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWalletModal() {
|
function createWalletModal() {
|
||||||
|
|
@ -420,12 +292,17 @@ function createWalletModal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function openModal() {
|
function openModal() {
|
||||||
|
const sw = window.solWallet;
|
||||||
|
if (!sw) { console.warn('solWallet not loaded'); return; }
|
||||||
|
|
||||||
const modal = $('#wallet-modal');
|
const modal = $('#wallet-modal');
|
||||||
const list = $('#wallet-list');
|
const list = $('#wallet-list');
|
||||||
|
|
||||||
|
const KNOWN_WALLETS = sw.KNOWN_WALLETS || [];
|
||||||
|
|
||||||
// Small delay to let async provider injection complete
|
// Small delay to let async provider injection complete
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const available = getAvailableWallets();
|
const available = sw.getAvailableWallets();
|
||||||
const detectedNames = new Set(available.map(w => w.name));
|
const detectedNames = new Set(available.map(w => w.name));
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
@ -464,12 +341,21 @@ function openModal() {
|
||||||
list.innerHTML = html;
|
list.innerHTML = html;
|
||||||
|
|
||||||
list.querySelectorAll('.sol-wallet-option.detected').forEach(btn => {
|
list.querySelectorAll('.sol-wallet-option.detected').forEach(btn => {
|
||||||
btn.addEventListener('click', (e) => {
|
btn.addEventListener('click', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const name = btn.dataset.wallet;
|
const name = btn.dataset.wallet;
|
||||||
const wallet = available.find(w => w.name === name);
|
const wallet = available.find(w => w.name === name);
|
||||||
if (wallet) connectWallet(wallet);
|
if (wallet) {
|
||||||
|
try {
|
||||||
|
closeModal();
|
||||||
|
await sw.connect(wallet);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Wallet connection failed:', err);
|
||||||
|
const status = $('#wallet-status');
|
||||||
|
if (status) status.innerHTML = `<span style="color:rgba(255,50,50,0.8)">CONNECTION REJECTED</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -490,44 +376,10 @@ async function toggleWallet() {
|
||||||
openModal();
|
openModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function connectWallet(wallet) {
|
|
||||||
try {
|
|
||||||
closeModal();
|
|
||||||
|
|
||||||
if (wallet.isWalletStandard) {
|
|
||||||
// Wallet Standard connect (MetaMask Solana, etc.)
|
|
||||||
const connectFeature = wallet.provider.features['standard:connect'];
|
|
||||||
const result = await connectFeature.connect();
|
|
||||||
const accounts = result.accounts || [];
|
|
||||||
if (accounts.length > 0) {
|
|
||||||
walletAddress = accounts[0].address;
|
|
||||||
connectedProvider = wallet;
|
|
||||||
updateWalletUI();
|
|
||||||
checkPendingRegistration();
|
|
||||||
} else {
|
|
||||||
throw new Error('No accounts returned');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Legacy provider connect (Phantom, Solflare, etc.)
|
|
||||||
const resp = await wallet.provider.connect();
|
|
||||||
walletAddress = resp.publicKey.toString();
|
|
||||||
connectedProvider = wallet;
|
|
||||||
updateWalletUI();
|
|
||||||
checkPendingRegistration();
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
console.error('Wallet connection failed:', err);
|
|
||||||
const status = $('#wallet-status');
|
|
||||||
if (status) status.innerHTML = `<span style="color:rgba(255,50,50,0.8)">CONNECTION REJECTED</span>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnectWallet() {
|
function disconnectWallet() {
|
||||||
if (connectedProvider && connectedProvider.provider) {
|
const sw = window.solWallet;
|
||||||
try { connectedProvider.provider.disconnect(); } catch(e) {}
|
if (sw) sw.disconnect();
|
||||||
}
|
|
||||||
walletAddress = null;
|
walletAddress = null;
|
||||||
connectedProvider = null;
|
|
||||||
updateWalletUI();
|
updateWalletUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,9 +387,11 @@ function updateWalletUI() {
|
||||||
const status = $('#wallet-status');
|
const status = $('#wallet-status');
|
||||||
const btn = $('#wallet-btn');
|
const btn = $('#wallet-btn');
|
||||||
|
|
||||||
|
const sw = window.solWallet;
|
||||||
|
|
||||||
if (walletAddress) {
|
if (walletAddress) {
|
||||||
const wName = connectedProvider ? connectedProvider.name : 'WALLET';
|
const wName = (sw && sw.walletName) ? sw.walletName : 'WALLET';
|
||||||
const wIcon = connectedProvider ? connectedProvider.icon : '◈';
|
const wIcon = '◆';
|
||||||
status.className = 'sol-wallet-status connected';
|
status.className = 'sol-wallet-status connected';
|
||||||
status.innerHTML = `● CONNECTED VIA ${wIcon} ${wName} <span class="sol-wallet-address">${truncAddr(walletAddress)}</span>`;
|
status.innerHTML = `● CONNECTED VIA ${wIcon} ${wName} <span class="sol-wallet-address">${truncAddr(walletAddress)}</span>`;
|
||||||
btn.className = 'sol-wallet-btn disconnect';
|
btn.className = 'sol-wallet-btn disconnect';
|
||||||
|
|
@ -784,7 +638,7 @@ function updateRegistrationStatus(state, message, extra) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeRegistration(domain) {
|
async function executeRegistration(domain) {
|
||||||
if (!walletAddress || !connectedProvider) {
|
if (!walletAddress || !window.solWallet?.provider) {
|
||||||
updateRegistrationStatus('error', 'WALLET NOT CONNECTED',
|
updateRegistrationStatus('error', 'WALLET NOT CONNECTED',
|
||||||
'<p class="sol-reg-error-detail">Please connect your wallet and try again.</p>');
|
'<p class="sol-reg-error-detail">Please connect your wallet and try again.</p>');
|
||||||
return;
|
return;
|
||||||
|
|
@ -854,7 +708,7 @@ async function executeRegistration(domain) {
|
||||||
// 6. Sign and send via connected wallet
|
// 6. Sign and send via connected wallet
|
||||||
let signature;
|
let signature;
|
||||||
|
|
||||||
if (connectedProvider.isWalletStandard) {
|
if (window.solWallet && window.solWallet.isWalletStandard) {
|
||||||
signature = await signAndSendWalletStandard(transaction, isVersioned, connection);
|
signature = await signAndSendWalletStandard(transaction, isVersioned, connection);
|
||||||
} else {
|
} else {
|
||||||
signature = await signAndSendLegacy(transaction, isVersioned, connection);
|
signature = await signAndSendLegacy(transaction, isVersioned, connection);
|
||||||
|
|
@ -923,7 +777,7 @@ async function executeRegistration(domain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signAndSendLegacy(transaction, isVersioned, connection) {
|
async function signAndSendLegacy(transaction, isVersioned, connection) {
|
||||||
const provider = connectedProvider.provider;
|
const provider = window.solWallet.provider;
|
||||||
|
|
||||||
// Try signAndSendTransaction first (Phantom, Solflare support this)
|
// Try signAndSendTransaction first (Phantom, Solflare support this)
|
||||||
if (typeof provider.signAndSendTransaction === 'function') {
|
if (typeof provider.signAndSendTransaction === 'function') {
|
||||||
|
|
@ -953,7 +807,7 @@ async function signAndSendLegacy(transaction, isVersioned, connection) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signAndSendWalletStandard(transaction, isVersioned, connection) {
|
async function signAndSendWalletStandard(transaction, isVersioned, connection) {
|
||||||
const wallet = connectedProvider.provider;
|
const wallet = window.solWallet.provider;
|
||||||
const account = wallet.accounts?.[0];
|
const account = wallet.accounts?.[0];
|
||||||
if (!account) throw new Error('No wallet account available.');
|
if (!account) throw new Error('No wallet account available.');
|
||||||
|
|
||||||
|
|
|
||||||
268
js/wallet-connect.js
Normal file
268
js/wallet-connect.js
Normal file
|
|
@ -0,0 +1,268 @@
|
||||||
|
/* ─── Sitewide Solana Wallet Connect ─────────────────────────
|
||||||
|
Global wallet connection module for jaeswift.xyz
|
||||||
|
Provides window.solWallet API + custom events
|
||||||
|
─────────────────────────────────────────────────────────── */
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const LS_WALLET_NAME = 'sol_wallet_name';
|
||||||
|
const LS_WALLET_ADDR = 'sol_wallet_address';
|
||||||
|
|
||||||
|
// ── State ──
|
||||||
|
const state = {
|
||||||
|
connected: false,
|
||||||
|
address: null,
|
||||||
|
provider: null,
|
||||||
|
_walletName: null,
|
||||||
|
_isWalletStandard: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Wallet Standard Discovery ──
|
||||||
|
const walletStandardWallets = [];
|
||||||
|
|
||||||
|
function initWalletStandard() {
|
||||||
|
// Listen for wallets registering AFTER us
|
||||||
|
window.addEventListener('wallet-standard:register-wallet', (event) => {
|
||||||
|
try {
|
||||||
|
const callback = event.detail;
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback({
|
||||||
|
register: (...wallets) => {
|
||||||
|
for (const w of wallets) {
|
||||||
|
if (!walletStandardWallets.some(e => e.name === w.name)) {
|
||||||
|
walletStandardWallets.push(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) { /* silent */ }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Announce app-ready — catches wallets loaded BEFORE us
|
||||||
|
try {
|
||||||
|
window.dispatchEvent(new CustomEvent('wallet-standard:app-ready', {
|
||||||
|
detail: {
|
||||||
|
register: (...wallets) => {
|
||||||
|
for (const w of wallets) {
|
||||||
|
if (!walletStandardWallets.some(e => e.name === w.name)) {
|
||||||
|
walletStandardWallets.push(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} catch (e) { /* silent */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
initWalletStandard();
|
||||||
|
|
||||||
|
// ── Known wallets for install links ──
|
||||||
|
const KNOWN_WALLETS = [
|
||||||
|
{ name: 'PHANTOM', icon: '👻', url: 'https://phantom.app' },
|
||||||
|
{ name: 'JUPITER', icon: '🪐', url: 'https://jup.ag' },
|
||||||
|
{ name: 'SOLFLARE', icon: '🔆', url: 'https://solflare.com' },
|
||||||
|
{ name: 'BACKPACK', icon: '🎒', url: 'https://backpack.app' },
|
||||||
|
{ name: 'COINBASE', icon: '🔵', url: 'https://coinbase.com/wallet' },
|
||||||
|
{ name: 'TRUST', icon: '🛡️', url: 'https://trustwallet.com' },
|
||||||
|
{ name: 'METAMASK', icon: '🦊', url: 'https://metamask.io' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// ── Wallet detection (reused from soldomains.js) ──
|
||||||
|
function getAvailableWallets() {
|
||||||
|
const found = [];
|
||||||
|
const seen = new Set();
|
||||||
|
|
||||||
|
function add(name, icon, provider, url, isWS) {
|
||||||
|
if (!provider || seen.has(name)) return;
|
||||||
|
seen.add(name);
|
||||||
|
found.push({ name, icon, provider, url, isWalletStandard: !!isWS });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Wallet Standard wallets
|
||||||
|
for (const w of walletStandardWallets) {
|
||||||
|
try {
|
||||||
|
if (!w.features?.['standard:connect']) continue;
|
||||||
|
const name = (w.name || 'WALLET').toUpperCase();
|
||||||
|
let icon = '◈';
|
||||||
|
if (name.includes('METAMASK')) icon = '🦊';
|
||||||
|
else if (name.includes('PHANTOM')) icon = '👻';
|
||||||
|
else if (name.includes('SOLFLARE')) icon = '🔆';
|
||||||
|
else if (name.includes('BACKPACK')) icon = '🎒';
|
||||||
|
else if (name.includes('COINBASE')) icon = '🔵';
|
||||||
|
else if (name.includes('TRUST')) icon = '🛡️';
|
||||||
|
else if (name.includes('JUPITER')) icon = '🪐';
|
||||||
|
add(name, icon, w, w.url || '#', true);
|
||||||
|
} catch (e) { /* silent */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Legacy injection points
|
||||||
|
try { const p = window.phantom?.solana; if (p && p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app'); } catch (e) {}
|
||||||
|
try { const s = window.solflare; if (s && s.isSolflare) add('SOLFLARE', '🔆', s, 'https://solflare.com'); } catch (e) {}
|
||||||
|
try { const b = window.backpack; if (b && b.isBackpack) add('BACKPACK', '🎒', b, 'https://backpack.app'); } catch (e) {}
|
||||||
|
try { if (window.coinbaseSolana) add('COINBASE', '🔵', window.coinbaseSolana, 'https://coinbase.com/wallet'); } catch (e) {}
|
||||||
|
try { const t = window.trustwallet?.solana; if (t) add('TRUST', '🛡️', t, 'https://trustwallet.com'); } catch (e) {}
|
||||||
|
|
||||||
|
// 3. Generic window.solana
|
||||||
|
try {
|
||||||
|
const ws = window.solana;
|
||||||
|
if (ws) {
|
||||||
|
if (ws.isJupiter && !seen.has('JUPITER')) add('JUPITER', '🪐', ws, 'https://jup.ag');
|
||||||
|
else if (ws.isMetaMask && !seen.has('METAMASK')) add('METAMASK', '🦊', ws, 'https://metamask.io');
|
||||||
|
else if (!ws.isPhantom && !ws.isSolflare && !ws.isBackpack && !seen.has('PHANTOM') && !seen.has('METAMASK'))
|
||||||
|
add('SOLANA WALLET', '◈', ws, '#');
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// 4. Jupiter specific
|
||||||
|
try { const j = window.jupiter?.solana; if (j) add('JUPITER', '🪐', j, 'https://jup.ag'); } catch (e) {}
|
||||||
|
|
||||||
|
// 5. Multi-provider array
|
||||||
|
try {
|
||||||
|
const providers = window.solana?.providers;
|
||||||
|
if (Array.isArray(providers)) {
|
||||||
|
for (const p of providers) {
|
||||||
|
if (p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app');
|
||||||
|
else if (p.isJupiter) add('JUPITER', '🪐', p, 'https://jup.ag');
|
||||||
|
else if (p.isSolflare) add('SOLFLARE', '🔆', p, 'https://solflare.com');
|
||||||
|
else if (p.isBackpack) add('BACKPACK', '🎒', p, 'https://backpack.app');
|
||||||
|
else if (p.isMetaMask) add('METAMASK', '🦊', p, 'https://metamask.io');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Connect ──
|
||||||
|
async function connect(walletObj) {
|
||||||
|
// If called without arg, open picker (handled by nav UI)
|
||||||
|
// If called with a wallet name string, find it
|
||||||
|
if (typeof walletObj === 'string') {
|
||||||
|
const available = getAvailableWallets();
|
||||||
|
walletObj = available.find(w => w.name === walletObj);
|
||||||
|
if (!walletObj) throw new Error('Wallet not found: ' + walletObj);
|
||||||
|
}
|
||||||
|
if (!walletObj) throw new Error('No wallet specified');
|
||||||
|
|
||||||
|
try {
|
||||||
|
let address;
|
||||||
|
if (walletObj.isWalletStandard) {
|
||||||
|
const connectFeature = walletObj.provider.features['standard:connect'];
|
||||||
|
const result = await connectFeature.connect();
|
||||||
|
const accounts = result.accounts || [];
|
||||||
|
if (accounts.length === 0) throw new Error('No accounts returned');
|
||||||
|
address = accounts[0].address;
|
||||||
|
} else {
|
||||||
|
const resp = await walletObj.provider.connect();
|
||||||
|
address = resp.publicKey.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.connected = true;
|
||||||
|
state.address = address;
|
||||||
|
state.provider = walletObj.provider;
|
||||||
|
state._walletName = walletObj.name;
|
||||||
|
state._isWalletStandard = walletObj.isWalletStandard;
|
||||||
|
|
||||||
|
// Persist
|
||||||
|
localStorage.setItem(LS_WALLET_NAME, walletObj.name);
|
||||||
|
localStorage.setItem(LS_WALLET_ADDR, address);
|
||||||
|
|
||||||
|
// Dispatch event
|
||||||
|
window.dispatchEvent(new CustomEvent('wallet-connected', {
|
||||||
|
detail: { address, walletName: walletObj.name }
|
||||||
|
}));
|
||||||
|
|
||||||
|
return { address, walletName: walletObj.name };
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('[solWallet] Connect failed:', err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Disconnect ──
|
||||||
|
function disconnect() {
|
||||||
|
if (state.provider) {
|
||||||
|
try {
|
||||||
|
if (state._isWalletStandard) {
|
||||||
|
const disconnectFeature = state.provider.features?.['standard:disconnect'];
|
||||||
|
if (disconnectFeature) disconnectFeature.disconnect();
|
||||||
|
} else {
|
||||||
|
state.provider.disconnect();
|
||||||
|
}
|
||||||
|
} catch (e) { /* silent */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevAddr = state.address;
|
||||||
|
state.connected = false;
|
||||||
|
state.address = null;
|
||||||
|
state.provider = null;
|
||||||
|
state._walletName = null;
|
||||||
|
state._isWalletStandard = false;
|
||||||
|
|
||||||
|
localStorage.removeItem(LS_WALLET_NAME);
|
||||||
|
localStorage.removeItem(LS_WALLET_ADDR);
|
||||||
|
|
||||||
|
window.dispatchEvent(new CustomEvent('wallet-disconnected', {
|
||||||
|
detail: { address: prevAddr }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Silent auto-reconnect ──
|
||||||
|
async function autoReconnect() {
|
||||||
|
const savedName = localStorage.getItem(LS_WALLET_NAME);
|
||||||
|
if (!savedName) return;
|
||||||
|
|
||||||
|
// Small delay to let wallet extensions inject
|
||||||
|
await new Promise(r => setTimeout(r, 600));
|
||||||
|
|
||||||
|
const available = getAvailableWallets();
|
||||||
|
const wallet = available.find(w => w.name === savedName);
|
||||||
|
if (!wallet) {
|
||||||
|
// Wallet no longer available — clear saved state quietly
|
||||||
|
localStorage.removeItem(LS_WALLET_NAME);
|
||||||
|
localStorage.removeItem(LS_WALLET_ADDR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await connect(wallet);
|
||||||
|
} catch (e) {
|
||||||
|
// Auto-reconnect failed — clear saved state quietly
|
||||||
|
localStorage.removeItem(LS_WALLET_NAME);
|
||||||
|
localStorage.removeItem(LS_WALLET_ADDR);
|
||||||
|
state.connected = false;
|
||||||
|
state.address = null;
|
||||||
|
state.provider = null;
|
||||||
|
state._walletName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Truncate address helper ──
|
||||||
|
function truncAddr(a) {
|
||||||
|
if (!a || a.length < 12) return a || '';
|
||||||
|
return a.slice(0, 4) + '...' + a.slice(-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Public API ──
|
||||||
|
window.solWallet = {
|
||||||
|
get connected() { return state.connected; },
|
||||||
|
get address() { return state.address; },
|
||||||
|
get provider() { return state.provider; },
|
||||||
|
get walletName() { return state._walletName; },
|
||||||
|
get isWalletStandard() { return state._isWalletStandard; },
|
||||||
|
connect,
|
||||||
|
disconnect,
|
||||||
|
getAvailableWallets,
|
||||||
|
truncAddr,
|
||||||
|
KNOWN_WALLETS,
|
||||||
|
_autoReconnect: autoReconnect,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kick off auto-reconnect when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', autoReconnect);
|
||||||
|
} else {
|
||||||
|
autoReconnect();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
@ -110,6 +110,7 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/js/post.js"></script>
|
<script src="/js/post.js"></script>
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="/js/awesomelist.js"></script>
|
<script src="/js/awesomelist.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="https://unpkg.com/@solana/web3.js@1.98.0/lib/index.iife.min.js"></script>
|
<script src="https://unpkg.com/@solana/web3.js@1.98.0/lib/index.iife.min.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="/js/blog.js"></script>
|
<script src="/js/blog.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
<script src="/js/radar.js?v=20260404"></script>
|
<script src="/js/radar.js?v=20260404"></script>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/wallet-connect.js"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/js/clock.js"></script>
|
<script src="/js/clock.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue