diff --git a/src/app/page.tsx b/src/app/page.tsx index 770b2f5..b04bba3 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -7,10 +7,8 @@ import { AuthButton } from '@/components/AuthButton'; import { EcosystemFooter } from '@/components/EcosystemFooter'; import { useAuthStore } from '@/stores/auth'; -// Emoji options for avatars const EMOJI_OPTIONS = ['🐙', '🦊', '🐻', '🐱', '🦝', '🐸', '🦉', '🐧', '🦋', '🐝']; -// Generate a URL-safe room slug function generateSlug(): string { return nanoid(8).toLowerCase(); } @@ -26,8 +24,6 @@ export default function HomePage() { const [isLoaded, setIsLoaded] = useState(false); const [lastRoom, setLastRoom] = useState(null); - // Load saved user info from localStorage on mount - // If opened as installed PWA (standalone mode), auto-redirect to last room useEffect(() => { let loadedEmoji = ''; try { @@ -40,7 +36,6 @@ export default function HomePage() { loadedEmoji = user.emoji; } } - // Load last visited room (show "Rejoin" button, but don't auto-redirect) const lastVisited = localStorage.getItem('rmaps_last_room'); if (lastVisited) { setLastRoom(lastVisited); @@ -48,14 +43,12 @@ export default function HomePage() { } catch { // Ignore parse errors } - // Set random emoji if none loaded if (!loadedEmoji) { setEmoji(EMOJI_OPTIONS[Math.floor(Math.random() * EMOJI_OPTIONS.length)]); } setIsLoaded(true); }, [router]); - // Auto-fill name from EncryptID when authenticated useEffect(() => { if (isAuthenticated && authUsername && !name) { setName(authUsername); @@ -64,31 +57,21 @@ export default function HomePage() { const handleCreateRoom = async () => { if (!name.trim()) return; - - // Require EncryptID auth to create rooms if (!isAuthenticated) { alert('Please sign in with EncryptID to create a room.'); return; } - const slug = roomName.trim() ? roomName.toLowerCase().replace(/[^a-z0-9]/g, '-').slice(0, 20) : generateSlug(); - - // Store user info in localStorage for the session localStorage.setItem('rmaps_user', JSON.stringify({ name, emoji })); localStorage.setItem('rmaps_last_room', slug); - - // Navigate to the room (will create it if it doesn't exist) router.push(`/${slug}`); }; const handleJoinRoom = () => { if (!name.trim() || !joinSlug.trim()) return; - localStorage.setItem('rmaps_user', JSON.stringify({ name, emoji })); - - // Clean the slug const cleanSlug = joinSlug.toLowerCase().replace(/[^a-z0-9-]/g, ''); localStorage.setItem('rmaps_last_room', cleanSlug); router.push(`/${cleanSlug}`); @@ -100,173 +83,395 @@ export default function HomePage() { router.push(`/${lastRoom}`); }; - return (
- {/* ── Hero Section ─────────────────────────────────────────── */} -
- {/* Subtle background grid */} -
-
- {/* Logo */} -
-
+ {/* ── Hero ──────────────────────────────────────────────────── */} +
+ {/* Background glow */} +
+
+ +
+ {/* Ecosystem badge */} +
+ Part of the rSpace Ecosystem +
+ + {/* Logo + Name */} +
+
rM
-

+

rMaps

{/* Headline */} -

- Collaborative Maps for Everyone +

+ Real-Time Collaborative Maps

-

- A flexible, privacy-first map tool for real-time location sharing, event navigation, and group coordination. - Create a room, share a link, and see your crew on the map. +

+ Share live locations, navigate indoor and outdoor spaces, coordinate meetups + — all from the browser. No app install. No tracking. No data collection.

{/* CTA buttons */} - -

- No app install. No sign-up required to join a room. +

+ No sign-up required to join. Works on any device.

- {/* ── Feature Cards ────────────────────────────────────────── */} -
-
- {/* Real-time GPS */} -
-
- + {/* ── Core Features ─────────────────────────────────────────── */} +
+
+ + Core Features + +

+ Everything you need to find your friends +

+
+ +
+ {/* Live Location */} +
+
+
-

Real-time GPS

-

- Share your live location with friends. See everyone on the map updating in real time as you move. +

Live GPS Sharing

+

+ Real-time location updates via WebSocket. See everyone on the map + as they move, with stale detection and high-accuracy fallback.

- {/* Event Maps */} -
-
- + {/* Indoor + Outdoor Navigation */} +
+
+
-

Event Maps

-

- Navigate festivals, camps, and conferences. Custom maps with labeled stages, food courts, and meeting points. +

Indoor + Outdoor Nav

+

+ Turn-by-turn routing via OSRM outdoors, seamless switch to c3nav + for indoor venues. Multi-floor, level-aware navigation.

- {/* Privacy First */} -
-
- + {/* Meeting Points */} +
+
+ + + +
+

Meeting Points

+

+ Drop waypoints for meetups, events, and points of interest. + Search by address, share coordinates, or pin from your location. +

+
+ + {/* Privacy */} +
+
+
-

Privacy First

-

- Zero-knowledge architecture. You control who sees you. Go invisible anytime. No tracking, no data collection. +

Privacy First

+

+ Ghost mode, precision levels, one-toggle location sharing. + Zero tracking, zero data collection. You control who sees you.

