Spaces:
Running
Running
LogicalTrue commited on
Commit ·
3fe34ab
1
Parent(s): 55c3b9c
feat: add mobile profile and privacy configuration modals
Browse files- frontend/viewer.html +213 -0
frontend/viewer.html
CHANGED
|
@@ -276,6 +276,13 @@
|
|
| 276 |
<div style="padding: 6px 10px; margin-bottom: 6px; font-size: 0.8rem; font-weight: 700; color: #ffeb3b; display: flex; align-items: center; gap: 6px; border-bottom: 1px solid rgba(255,255,255,0.08);">
|
| 277 |
🪙 <span id="mobCredits">0</span> Créditos
|
| 278 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
<button id="btnMobLogout" style="width: 100%; text-align: left; background: transparent; border: none; color: #ef4444; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px;">
|
| 280 |
🚪 <span>Salir</span>
|
| 281 |
</button>
|
|
@@ -494,6 +501,89 @@
|
|
| 494 |
</div>
|
| 495 |
</div>
|
| 496 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 497 |
<script>
|
| 498 |
const urlParams = new URLSearchParams(window.location.search);
|
| 499 |
const modelUrl = urlParams.get('model');
|
|
@@ -1451,6 +1541,129 @@
|
|
| 1451 |
});
|
| 1452 |
}
|
| 1453 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1454 |
// Logout handler
|
| 1455 |
const btnMobLogout = document.getElementById('btnMobLogout');
|
| 1456 |
if (btnMobLogout) {
|
|
|
|
| 276 |
<div style="padding: 6px 10px; margin-bottom: 6px; font-size: 0.8rem; font-weight: 700; color: #ffeb3b; display: flex; align-items: center; gap: 6px; border-bottom: 1px solid rgba(255,255,255,0.08);">
|
| 277 |
🪙 <span id="mobCredits">0</span> Créditos
|
| 278 |
</div>
|
| 279 |
+
<button id="btnMobProfile" style="width: 100%; text-align: left; background: transparent; border: none; color: #ffffff; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 6px;">
|
| 280 |
+
👤 <span>Mi perfil</span>
|
| 281 |
+
</button>
|
| 282 |
+
<button id="btnMobPrivacy" style="width: 100%; text-align: left; background: transparent; border: none; color: #4caf50; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 6px;">
|
| 283 |
+
🛡️ <span>Privacidad y Seguridad</span>
|
| 284 |
+
</button>
|
| 285 |
+
<div style="height: 1px; background: rgba(255,255,255,0.08); margin: 4px 0;"></div>
|
| 286 |
<button id="btnMobLogout" style="width: 100%; text-align: left; background: transparent; border: none; color: #ef4444; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px;">
|
| 287 |
🚪 <span>Salir</span>
|
| 288 |
</button>
|
|
|
|
| 501 |
</div>
|
| 502 |
</div>
|
| 503 |
|
| 504 |
+
<!-- User Profile Modal (Mobile) -->
|
| 505 |
+
<div id="mobProfileModal" style="display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background: rgba(10, 10, 15, 0.95); backdrop-filter: blur(12px); flex-direction: column; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box;">
|
| 506 |
+
<div style="width: 100%; max-width: 420px; background: #161622; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; padding: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.8);">
|
| 507 |
+
<div style="display: flex; justify-content: space-between; align-items: center; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.1); margin-bottom: 16px;">
|
| 508 |
+
<div style="font-size: 1.1rem; font-weight: 700; color: #fff; display: flex; align-items: center; gap: 6px;">
|
| 509 |
+
👤 <span>Mi Perfil</span>
|
| 510 |
+
</div>
|
| 511 |
+
<button type="button" id="btnCloseMobProfile" style="background: rgba(255,255,255,0.1); border: none; color: #fff; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-weight: bold; font-size: 0.9rem;">✕</button>
|
| 512 |
+
</div>
|
| 513 |
+
<form id="mobProfileForm" style="display: flex; flex-direction: column; gap: 14px;">
|
| 514 |
+
<div style="display: flex; align-items: center; gap: 12px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.1); padding: 12px; border-radius: 12px;">
|
| 515 |
+
<div style="position: relative; width: 56px; height: 56px; border-radius: 50%; overflow: hidden; border: 2px solid #7c3aed; flex-shrink: 0; background: #000;">
|
| 516 |
+
<img id="mobProfileAvatarPreview" src="" alt="Avatar" style="width: 100%; height: 100%; object-fit: cover;">
|
| 517 |
+
</div>
|
| 518 |
+
<div style="flex: 1; display: flex; flex-direction: column; gap: 4px;">
|
| 519 |
+
<label for="mobProfileAvatarInput" style="font-size: 0.75rem; padding: 6px 12px; background: rgba(124, 58, 237, 0.2); border: 1px solid #7c3aed; color: #a78bfa; border-radius: 8px; font-weight: 600; cursor: pointer; display: inline-block; width: fit-content;">
|
| 520 |
+
📷 Cambiar foto
|
| 521 |
+
</label>
|
| 522 |
+
<input type="file" id="mobProfileAvatarInput" accept="image/*" style="display: none;">
|
| 523 |
+
<span style="font-size: 0.7rem; color: #a1a1aa;">Formato JPG o PNG</span>
|
| 524 |
+
</div>
|
| 525 |
+
</div>
|
| 526 |
+
|
| 527 |
+
<div>
|
| 528 |
+
<label style="font-size: 0.8rem; color: #a1a1aa; display: block; margin-bottom: 6px; font-weight: 600;">Apodo / Nickname</label>
|
| 529 |
+
<input type="text" id="mobProfileNickInput" required style="width: 100%; box-sizing: border-box; padding: 10px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.9rem;">
|
| 530 |
+
</div>
|
| 531 |
+
|
| 532 |
+
<div>
|
| 533 |
+
<label style="font-size: 0.8rem; color: #a1a1aa; display: block; margin-bottom: 6px; font-weight: 600;">Nombre Completo (Opcional)</label>
|
| 534 |
+
<input type="text" id="mobProfileFullNameInput" style="width: 100%; box-sizing: border-box; padding: 10px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.9rem;">
|
| 535 |
+
</div>
|
| 536 |
+
|
| 537 |
+
<div style="display: flex; gap: 8px; justify-content: flex-end; margin-top: 6px;">
|
| 538 |
+
<button type="button" id="btnCancelMobProfile" style="background: rgba(255,255,255,0.1); border: none; color: #fff; padding: 10px 16px; border-radius: 8px; font-weight: 600; font-size: 0.85rem; cursor: pointer;">Cancelar</button>
|
| 539 |
+
<button type="submit" style="background: #7c3aed; border: none; color: #fff; padding: 10px 18px; border-radius: 8px; font-weight: 700; font-size: 0.85rem; cursor: pointer;">Guardar</button>
|
| 540 |
+
</div>
|
| 541 |
+
</form>
|
| 542 |
+
</div>
|
| 543 |
+
</div>
|
| 544 |
+
|
| 545 |
+
<!-- Privacy & Security Modal (Mobile) -->
|
| 546 |
+
<div id="mobPrivacyModal" style="display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background: rgba(10, 10, 15, 0.95); backdrop-filter: blur(12px); flex-direction: column; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box;">
|
| 547 |
+
<div style="width: 100%; max-width: 420px; background: #161622; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; padding: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.8);">
|
| 548 |
+
<div style="display: flex; justify-content: space-between; align-items: center; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.1); margin-bottom: 16px;">
|
| 549 |
+
<div style="font-size: 1.1rem; font-weight: 700; color: #4caf50; display: flex; align-items: center; gap: 6px;">
|
| 550 |
+
🛡️ <span>Privacidad y Seguridad</span>
|
| 551 |
+
</div>
|
| 552 |
+
<button type="button" id="btnCloseMobPrivacy" style="background: rgba(255,255,255,0.1); border: none; color: #fff; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-weight: bold; font-size: 0.9rem;">✕</button>
|
| 553 |
+
</div>
|
| 554 |
+
<form id="mobPrivacyForm" style="display: flex; flex-direction: column; gap: 12px;">
|
| 555 |
+
<div>
|
| 556 |
+
<label style="font-size: 0.8rem; color: #a1a1aa; display: block; margin-bottom: 6px; font-weight: 600;">Correo Electrónico</label>
|
| 557 |
+
<input type="email" id="mobPrivacyEmailInput" style="width: 100%; box-sizing: border-box; padding: 10px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.9rem;">
|
| 558 |
+
</div>
|
| 559 |
+
|
| 560 |
+
<div style="border-top: 1px solid rgba(255,255,255,0.1); padding-top: 10px; margin-top: 4px;">
|
| 561 |
+
<div style="font-size: 0.85rem; font-weight: 700; color: #fff; margin-bottom: 8px;">Cambiar Contraseña</div>
|
| 562 |
+
|
| 563 |
+
<div style="margin-bottom: 8px;">
|
| 564 |
+
<label style="font-size: 0.75rem; color: #a1a1aa; display: block; margin-bottom: 4px;">Contraseña Actual</label>
|
| 565 |
+
<input type="password" id="mobPrivacyCurrentPasswordInput" style="width: 100%; box-sizing: border-box; padding: 8px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.85rem;">
|
| 566 |
+
</div>
|
| 567 |
+
|
| 568 |
+
<div style="margin-bottom: 8px;">
|
| 569 |
+
<label style="font-size: 0.75rem; color: #a1a1aa; display: block; margin-bottom: 4px;">Nueva Contraseña</label>
|
| 570 |
+
<input type="password" id="mobPrivacyNewPasswordInput" style="width: 100%; box-sizing: border-box; padding: 8px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.85rem;">
|
| 571 |
+
</div>
|
| 572 |
+
|
| 573 |
+
<div>
|
| 574 |
+
<label style="font-size: 0.75rem; color: #a1a1aa; display: block; margin-bottom: 4px;">Repetir Nueva Contraseña</label>
|
| 575 |
+
<input type="password" id="mobPrivacyConfirmPasswordInput" style="width: 100%; box-sizing: border-box; padding: 8px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.85rem;">
|
| 576 |
+
</div>
|
| 577 |
+
</div>
|
| 578 |
+
|
| 579 |
+
<div style="display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px;">
|
| 580 |
+
<button type="button" id="btnCancelMobPrivacy" style="background: rgba(255,255,255,0.1); border: none; color: #fff; padding: 10px 16px; border-radius: 8px; font-weight: 600; font-size: 0.85rem; cursor: pointer;">Cancelar</button>
|
| 581 |
+
<button type="submit" style="background: #4caf50; border: none; color: #fff; padding: 10px 18px; border-radius: 8px; font-weight: 700; font-size: 0.85rem; cursor: pointer;">Guardar</button>
|
| 582 |
+
</div>
|
| 583 |
+
</form>
|
| 584 |
+
</div>
|
| 585 |
+
</div>
|
| 586 |
+
|
| 587 |
<script>
|
| 588 |
const urlParams = new URLSearchParams(window.location.search);
|
| 589 |
const modelUrl = urlParams.get('model');
|
|
|
|
| 1541 |
});
|
| 1542 |
}
|
| 1543 |
|
| 1544 |
+
// Mobile Profile and Privacy Modals Bindings
|
| 1545 |
+
const btnMobProfile = document.getElementById('btnMobProfile');
|
| 1546 |
+
const btnMobPrivacy = document.getElementById('btnMobPrivacy');
|
| 1547 |
+
const mobProfileModal = document.getElementById('mobProfileModal');
|
| 1548 |
+
const mobPrivacyModal = document.getElementById('mobPrivacyModal');
|
| 1549 |
+
const btnCloseMobProfile = document.getElementById('btnCloseMobProfile');
|
| 1550 |
+
const btnCancelMobProfile = document.getElementById('btnCancelMobProfile');
|
| 1551 |
+
const btnCloseMobPrivacy = document.getElementById('btnCloseMobPrivacy');
|
| 1552 |
+
const btnCancelMobPrivacy = document.getElementById('btnCancelMobPrivacy');
|
| 1553 |
+
const mobProfileForm = document.getElementById('mobProfileForm');
|
| 1554 |
+
const mobPrivacyForm = document.getElementById('mobPrivacyForm');
|
| 1555 |
+
const mobProfileAvatarInput = document.getElementById('mobProfileAvatarInput');
|
| 1556 |
+
const mobProfileAvatarPreview = document.getElementById('mobProfileAvatarPreview');
|
| 1557 |
+
|
| 1558 |
+
if (btnMobProfile) {
|
| 1559 |
+
btnMobProfile.addEventListener('click', (e) => {
|
| 1560 |
+
e.stopPropagation();
|
| 1561 |
+
if (mobUserDropdown) mobUserDropdown.style.display = 'none';
|
| 1562 |
+
fetch('/api/me').then(res => res.json()).then(data => {
|
| 1563 |
+
const nickInput = document.getElementById('mobProfileNickInput');
|
| 1564 |
+
const fullNameInput = document.getElementById('mobProfileFullNameInput');
|
| 1565 |
+
if (nickInput) nickInput.value = data.nick || data.username || '';
|
| 1566 |
+
if (fullNameInput) fullNameInput.value = data.full_name || '';
|
| 1567 |
+
if (mobProfileAvatarPreview && data.avatar) mobProfileAvatarPreview.src = data.avatar;
|
| 1568 |
+
if (mobProfileModal) mobProfileModal.style.display = 'flex';
|
| 1569 |
+
}).catch(() => {
|
| 1570 |
+
if (mobProfileModal) mobProfileModal.style.display = 'flex';
|
| 1571 |
+
});
|
| 1572 |
+
});
|
| 1573 |
+
}
|
| 1574 |
+
|
| 1575 |
+
if (btnMobPrivacy) {
|
| 1576 |
+
btnMobPrivacy.addEventListener('click', (e) => {
|
| 1577 |
+
e.stopPropagation();
|
| 1578 |
+
if (mobUserDropdown) mobUserDropdown.style.display = 'none';
|
| 1579 |
+
fetch('/api/me').then(res => res.json()).then(data => {
|
| 1580 |
+
const emailInput = document.getElementById('mobPrivacyEmailInput');
|
| 1581 |
+
if (emailInput) emailInput.value = data.email || '';
|
| 1582 |
+
if (mobPrivacyModal) mobPrivacyModal.style.display = 'flex';
|
| 1583 |
+
}).catch(() => {
|
| 1584 |
+
if (mobPrivacyModal) mobPrivacyModal.style.display = 'flex';
|
| 1585 |
+
});
|
| 1586 |
+
});
|
| 1587 |
+
}
|
| 1588 |
+
|
| 1589 |
+
if (mobProfileAvatarInput && mobProfileAvatarPreview) {
|
| 1590 |
+
mobProfileAvatarInput.addEventListener('change', (e) => {
|
| 1591 |
+
const file = e.target.files[0];
|
| 1592 |
+
if (file) {
|
| 1593 |
+
const reader = new FileReader();
|
| 1594 |
+
reader.onload = (evt) => {
|
| 1595 |
+
mobProfileAvatarPreview.src = evt.target.result;
|
| 1596 |
+
};
|
| 1597 |
+
reader.readAsDataURL(file);
|
| 1598 |
+
}
|
| 1599 |
+
});
|
| 1600 |
+
}
|
| 1601 |
+
|
| 1602 |
+
[btnCloseMobProfile, btnCancelMobProfile].forEach(btn => {
|
| 1603 |
+
if (btn) btn.addEventListener('click', () => mobProfileModal.style.display = 'none');
|
| 1604 |
+
});
|
| 1605 |
+
|
| 1606 |
+
[btnCloseMobPrivacy, btnCancelMobPrivacy].forEach(btn => {
|
| 1607 |
+
if (btn) btn.addEventListener('click', () => mobPrivacyModal.style.display = 'none');
|
| 1608 |
+
});
|
| 1609 |
+
|
| 1610 |
+
if (mobProfileForm) {
|
| 1611 |
+
mobProfileForm.addEventListener('submit', async (e) => {
|
| 1612 |
+
e.preventDefault();
|
| 1613 |
+
const nick = document.getElementById('mobProfileNickInput')?.value.trim();
|
| 1614 |
+
const full_name = document.getElementById('mobProfileFullNameInput')?.value.trim();
|
| 1615 |
+
const avatar = mobProfileAvatarPreview?.src;
|
| 1616 |
+
try {
|
| 1617 |
+
const res = await fetch('/api/update-profile', {
|
| 1618 |
+
method: 'POST',
|
| 1619 |
+
headers: { 'Content-Type': 'application/json' },
|
| 1620 |
+
body: JSON.stringify({ nick, full_name, avatar })
|
| 1621 |
+
});
|
| 1622 |
+
if (res.ok) {
|
| 1623 |
+
alert('¡Perfil actualizado con éxito!');
|
| 1624 |
+
if (mobProfileModal) mobProfileModal.style.display = 'none';
|
| 1625 |
+
fetchUserInfo();
|
| 1626 |
+
} else {
|
| 1627 |
+
alert('Error al actualizar el perfil.');
|
| 1628 |
+
}
|
| 1629 |
+
} catch (err) {
|
| 1630 |
+
alert('Error de conexión al actualizar perfil.');
|
| 1631 |
+
}
|
| 1632 |
+
});
|
| 1633 |
+
}
|
| 1634 |
+
|
| 1635 |
+
if (mobPrivacyForm) {
|
| 1636 |
+
mobPrivacyForm.addEventListener('submit', async (e) => {
|
| 1637 |
+
e.preventDefault();
|
| 1638 |
+
const email = document.getElementById('mobPrivacyEmailInput')?.value.trim();
|
| 1639 |
+
const current_password = document.getElementById('mobPrivacyCurrentPasswordInput')?.value;
|
| 1640 |
+
const new_password = document.getElementById('mobPrivacyNewPasswordInput')?.value;
|
| 1641 |
+
const confirm_password = document.getElementById('mobPrivacyConfirmPasswordInput')?.value;
|
| 1642 |
+
|
| 1643 |
+
if (new_password && new_password !== confirm_password) {
|
| 1644 |
+
alert('Las contraseñas no coinciden.');
|
| 1645 |
+
return;
|
| 1646 |
+
}
|
| 1647 |
+
|
| 1648 |
+
try {
|
| 1649 |
+
const res = await fetch('/api/update-privacy', {
|
| 1650 |
+
method: 'POST',
|
| 1651 |
+
headers: { 'Content-Type': 'application/json' },
|
| 1652 |
+
body: JSON.stringify({ email, current_password, new_password })
|
| 1653 |
+
});
|
| 1654 |
+
const data = await res.json();
|
| 1655 |
+
if (res.ok && data.success) {
|
| 1656 |
+
alert('¡Configuración de privacidad guardada!');
|
| 1657 |
+
if (mobPrivacyModal) mobPrivacyModal.style.display = 'none';
|
| 1658 |
+
} else {
|
| 1659 |
+
alert(data.error || 'Error al guardar privacidad.');
|
| 1660 |
+
}
|
| 1661 |
+
} catch (err) {
|
| 1662 |
+
alert('Error de conexión al guardar privacidad.');
|
| 1663 |
+
}
|
| 1664 |
+
});
|
| 1665 |
+
}
|
| 1666 |
+
|
| 1667 |
// Logout handler
|
| 1668 |
const btnMobLogout = document.getElementById('btnMobLogout');
|
| 1669 |
if (btnMobLogout) {
|