4c3c8c6f87
Backend KOL_FEEDS trimmed from 29 to 25 (dead feeds removed).
Sync all hardcoded count mentions:
- layout.tsx JSON-LD, page.tsx (metric + comparison + copy)
- kol/page.tsx, KolPageClient.tsx ("and 26 more" → "and 22 more")
- glossary/page.tsx, opengraph-image.tsx
- public/llms.txt, llms-full.txt
- drop removed KOLs (Dragonfly Capital, Nic Carter) from named lists
Bundles other in-flight frontend work already in the working tree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
190 lines
6.0 KiB
TypeScript
190 lines
6.0 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* MobileWalletSheet — bottom-drawer shown on mobile when no injected wallet
|
|
* is detected. Offers deep links to common mobile wallets so the user can
|
|
* open MetaMask (or Trust / Coinbase / OKX) and land directly on this dApp.
|
|
*
|
|
* Rendered as a fixed overlay + slide-up panel. Backdrop tap dismisses it.
|
|
* The CSS animation class `mobile-sheet-enter` is defined in globals.css.
|
|
*
|
|
* Usage:
|
|
* <MobileWalletSheet open={open} onClose={() => setOpen(false)} />
|
|
*/
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { getWalletLinks } from '@/lib/mobileWallet'
|
|
import type { WalletLink } from '@/lib/mobileWallet'
|
|
|
|
interface Props {
|
|
open: boolean
|
|
onClose: () => void
|
|
}
|
|
|
|
export default function MobileWalletSheet({ open, onClose }: Props) {
|
|
const [links, setLinks] = useState<WalletLink[]>([])
|
|
|
|
// Build deep links client-side only (needs window.location.href).
|
|
useEffect(() => {
|
|
if (open) {
|
|
setLinks(getWalletLinks(window.location.href))
|
|
}
|
|
}, [open])
|
|
|
|
// Lock body scroll while sheet is open.
|
|
useEffect(() => {
|
|
if (open) {
|
|
document.body.style.overflow = 'hidden'
|
|
return () => { document.body.style.overflow = '' }
|
|
}
|
|
}, [open])
|
|
|
|
// Escape key closes.
|
|
useEffect(() => {
|
|
if (!open) return
|
|
function onKey(e: KeyboardEvent) { if (e.key === 'Escape') onClose() }
|
|
window.addEventListener('keydown', onKey)
|
|
return () => window.removeEventListener('keydown', onKey)
|
|
}, [open, onClose])
|
|
|
|
if (!open) return null
|
|
|
|
return (
|
|
/* Backdrop */
|
|
<div
|
|
style={{
|
|
position: 'fixed', inset: 0, zIndex: 9000,
|
|
background: 'rgba(0,0,0,0.55)',
|
|
display: 'flex', alignItems: 'flex-end',
|
|
backdropFilter: 'blur(4px)',
|
|
WebkitBackdropFilter: 'blur(4px)',
|
|
}}
|
|
onClick={onClose}
|
|
aria-modal="true"
|
|
role="dialog"
|
|
aria-label="Connect a wallet"
|
|
>
|
|
{/* Sheet */}
|
|
<div
|
|
className="mobile-sheet-enter"
|
|
style={{
|
|
width: '100%',
|
|
background: 'var(--bg-elev)',
|
|
borderRadius: '20px 20px 0 0',
|
|
padding: '20px 20px 32px',
|
|
boxShadow: '0 -8px 40px rgba(0,0,0,0.18)',
|
|
maxHeight: '80vh',
|
|
overflowY: 'auto',
|
|
}}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
{/* Handle bar */}
|
|
<div style={{
|
|
width: 40, height: 4,
|
|
background: 'var(--line-2)',
|
|
borderRadius: 99,
|
|
margin: '0 auto 20px',
|
|
}} />
|
|
|
|
{/* Header */}
|
|
<div style={{ marginBottom: 20 }}>
|
|
<div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.01em', marginBottom: 4 }}>
|
|
Connect a wallet
|
|
</div>
|
|
<div style={{ fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.5 }}>
|
|
Open the dApp inside your wallet's built-in browser to connect.
|
|
</div>
|
|
</div>
|
|
|
|
{/* Wallet list */}
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
{links.map(link => (
|
|
<a
|
|
key={link.name}
|
|
href={link.href}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 14,
|
|
padding: '14px 16px',
|
|
borderRadius: 'var(--r-md)',
|
|
border: '1px solid var(--line)',
|
|
background: 'var(--surface)',
|
|
textDecoration: 'none',
|
|
color: 'inherit',
|
|
transition: 'background 120ms, border-color 120ms',
|
|
}}
|
|
onTouchStart={e => {
|
|
(e.currentTarget as HTMLAnchorElement).style.background = 'var(--bg-sunk)'
|
|
}}
|
|
onTouchEnd={e => {
|
|
(e.currentTarget as HTMLAnchorElement).style.background = 'var(--surface)'
|
|
}}
|
|
>
|
|
{/* Icon */}
|
|
<div style={{
|
|
width: 44, height: 44,
|
|
borderRadius: 12,
|
|
background: link.color,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
fontSize: link.abbr.length > 2 ? 12 : 20,
|
|
fontWeight: 700,
|
|
color: '#fff',
|
|
flexShrink: 0,
|
|
letterSpacing: '-0.01em',
|
|
}}>
|
|
{link.abbr}
|
|
</div>
|
|
|
|
{/* Text */}
|
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
<div style={{ fontSize: 15, fontWeight: 600, marginBottom: 2 }}>{link.name}</div>
|
|
<div style={{ fontSize: 12, color: 'var(--ink-3)' }}>{link.hint}</div>
|
|
</div>
|
|
|
|
{/* Arrow */}
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{ color: 'var(--ink-3)', flexShrink: 0 }}>
|
|
<path d="M9 18l6-6-6-6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
</svg>
|
|
</a>
|
|
))}
|
|
</div>
|
|
|
|
{/* WalletConnect note */}
|
|
<div style={{
|
|
marginTop: 18,
|
|
padding: '12px 14px',
|
|
borderRadius: 'var(--r-sm)',
|
|
background: 'var(--bg-sunk)',
|
|
fontSize: 12,
|
|
color: 'var(--ink-3)',
|
|
lineHeight: 1.55,
|
|
}}>
|
|
<strong style={{ color: 'var(--ink-2)' }}>Already in your wallet's browser?</strong>
|
|
{' '}Tap Cancel to dismiss, then use the built-in browser navigation to reload the page — MetaMask should auto-detect the dApp.
|
|
</div>
|
|
|
|
{/* Close */}
|
|
<button
|
|
onClick={onClose}
|
|
style={{
|
|
marginTop: 14,
|
|
width: '100%',
|
|
padding: '14px',
|
|
borderRadius: 'var(--r-md)',
|
|
background: 'var(--bg-sunk)',
|
|
border: '1px solid var(--line)',
|
|
fontSize: 14,
|
|
fontWeight: 500,
|
|
color: 'var(--ink-2)',
|
|
}}
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|