Add qr code

This commit is contained in:
Joey Yakimowich-Payne 2026-01-16 10:13:25 -07:00
commit 89caf4fd79
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
4 changed files with 122 additions and 14 deletions

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Player } from '../types';
import { motion, AnimatePresence } from 'framer-motion';
import { Sparkles, User, X, Link, Check } from 'lucide-react';
import { QRCodeSVG } from 'qrcode.react';
import { PlayerAvatar } from './PlayerAvatar';
import toast from 'react-hot-toast';
@ -22,6 +23,15 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
const realPlayers = players.filter(p => p.id !== 'host');
const currentPlayer = currentPlayerId ? players.find(p => p.id === currentPlayerId) : null;
const [linkCopied, setLinkCopied] = useState(false);
const [isQrModalOpen, setIsQrModalOpen] = useState(false);
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') setIsQrModalOpen(false);
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
const copyJoinLink = async () => {
if (!gamePin) return;
@ -35,21 +45,36 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
return (
<div className="flex flex-col h-screen p-4 md:p-6 overflow-hidden">
<header className="flex flex-col md:flex-row justify-between items-center bg-white/10 p-4 md:p-6 rounded-2xl md:rounded-[2rem] backdrop-blur-md mb-4 md:mb-8 gap-3 md:gap-6 border-4 border-white/20 shadow-xl shrink-0">
<div className="flex flex-col items-center md:items-start">
<span className="text-white/80 font-bold uppercase tracking-widest text-xs md:text-sm mb-1">Game PIN</span>
<div className="flex items-center gap-2">
<div className="text-4xl md:text-6xl font-black bg-white text-theme-primary px-6 md:px-8 py-1 md:py-2 rounded-full shadow-[0_6px_0_rgba(0,0,0,0.2)] tracking-wider">
{gamePin}
<div className="flex items-center gap-4">
{isHost && gamePin && (
<div
onClick={() => setIsQrModalOpen(true)}
className="bg-white p-2 rounded-xl shadow-lg hidden md:block cursor-pointer hover:scale-105 transition-transform"
title="Click to expand"
>
<QRCodeSVG
value={`${window.location.origin}/play/${gamePin}`}
size={80}
level="M"
/>
</div>
)}
<div className="flex flex-col items-center md:items-start">
<span className="text-white/80 font-bold uppercase tracking-widest text-xs md:text-sm mb-1">Game PIN</span>
<div className="flex items-center gap-2">
<div className="text-4xl md:text-6xl font-black bg-white text-theme-primary px-6 md:px-8 py-1 md:py-2 rounded-full shadow-[0_6px_0_rgba(0,0,0,0.2)] tracking-wider">
{gamePin}
</div>
{isHost && (
<button
onClick={copyJoinLink}
className="bg-white/20 hover:bg-white/30 p-3 rounded-full transition-all active:scale-95"
title="Copy join link"
>
{linkCopied ? <Check size={24} className="text-green-400" /> : <Link size={24} />}
</button>
)}
</div>
{isHost && (
<button
onClick={copyJoinLink}
className="bg-white/20 hover:bg-white/30 p-3 rounded-full transition-all active:scale-95"
title="Copy join link"
>
{linkCopied ? <Check size={24} className="text-green-400" /> : <Link size={24} />}
</button>
)}
</div>
</div>
@ -153,6 +178,43 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
</div>
)}
</main>
<AnimatePresence>
{isQrModalOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={() => setIsQrModalOpen(false)}
>
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.8, opacity: 0 }}
className="bg-white p-8 md:p-12 rounded-[2rem] shadow-2xl flex flex-col items-center gap-6 max-w-lg w-full"
onClick={(e) => e.stopPropagation()}
>
<h2 className="text-3xl md:text-4xl font-black text-theme-primary font-display">Scan to Join</h2>
<div className="bg-white p-4 rounded-3xl shadow-[inset_0_4px_12px_rgba(0,0,0,0.1)] border-4 border-gray-100">
<QRCodeSVG
value={`${window.location.origin}/play/${gamePin}`}
size={300}
level="H"
className="w-full h-full max-w-[300px] max-h-[300px]"
/>
</div>
<div className="flex flex-col items-center gap-2 w-full text-center">
<div className="text-gray-400 font-bold uppercase tracking-widest text-sm">Or visit URL</div>
<div className="text-lg md:text-xl font-black bg-gray-100 text-gray-800 px-6 py-3 rounded-xl break-all w-full select-all">
{`${window.location.origin}/play/${gamePin}`}
</div>
</div>
<div className="text-sm text-gray-400 font-bold mt-2">Tap backdrop or ESC to close</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
};