- {/* ── How It Works ─────────────────────────────────────────── */} -
-

How It Works

+ {/* ── What Makes rMaps Different ────────────────────────────── */} +
+
+ + Beyond Google Maps + +

+ What makes rMaps different +

+
+ +
+ {/* CCC Event Integration */} +
+
+
+ 🏕️ +
+
+

CCC Event Integration

+

+ Native c3nav integration for 39C3, Camp, and other CCC events. Indoor maps with + multi-floor routing, venue bounds detection, and automatic map switching + when you walk inside. +

+
+
+
+ + {/* Push Notifications & Pinging */} +
+
+
+ 📡 +
+
+

Location Pinging

+

+ Request a friend's location with one tap. Push notifications via + Web Push API with vibration alerts. Works even when the app is + backgrounded via service worker. +

+
+
+
+ + {/* Google Maps Import */} +
+
+
+ 📦 +
+
+

Google Maps Import

+

+ Import your saved places from Google Takeout ZIP exports. GeoJSON + parsing, auto-emoji mapping by place type, and preview before + importing. +

+
+
+
+ + {/* PWA & Offline */} +
+
+
+ 📱 +
+
+

PWA & Offline Mode

+

+ Install as a native app. Three-tier service worker caching keeps + maps accessible offline with up to 500 cached tiles. Background + sync handles location updates. +

+
+
+
+ + {/* QR Sharing */} +
+
+
+ 🔗 +
+
+

Instant Room Sharing

+

+ Generate a QR code or shareable link for any room. Friends scan or tap + to join instantly — no account needed, no app download. + Native share dialog on mobile. +

+
+
+
+ + {/* CRDT Sync */} +
+
+
+ 🔄 +
+
+

Conflict-Free Sync

+

+ Automerge CRDT architecture ensures everyone sees the same map state, + even through disconnections. WebSocket real-time sync with automatic + reconnection and state recovery. +

+
+
+
+
+
+ + {/* ── How It Works ──────────────────────────────────────────── */} +
+
+ + How It Works + +

+ Three steps to find your crew +

+
+
- {/* Step 1 */}
-
+
1

Create a Map Room

-

- Sign in and create a room for your event. Get a shareable link like rmaps.online/ccc-camp +

+ Sign in and name your room. Get a link like{' '} + camp.rmaps.online{' '} + or a custom slug.

- {/* Step 2 */}
-
+
2

Share with Friends

-

- Send the link to your crew. They join instantly from any device -- no app download, no account needed. +

+ Send the link or scan the QR code. Friends join from any browser + — no app download, no account creation needed.

- {/* Step 3 */}
-
+
3
-

Find Each Other

-

- See everyone on the map in real time. Set your status, share meeting points, and never lose your friends again. +

Navigate Together

+

+ See everyone in real time. Drop meeting points, get turn-by-turn + directions, and ping friends when you need to regroup. +

+
+
+
+ + {/* ── Use Cases ─────────────────────────────────────────────── */} +
+
+ + Built For + +

+ Maps for every gathering +

+
+ +
+
+
🏕️
+

Festivals & Camps

+

+ Navigate massive venues with indoor maps. Find stages, food courts, + and your crew across multi-day events like CCC Camp. +

+
+ +
+
🏙️
+

City Exploration

+

+ Exploring a new city with friends? Share locations, drop pins at + restaurants and landmarks, import your Google Maps saved places. +

+
+ +
+
🤝
+

Group Coordination

+

+ Conferences, retreats, team offsites. Set meeting points, ping + stragglers, and get walking directions to the next session.

- {/* Connector lines (decorative) */} -
-
- Built for +
+
+ First built for CCC events - and beyond + — now for any gathering
- {/* ── Get Started (existing login/room interface) ──────────── */} -
+ {/* ── Technical Highlights ──────────────────────────────────── */} +
+
+ + Under the Hood + +

+ Built on open standards +

+
+ +
+ {[ + { label: 'MapLibre GL', desc: 'Open-source maps' }, + { label: 'OSRM', desc: 'Outdoor routing' }, + { label: 'c3nav', desc: 'Indoor navigation' }, + { label: 'Automerge', desc: 'CRDT sync' }, + { label: 'Web Push', desc: 'Notifications' }, + { label: 'Service Worker', desc: 'Offline & PWA' }, + { label: 'WebSocket', desc: 'Real-time sync' }, + { label: 'EncryptID', desc: 'Identity & auth' }, + ].map((tech) => ( +
+

{tech.label}

+

{tech.desc}

+
+ ))} +
+
+ + {/* ── Get Started ───────────────────────────────────────────── */} +
- {/* Section heading */}

Get Started

-

Create or join a map room

+

Create or join a map room

- {/* Quick Rejoin Card - show when user has saved info and last room */} + {/* Quick Rejoin */} {isLoaded && name && lastRoom && (

Welcome back, {name}!

@@ -285,10 +490,8 @@ export default function HomePage() { {/* Main Card */}
- {/* User Setup */}

Your Profile

-
-
@@ -323,7 +525,6 @@ export default function HomePage() {
- {/* Create Room */} {!isCreating ? (
-
or
- - {/* Join Room */}
-
- {/* ── Ecosystem Footer ─────────────────────────────────────── */} + {/* ── Ecosystem Footer ──────────────────────────────────────── */}
);