diff --git a/client/src/views/Credits.vue b/client/src/views/Credits.vue
index 9ef6bfc..e2f476b 100644
--- a/client/src/views/Credits.vue
+++ b/client/src/views/Credits.vue
@@ -27,18 +27,158 @@
Gracias por jugar y contribuir a la comunidad. Si querés colaborar, difundir o proponer mejoras, ¡escribinos!
+
+
+
+
Mensajes de la comunidad
+
+
+
+
+
+
+
+ #{{ messages.length - index }}
+ {{ formatDate(message.timestamp) }}
+
+
{{ message.content }}
+
+
+
+
+ No hay mensajes todavía. ¡Sé el primero en dejar uno!
+
+
diff --git a/client/src/views/DemoGame.vue b/client/src/views/DemoGame.vue
index 4a3f78a..8d7c7c6 100644
--- a/client/src/views/DemoGame.vue
+++ b/client/src/views/DemoGame.vue
@@ -82,6 +82,9 @@
⏸️
Juego en pausa
Esperando a que ambos jugadores estén conectados…
+
+
+
@@ -489,4 +492,5 @@ async function leaveGame() {
.pause-box .icon { font-size: 48px; margin-bottom: 8px; }
.pause-box .title { font-weight: 800; font-size: 20px; }
.pause-box .hint { margin-top: 6px; color:#666; font-size: 14px; }
+.pause-box .actions { margin-top: 16px; }
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index 334b0f1..658ce28 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -47,6 +47,9 @@
⏸️
Game Paused
Waiting for players to reconnect...
+
+
+
diff --git a/client/src/views/Lobby.vue b/client/src/views/Lobby.vue
index ffd44ce..23b523a 100644
--- a/client/src/views/Lobby.vue
+++ b/client/src/views/Lobby.vue
@@ -31,6 +31,16 @@
Arena de intercambio social
+
+
+
⚠️
+
+
No se pudo reconectar a la partida
+
{{ reconnectionErrorMessage }}
+
+
+
+
();
+
+// Reconnection error state
+const reconnectionError = ref(false);
+const reconnectionErrorMessage = ref('');
const showInstallBanner = ref(false);
const canPromptInstall = ref(false);
let deferredPrompt: any = null;
@@ -204,8 +218,18 @@ onMounted(async () => {
colyseusService.lobbyRoom.value = null;
}
await router.push(`/${routeUuid.value}/demo`);
- } catch (error) {
+ } catch (error: any) {
console.error('Reconnection failed:', error);
+
+ // Check if it's a reconnection token error
+ const errorMessage = error?.message || String(error);
+ if (errorMessage.includes('reconnection token invalid') || errorMessage.includes('expired')) {
+ reconnectionError.value = true;
+ reconnectionErrorMessage.value = 'Otro jugador ya está usando este UUID. Si eres tú en otro dispositivo, cierra esa sesión primero.';
+ } else {
+ reconnectionError.value = true;
+ reconnectionErrorMessage.value = 'Error al reconectar: ' + errorMessage;
+ }
}
});
@@ -447,6 +471,11 @@ function goToSelector() {
router.push('/');
}
+function dismissError() {
+ reconnectionError.value = false;
+ reconnectionErrorMessage.value = '';
+}
+
async function triggerInstall() {
try {
if (!deferredPrompt) return;
@@ -591,6 +620,56 @@ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
font-size: 1.2rem;
}
+/* Error notification styles */
+.error-notification {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin: 20px 0;
+ padding: 16px;
+ background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
+ border: 1px solid #fca5a5;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.15);
+}
+
+.error-icon {
+ font-size: 24px;
+ flex-shrink: 0;
+}
+
+.error-content {
+ flex: 1;
+}
+
+.error-title {
+ font-weight: 700;
+ color: #991b1b;
+ margin-bottom: 4px;
+}
+
+.error-message {
+ color: #dc2626;
+ font-size: 14px;
+ line-height: 1.4;
+}
+
+.btn-dismiss-error {
+ background: none;
+ border: none;
+ font-size: 18px;
+ color: #dc2626;
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 4px;
+ transition: background-color 0.2s;
+ flex-shrink: 0;
+}
+
+.btn-dismiss-error:hover {
+ background: rgba(220, 38, 38, 0.1);
+}
+
.player-section {
margin: 30px 0;
padding: 20px;
diff --git a/client/src/views/UuidSelector.vue b/client/src/views/UuidSelector.vue
index bc60d29..2158078 100644
--- a/client/src/views/UuidSelector.vue
+++ b/client/src/views/UuidSelector.vue
@@ -37,6 +37,9 @@
+
@@ -353,6 +356,10 @@ function goToLeaderboard() {
router.push('/leaderboard');
}
+function goToCredits() {
+ router.push('/credits');
+}
+
// Context menu functions
function showContextMenu(event: MouseEvent, uuidInfo: UuidInfo) {
contextMenu.value = {