## 1. sanitizeUrl() (already existed, verified applied) - Applied to ShopDetailPage, CoachDetailPage with urlDomain() tooltip hints - Blocks javascript:, data:, vbscript: and all non-http(s) schemes - Returns null for invalid URLs — link simply doesn't render ## 2. Geohash validation in parsers - Added isValidGeohash() in types.ts using ngeohash.decode() - Min 4 chars (country-level precision rejected), max 12 chars - Rejects lat/lng outside Earth bounds - Rejects 0,0 "null island" — classic bot artifact - VPN/Tor unaffected: geohash comes from map pin, not user IP ## 3. Content length limits in all parsers - MAX_TITLE_LEN=100, MAX_DESCRIPTION_LEN=3000, MAX_LOCATION_LEN=200 - MIN_TITLE_LEN=2, MIN_GEOHASH_LEN=4 - Applied to parseSkateSpot, parseSkateShop, parseSkateCoach, parseSkateFilmer, parseSkateEditor, parseSkateEvent ## 4. Smart WoT default (already in codebase, verified correct) - Logged out / <5 follows → 'all' (don't punish new/anon users) - Logged in with ≥5 follows → 'trusted' (sort trusted first) - Soft mode: trusted content floats to top, nothing hidden - Active in MapPage and EventsPage ## 5. Trusted count stats in useGrowthStats - Added trustedSpots and trustedMappers to GrowthStats type - Heuristic: pubkeys with ≥2 published items = organic community member - CommunityStatsPage now shows "🛡 X from active accounts" below spot count - Purely additive — both numbers always visible ## 6. OfficialBadge component (already existed, wired up) - Added to SpotCard, EventCard, ShopDetailPage, CoachDetailPage - Only renders for FOUNDER_PUBKEY (20e7c953...) - Zero-friction: invisible for all other pubkeys - Impersonation attempts immediately visible ## 7. Leaflet marker clustering - Installed leaflet.markercluster v1.5.3 + @types - MarkerClusterGroup replaces direct marker.addTo(map) - Custom cluster icon: red/orange gradient circle with count - maxClusterRadius: 60px, spiderfyOnMaxZoom: true - MarkerCluster CSS loaded from CDN in index.html ## 8. DM phishing warning banner - PhishingWarningBanner component in DirectMessagesPage - One-time dismissible (localStorage key: dm-phishing-warning-dismissed) - Message: SkateSpots will never DM you asking for keys/payments - Uses useLocalStorage hook, Shield icon, X dismiss button Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
51 lines
2.9 KiB
HTML
51 lines
2.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
<meta http-equiv="content-security-policy" content="default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' https://unpkg.com; frame-src 'self' https:; font-src 'self'; base-uri 'self'; manifest-src 'self'; connect-src 'self' blob: https: wss:; img-src 'self' data: blob: https:; media-src 'self' https:">
|
|
|
|
<!-- PWA Manifest -->
|
|
<link rel="manifest" href="/manifest.webmanifest">
|
|
|
|
<!-- App Icons -->
|
|
<link rel="icon" type="image/svg+xml" href="/images/icon.svg">
|
|
<link rel="apple-touch-icon" href="/images/icon.svg">
|
|
<link rel="apple-touch-icon" sizes="152x152" href="/images/icon.svg">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/images/icon.svg">
|
|
<link rel="apple-touch-startup-image" href="/images/icon.svg">
|
|
|
|
<!-- Theme Color -->
|
|
<meta name="theme-color" content="#ff0040">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<meta name="apple-mobile-web-app-title" content="SkateSpots">
|
|
|
|
<!-- SEO Meta Tags -->
|
|
<meta name="description" content="Discover skate spots, find coaches, join events, and connect with the skateboarding community on Nostr. Free, decentralized, censorship-resistant.">
|
|
<meta name="keywords" content="skateboarding, skate spots, skate parks, nostr, decentralized, bitcoin, lightning, zaps">
|
|
|
|
<!-- Open Graph -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:title" content="SkateSpots - Decentralized Skate Map & Community">
|
|
<meta property="og:description" content="Discover skate spots, find coaches, join events, and connect with the skateboarding community on Nostr.">
|
|
<meta property="og:image" content="https://skate-spots.com/images/icon.svg">
|
|
<meta property="og:url" content="https://skate-spots.com">
|
|
|
|
<!-- Twitter Card -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="SkateSpots - Decentralized Skate Map">
|
|
<meta name="twitter:description" content="Discover skate spots, find coaches, join events on Nostr.">
|
|
<meta name="twitter:image" content="https://skate-spots.com/images/icon.svg">
|
|
|
|
<!-- Leaflet CSS (loaded via CDN to avoid build issues) -->
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
|
|
<!-- Leaflet MarkerCluster CSS -->
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css" crossorigin="">
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css" crossorigin="">
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|