Adopt PosterChan's "encrypted events are the datastore" idea for two per-browser bits of state, so they follow the user's npub across devices. New shared engine (src/hooks/useSyncedAppData.ts): - Stores one encrypted kind-30078 (NIP-78) event per d-tag, NIP-44 encrypt-to-self, modeled on the proven useStash pattern. - Clobber-guard: re-fetch + re-decrypt the freshest remote copy and MERGE the local patch in before publishing (never overwrites a field another device just set). - Debounced publisher (coalesces rapid writes, flushes on pagehide/ beforeunload) so frequent read-marker changes don't spam relays. - No-op for anon / non-NIP-44 signers. Read-state sync (src/hooks/useReadState.ts), d-tag skatespots:read-state: - Unifies the four previously localStorage-only markers (notification bell, DMs, crew chat, newsletter) into one synced blob. - Merge = field-wise MAX (markers only move forward → commutative, safe). - The four legacy hooks now re-export drop-in replacements with identical signatures, so NO consumer component changed. localStorage stays as the instant/offline/anon fast path; the relay copy hydrates and max-merges. - Fixes the real bug where clearing a badge on one device left it lit on another. Preference sync (src/hooks/usePreferences.ts), d-tag skatespots:preferences: - Mobile + desktop nav layouts now sync (language field wired for later). - Merge = last-write-wins by updatedAt (a pref replaces, not accumulates). - useNavPreferences / useDesktopNavPreferences adopt a synced layout when present and mirror changes; public APIs unchanged, localStorage fallback intact. Docs: NIP.md gains a "Cross-device app data (kind 30078, encrypted)" section documenting both d-tags, their content shapes, merge policies, and the clobber-guard/debounce/anon safety notes. Build + typecheck pass. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy> |
||
|---|---|---|
| .agents/skills | ||
| .github | ||
| .nsite | ||
| .vscode | ||
| android-templates | ||
| cvm-server | ||
| eslint-rules | ||
| public | ||
| scripts | ||
| skills | ||
| src | ||
| .gitignore | ||
| .gitlab-ci.yml | ||
| .mcp.json | ||
| .npmrc | ||
| AGENTS.md | ||
| BOUNTY-SYSTEM-PLAN.md | ||
| BUDGET_IMPLEMENTATION.md | ||
| capacitor.config.ts | ||
| components.json | ||
| CONTEXTVM_INTEGRATION.md | ||
| DEPLOYMENT_COMPARISON.md | ||
| EMERGENCY_FIX.md | ||
| eslint.config.js | ||
| FINAL_POLISH_BREAKDOWN.md | ||
| FINAL_SCOPE.md | ||
| FIXES_BREAKDOWN.md | ||
| FOLLOW_MANAGEMENT_BRIEFING.md | ||
| HOW_TO_ENABLE_RELATR.md | ||
| IMPLEMENTATION_COMPLETE.md | ||
| IMPLEMENTATION_PLAN.md | ||
| index.html | ||
| INTEGRATION_BRIEFING.md | ||
| LIVE_MUSIC_PROPOSAL.md | ||
| LOGO_USAGE.md | ||
| MCP_SERVER_SPEC.md | ||
| netlify.toml | ||
| NIP-06-IMPLEMENTATION.md | ||
| NIP.md | ||
| NOGOOD_RADIO_PROPOSAL.md | ||
| opencode.json | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.js | ||
| RADIO_PLAYER_GUIDE.md | ||
| README.md | ||
| RELATR_INTEGRATION.md | ||
| RELAY_FIX.md | ||
| RELAY_SETUP.md | ||
| release-notes.txt | ||
| SECURITY.md | ||
| SHOP_VERIFICATION_ANALYSIS.md | ||
| stack.json | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vite.config.ts | ||
| WEATHER_FEATURE.md | ||
| ZAP_INTEGRATION_PROPOSAL.md | ||
| ZAP_QUICK_WINS.md | ||
| ZAP_SIMPLE_PLAN.md | ||
| ZAPSTORE.md | ||
| zapstore.yaml | ||
SkateSpots - Decentralized Skate Map & Community
A decentralized skateboarding community platform built on Nostr. Discover skate spots, find coaches, join events, and connect with skaters worldwide.
🌐 Live App
- Deployed URL: https://skate-spots.org
- Nostr Git: nostr://npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx/git.shakespeare.diy/SkateSpots
✨ Features
- 🗺️ Interactive Skate Map - Discover skate spots worldwide with geolocation
- 🛹 Spot Database - Browse, create, and review skate parks and street spots
- 📅 Events Calendar - Find and create skateboarding events and competitions
- 👨🏫 The Lab - Connect with coaches, filmers, and video editors in your area
- 🎬 Video Editors - Find editors who list their software, hardware, and portfolio
- 🛍️ Skate Shops - Discover local skate shops and support the community
- 🔄 Buy/Sell Gear - Marketplace for used skate gear (NIP-99 classified listings)
- 🎒 My Stash - Private encrypted gear inventory with location tracking
- 🎵 NoGood Radio - 24/7 vibing music (lofi, synthwave, electronica)
- ⚡ Lightning Zaps - Send Bitcoin tips to content creators
- 🌤️ Weather Integration - Check conditions before you skate
- 🔐 Nostr Authentication - Decentralized login with your Nostr keys
- 📱 PWA Support - Install as a native app on any device
- ❤️ Supporters - Community funding via Angor (decentralized Bitcoin crowdfunding)
🛠️ Technology Stack
- React 18 - Modern UI with hooks and concurrent rendering
- Nostr Protocol - Decentralized data storage and communication
- TailwindCSS 3 - Utility-first CSS framework
- Vite - Lightning-fast build tool and dev server
- shadcn/ui - Beautiful, accessible UI components
- Nostrify - Nostr framework for web applications
- TanStack Query - Data fetching and caching
- OpenStreetMap - Interactive maps
- TypeScript - Type-safe development
🚀 Getting Started
Prerequisites
- Node.js 18+
- npm or yarn
Installation
# Clone the repository
git clone nostr://npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx/git.shakespeare.diy/SkateSpots
# Install dependencies
npm install
# Start development server
npm run dev
Building
# Build for production
npm run build
# Run tests
npm test
🌍 Deployment
The app is configured for deployment via:
- Shakespeare Deploy - Nostr-native deployment (configured)
- Netlify - Static site hosting
- Vercel - Edge deployment
- nsite - Nostr-native sites
Deploy command:
npm run deploy
📱 PWA Installation
SkateSpots works as a Progressive Web App (PWA) and can be installed on:
- Desktop: Chrome, Edge, Safari - Look for install icon in address bar
- Android: Chrome - "Add to Home Screen" from menu
- iOS: Safari - "Add to Home Screen" from share menu
🔌 Nostr Integration
Custom Event Kinds
This app uses custom Nostr event kinds defined in NIP.md:
| Kind | Type | Description |
|---|---|---|
| 30017 | Addressable | Skate Shops (NIP-15 Stall) |
| 30402 | Addressable | Gear Listings (NIP-99 Classified) |
| 30818 | Addressable | Skate Spots |
| 30823 | Addressable | Skate Tours |
| 31923 | Addressable | Skate Events (NIP-52 Calendar) |
| 35001 | Addressable | Skate Coaches |
| 35002 | Addressable | Skate Filmers / Photographers |
| 35003 | Addressable | Skate Video Editors |
Relay Configuration
Default relay: wss://goskatespots.nostr1.com
Users can manage relays in Settings following NIP-65.
🎒 My Stash
The Stash is a private, encrypted gear inventory (NIP-51 kind 30001). Users can track their equipment with:
- Categories: Decks, trucks, wheels, bearings, shoes, apparel, protective gear, and more
- Storage locations: Home, car, backpack, wardrobe, van, skatepark, friend's, parents', warehouse, storage unit, sponsor, or other
- Condition rating (1–5 stars), rarity tier, and optional price
- Visibility control: Private (encrypted), friends-only, or public
- Filter & search by category, storage location, or keyword
🧪 The Lab
The Lab connects skaters with service providers in three categories:
- Coaches - Skateboarding instructors with skill specializations
- Filmers - Videographers and photographers with equipment/style details
- Video Editors - Editors who list their software (Premiere, DaVinci, After Effects, etc.), hardware setup, portfolio, and contact details
📦 Dependencies Overview
Core Dependencies
@nostrify/nostrify- Nostr protocol implementation@nostrify/react- React integration for Nostrreact&react-dom- UI frameworkreact-router-dom- Client-side routing@tanstack/react-query- Data fetching and statenostr-tools- Nostr utilities and cryptography
UI & Styling
tailwindcss- Utility-first CSS@radix-ui/*- Accessible UI primitiveslucide-react- Icon libraryclass-variance-authority- Component variantstailwind-merge- Class name merging
Maps & Location
- OpenStreetMap - Interactive maps
ngeohash- Geohash encoding/decoding
Bitcoin & Lightning
@getalby/sdk- Alby wallet integration@cashu/cashu-ts- Cashu ecash protocolnpubcash-sdk- npub.cash integrationbitcoinjs-lib- Bitcoin utilities@noble/curves&@noble/hashes- Cryptography
Media & Streaming
hls.js- HLS streaming supportqrcode- QR code generationhtml5-qrcode- QR code scanning
Utilities
date-fns- Date formattingzod- Schema validationreact-hook-form- Form handlingbuffer- Node.js buffer polyfillidb&dexie- IndexedDB wrappers
🏗️ Project Structure
/src
├── components/ # React components
│ ├── ui/ # shadcn/ui components (48+)
│ ├── auth/ # Authentication components
│ └── ... # Feature components
├── hooks/ # Custom React hooks
├── pages/ # Route pages
├── lib/ # Utilities and helpers
├── contexts/ # React context providers
└── test/ # Testing utilities
/public
├── images/ # Static images and icons
├── manifest.webmanifest
└── robots.txt
/skills # Implementation guides for features
🧪 Testing
# Run all tests (TypeScript, ESLint, Vitest, Build)
npm test
# Type checking only
npx tsc --noEmit
# Linting only
npx eslint
🤝 Contributing
This is a Nostr-based open source project. Contributions are welcome!
- Fork/clone the repository via Shakespeare or git
- Create your feature branch
- Make your changes
- Run tests (
npm test) - Commit your changes
- Push and create a pull request
📄 License
YOU ARE FREE TO SKATEBOARD
This project is open. Share it, fork it, build on it, skate it. The streets belong to everyone. So does this code. Just keep skating and don't be a kook.
Copyright © 2025–∞ SkateSpots contributors & the community
See /license in the app for the full license page.
❤️ Support the Project
SkateSpots is 100% free, open-source, and community-funded. No ads, no data selling, no investors.
- Angor: https://angor.io/project/angor1qtunna00uqyx52rr0nvqa059z2gknhrmn7urd6z
- Supporters page: https://skate-spots.org/supporters
🙏 Credits
- Built with: MKStack - Nostr client framework
- Vibed with: Shakespeare - AI-powered development
- Powered by: Nostr - Decentralized protocol
- Funded by: Angor - Decentralized Bitcoin crowdfunding
🔗 Links
📞 Support
For issues or questions:
- Open an issue on the repository
- Contact via Nostr: npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx
Made with ❤️ for the skateboarding community