Add floating QR code button on map for easy room joining

Tap the QR icon (bottom-right on mobile, top-right on desktop) to show
an inline QR code overlay. Scan-to-join — tapping the QR dismisses it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-19 00:20:30 +00:00
parent 0bea3ba73b
commit 8fd4ed22f9
1 changed files with 37 additions and 0 deletions

View File

@ -17,6 +17,7 @@ import ImportModal from '@/components/room/ImportModal';
import type { ImportedPlace } from '@/components/room/ImportModal'; import type { ImportedPlace } from '@/components/room/ImportModal';
import InstallBanner from '@/components/room/InstallBanner'; import InstallBanner from '@/components/room/InstallBanner';
import JoinForm from '@/components/room/JoinForm'; import JoinForm from '@/components/room/JoinForm';
import { QRCodeSVG } from 'qrcode.react';
import type { Participant, ParticipantLocation, LocationSource, Waypoint } from '@/types'; import type { Participant, ParticipantLocation, LocationSource, Waypoint } from '@/types';
// Dynamic import for map to avoid SSR issues with MapLibre // Dynamic import for map to avoid SSR issues with MapLibre
@ -39,6 +40,7 @@ export default function RoomPage() {
const [showParticipants, setShowParticipants] = useState(true); const [showParticipants, setShowParticipants] = useState(true);
const [showMeetingPoint, setShowMeetingPoint] = useState(false); const [showMeetingPoint, setShowMeetingPoint] = useState(false);
const [showImport, setShowImport] = useState(false); const [showImport, setShowImport] = useState(false);
const [showQR, setShowQR] = useState(false);
const [currentUser, setCurrentUser] = useState<{ name: string; emoji: string } | null>(null); const [currentUser, setCurrentUser] = useState<{ name: string; emoji: string } | null>(null);
const [selectedParticipant, setSelectedParticipant] = useState<Participant | null>(null); const [selectedParticipant, setSelectedParticipant] = useState<Participant | null>(null);
const [selectedWaypoint, setSelectedWaypoint] = useState<Waypoint | null>(null); const [selectedWaypoint, setSelectedWaypoint] = useState<Waypoint | null>(null);
@ -428,6 +430,41 @@ export default function RoomPage() {
</button> </button>
)} )}
{/* Floating QR button */}
<button
onClick={() => setShowQR(!showQR)}
className="absolute bottom-6 right-4 md:top-4 md:bottom-auto flex items-center gap-2 px-3 py-2.5 rounded-full bg-rmaps-dark/90 backdrop-blur border border-white/20 hover:bg-rmaps-dark hover:border-white/30 transition-colors shadow-lg z-20"
title="Show room QR code"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="7" height="7" />
<rect x="14" y="3" width="7" height="7" />
<rect x="3" y="14" width="7" height="7" />
<rect x="14" y="14" width="3" height="3" />
<line x1="21" y1="14" x2="21" y2="14.01" />
<line x1="21" y1="21" x2="21" y2="21.01" />
<line x1="17" y1="17" x2="17" y2="21" />
<line x1="21" y1="17" x2="21" y2="17.01" />
</svg>
</button>
{/* QR code overlay */}
{showQR && (
<div
className="absolute bottom-20 right-4 md:top-16 md:bottom-auto z-30 bg-white rounded-2xl p-4 shadow-2xl"
onClick={() => setShowQR(false)}
>
<QRCodeSVG
value={typeof window !== 'undefined' ? `${window.location.origin}/${slug}` : `https://rmaps.online/${slug}`}
size={160}
bgColor="#ffffff"
fgColor="#0f172a"
level="M"
/>
<p className="text-center text-xs text-slate-500 mt-2 font-medium">Scan to join</p>
</div>
)}
{/* Connection status indicator */} {/* Connection status indicator */}
{!isConnected && ( {!isConnected && (
<div className="absolute top-4 left-1/2 -translate-x-1/2 bg-yellow-500/90 text-black text-sm px-3 py-1.5 rounded-full z-30"> <div className="absolute top-4 left-1/2 -translate-x-1/2 bg-yellow-500/90 text-black text-sm px-3 py-1.5 rounded-full z-30">