'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { nanoid } from 'nanoid'; import { AuthButton } from '@/components/AuthButton'; 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(); } export default function HomePage() { const router = useRouter(); const { isAuthenticated, username: authUsername } = useAuthStore(); const [isCreating, setIsCreating] = useState(false); const [joinSlug, setJoinSlug] = useState(''); const [name, setName] = useState(''); const [emoji, setEmoji] = useState(''); const [roomName, setRoomName] = useState(''); 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 { const stored = localStorage.getItem('rmaps_user'); if (stored) { const user = JSON.parse(stored); if (user.name) setName(user.name); if (user.emoji) { setEmoji(user.emoji); loadedEmoji = user.emoji; } } // Load last visited room const lastVisited = localStorage.getItem('rmaps_last_room'); if (lastVisited) { setLastRoom(lastVisited); // Auto-redirect if running as installed PWA and user has saved info const isStandalone = window.matchMedia('(display-mode: standalone)').matches || (navigator as unknown as { standalone?: boolean }).standalone === true; if (isStandalone && stored) { router.push(`/${lastVisited}`); return; } } } 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); } }, [isAuthenticated, authUsername, name]); 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}`); }; const handleRejoinLastRoom = () => { if (!name.trim() || !lastRoom) return; localStorage.setItem('rmaps_user', JSON.stringify({ name, emoji })); router.push(`/${lastRoom}`); }; return (
{/* ── Header Nav ─────────────────────────────────────────── */} {/* ── Hero Section ─────────────────────────────────────────── */}
{/* Subtle background grid */}
{/* Logo */}
rM

rMaps

{/* Headline */}

Find Your Friends, Anywhere

Privacy-first real-time location sharing for events, festivals, and camps. See where your crew is without trusting a central server.

{/* CTA buttons */}
Try the Demo Get Started

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

{/* ── Feature Cards ────────────────────────────────────────── */}
{/* Real-time GPS */}

Real-time GPS

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

{/* Event Maps */}

Event Maps

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

{/* Privacy First */}

Privacy First

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

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

How It Works

{/* 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

{/* Step 2 */}
2

Share with Friends

Send the link to your crew. They join instantly from any device -- no app download, no account 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.

{/* Connector lines (decorative) */}
Built for CCC events and beyond
{/* ── Get Started (existing login/room interface) ──────────── */}
{/* Section heading */}

Get Started

Create or join a map room

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

Welcome back, {name}!

Or create/join a different room below

)} {/* Main Card */}
{/* User Setup */}

Your Profile

setName(e.target.value)} placeholder="Enter your name" className="input w-full" maxLength={20} />
{EMOJI_OPTIONS.map((e) => ( ))}

{/* Create Room */} {!isCreating ? (
or
{/* Join Room */}
setJoinSlug(e.target.value)} placeholder="Enter room name or code" className="input w-full" />
) : (
setRoomName(e.target.value)} placeholder="e.g., 39c3-crew" className="input flex-1" maxLength={20} /> .rmaps.online

Leave blank for a random code

)}
{/* ── Ecosystem Footer ─────────────────────────────────────── */}
); }