diff --git a/client/src/views/Lobby.vue b/client/src/views/Lobby.vue index d8af232..fb3b00d 100644 --- a/client/src/views/Lobby.vue +++ b/client/src/views/Lobby.vue @@ -26,10 +26,11 @@ -

- Snatch Game -

-
Arena de intercambio social
+
+
+

Snatch Game

+
Arena de intercambio social
+
@@ -42,56 +43,53 @@
-
- - + +
+
+ + +
+
+ + +
+
+ +
-
- -
Antes de jugar, presiona "Confirmar Nombre" para confirmar tu nombre.
-
-
- Jugando como: {{ playerName || 'invitado' }} (✏️) -
-
- - -
-
- -
-
- -
-
-
- -
-

{{ shortUrl }}

-
- - -
-

UUID: {{ routeUuid.substring(0, 8) }}...

-
-
@@ -108,7 +106,7 @@ import AppCredits from '../components/AppCredits.vue'; import { useRouter, useRoute } from 'vue-router'; import { colyseusService } from '../services/colyseus'; import { getStateCallbacks } from 'colyseus.js'; -import QRCode from 'qrcode'; +// QR eliminado const router = useRouter(); const route = useRoute(); @@ -116,7 +114,6 @@ const routeUuid = computed(() => (route.params as any)?.uuid as string || ''); const inputName = ref(''); const isJoining = ref(false); const colorInput = ref('#667eea'); -const qrCanvas = ref(); // Reconnection error state const reconnectionError = ref(false); @@ -129,21 +126,7 @@ let installTimer: any = null; const editingName = ref(false); const nameInputRef = ref(null); -// QR Code computed properties -const gameUrl = computed(() => { - const baseUrl = window.location.origin; - return `${baseUrl}/${routeUuid.value}`; -}); - -const shortUrl = computed(() => { - try { - const u = new URL(gameUrl.value); - // usar host corto + path uuid - return `${u.host}/${routeUuid.value}`; - } catch { - return gameUrl.value; - } -}); +// QR eliminado const isStandalone = () => (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || @@ -185,6 +168,8 @@ const previewPlayer = computed(() => ({ color: colorInput.value || playerColor.value })); +const accentColor = computed(() => playerColor.value || colorInput.value || '#667eea'); + // Define missing reactive variables const availableRooms = ref([]); const totalPlayers = ref(0); @@ -349,9 +334,6 @@ onMounted(async () => { } }); - // Generate QR code after lobby is set up - await generateQRCode(); - // Show install banner if applicable (e.g., iOS or after BIP fired) maybeShowInstallBanner(); } catch (error) { @@ -404,51 +386,7 @@ async function handleQuickPlay() { -// QR Code functions -async function generateQRCode() { - await nextTick(); - if (qrCanvas.value && routeUuid.value) { - try { - // Responsive QR size based on screen width - const isMobile = window.innerWidth <= 767; - const qrSize = isMobile ? 100 : 140; // aún más compacto - - await QRCode.toCanvas(qrCanvas.value, gameUrl.value, { - width: qrSize, - margin: 0, - color: { - dark: '#000000', - light: '#FFFFFF' - } - }); - } catch (error) { - console.error('Error generating QR code:', error); - } - } -} - -async function copyUrl() { - try { - await navigator.clipboard.writeText(gameUrl.value); - // Could add a toast notification here - console.log('URL copied to clipboard'); - } catch (error) { - console.error('Failed to copy URL:', error); - } -} - -function shareQR() { - if (navigator.share) { - navigator.share({ - title: 'Únete a mi Snatch Game', - text: `Únete a ${playerName.value || 'mí'} en Snatch Game!`, - url: gameUrl.value - }).catch(err => console.log('Error sharing:', err)); - } else { - // Fallback: copy to clipboard - copyUrl(); - } -} +// QR eliminado function goToSelector() { router.push('/'); @@ -479,7 +417,6 @@ function dismissBanner() { function startEditName() { editingName.value = true; - // Siempre iniciar la edición con el campo vacío (sin sugerencias) inputName.value = ''; nextTick(() => { nameInputRef.value?.focus(); @@ -493,14 +430,9 @@ function startEditName() { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; overflow-y: auto; -} - -@media (min-width: 768px) { - .lobby { - display: flex; - align-items: center; - justify-content: center; - } + display: flex; + align-items: center; + justify-content: center; } .lobby-container { @@ -576,14 +508,13 @@ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); } } + +.hero { display: flex; flex-direction: column; align-items: center; } +.logo-row { display: flex; justify-content: center; margin-top: 2px; margin-bottom: 2px; } .title { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; font-size: 3rem; text-align: center; - margin: 0; + margin: 2px 0 0 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; @@ -599,7 +530,7 @@ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); .subtitle { text-align: center; color: #666; - margin-top: 10px; + margin-top: 2px; font-size: 1.2rem; } @@ -656,7 +587,6 @@ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); .player-section { margin: 30px 0; padding: 20px; - background: #f8f9fa; border-radius: 10px; } @@ -848,6 +778,53 @@ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); margin-bottom: 15px; } +/* Compact setup area */ +.setup-wrapper { margin-top: 8px; } +.name-input-group.compact { display:flex; gap: 10px; align-items: center; } +.name-input-group.compact .name-input { flex: 1 1 auto; min-width: 0; } +.btn-compact { padding: 10px 14px; border-radius: 8px; } + +/* Play CTA with glasmorphism and color glow */ +.play-cta { display: flex; flex-direction: column; align-items: center; gap: 10px; margin: 28px 0; } +.current-name.ready { color: #475569; font-size: 1rem; } +.btn-play { + position: relative; + padding: 16px 32px; + font-size: 22px; + border-radius: 16px; + color: #0f172a; + background: rgba(255,255,255,0.55); + border: 1px solid rgba(255,255,255,0.4); + box-shadow: none; + overflow: hidden; + min-width: 300px; +} +.btn-play.glass { backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); } +.btn-play::before { + content: ""; + position: absolute; + left: -22%; + right: -22%; + bottom: -50%; + height: 160%; + background: + radial-gradient(70% 70% at 50% 100%, var(--accent, #667eea) 0%, var(--accent, #667eea) 42%, rgba(255,255,255,0) 85%); + filter: blur(28px); + opacity: 0.75; + pointer-events: none; +} +.btn-play::after { + content: ""; + position: absolute; + left: 0; right: 0; bottom: 0; top: 0; + background: linear-gradient(to top, var(--accent, #667eea) 0%, rgba(255,255,255,0) 55%); + filter: blur(18px); + opacity: 0.45; + pointer-events: none; +} +.btn-play:hover { transform: translateY(-2px); box-shadow: none; } +.btn-play:disabled { opacity: 0.8; cursor: not-allowed; } + .player-tag { padding: 6px 12px; background: white; @@ -1005,6 +982,22 @@ margin: 0 0 20px 0; width: 100%; margin: 0; } + + /* Back button ultra-compact on mobile */ + .topbar { gap: 6px; margin-bottom: 6px; } + .lobby-header { margin-bottom: 6px; } + .btn-back { padding: 4px 8px; font-size: 12px; border-radius: 6px; } + .btn-back:hover { transform: none; box-shadow: none; } + + /* Install banner: much more compact on mobile */ + .install-banner { padding: 6px 8px; gap: 8px; border-radius: 8px; } + .install-banner-content { gap: 6px; } + .install-icon { width: 18px; height: 18px; } + .install-text strong { font-size: 12px; } + .install-text span { font-size: 11px; } + .install-actions { gap: 4px; } + .btn-install { padding: 4px 8px; font-size: 11px; border-radius: 6px; } + .btn-dismiss { padding: 2px 4px; font-size: 12px; } .qr-container { padding: 14px; } .qr-code-wrapper { padding: 10px; margin: 10px 0; } @@ -1039,6 +1032,15 @@ margin: 0 0 20px 0; font-size: 18px; width: 100%; } + + /* Compact the setup area further on mobile */ + .name-input-group.compact { gap: 8px; } + .name-input-group.compact .name-input { padding: 10px; font-size: 15px; } + .btn-compact { padding: 10px 12px; font-size: 14px; } + .color-picker { gap: 8px; margin-top: 8px; } + .color-input { width: 38px; height: 28px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); } + .preview { margin-top: 6px; } + .play-cta .btn-play { width: 100%; max-width: 100%; min-width: 0; padding: 14px 20px; font-size: 20px; border-radius: 14px; } }