186 lines
7.6 KiB
Text
186 lines
7.6 KiB
Text
===============================================================
|
|
CAMBIO QR — DOCUMENTACION TECNICA
|
|
(commit 54ad8a1)
|
|
===============================================================
|
|
|
|
OBJETIVO:
|
|
Añadir QR codes en tres puntos de la app para facilitar
|
|
el uso en movil: escanear en vez de copiar strings largos.
|
|
|
|
LIBRERIA:
|
|
qrcode ^1.5.4
|
|
Ya estaba en package.json (usada previamente en wallet_view.js).
|
|
Path de require dentro de las vistas:
|
|
const QRCode = require('../server/node_modules/qrcode');
|
|
Uso: QRCode.toString(string, { type: 'svg' }) -> devuelve SVG como string
|
|
Render: div({ class: 'qr-code', innerHTML: svgString })
|
|
innerHTML es un atributo que hyperaxe inyecta directamente como HTML crudo.
|
|
|
|
--------------------------------------------------------------
|
|
1. TRIBE INVITES — tribes_view.js
|
|
--------------------------------------------------------------
|
|
FUNCION MODIFICADA: renderInvitePage (linea 101)
|
|
|
|
ANTES:
|
|
exports.renderInvitePage = (inviteCode) => { ... }
|
|
|
|
DESPUES:
|
|
exports.renderInvitePage = async (inviteCode) => {
|
|
const qrSvg = inviteCode
|
|
? await QRCode.toString(inviteCode, { type: 'svg' })
|
|
: '';
|
|
// ... div con el codigo + div con innerHTML: qrSvg
|
|
}
|
|
|
|
PORQUE NO REQUIRIO CAMBIOS EN BACKEND:
|
|
backend.js ya tenia: ctx.body = await renderInvitePage(...)
|
|
El await ya estaba, hacer la funcion async no rompio nada.
|
|
|
|
QUE SE MUESTRA:
|
|
- Titulo h2 con el nombre de la tribe
|
|
- p con el invite code en texto
|
|
- SVG del QR debajo del codigo (clase: 'div-center qr-code')
|
|
- Si inviteCode es null/undefined -> qrSvg = '' -> el div QR no se renderiza
|
|
|
|
--------------------------------------------------------------
|
|
2. PUB INVITES — invites_view.js
|
|
--------------------------------------------------------------
|
|
FUNCION MODIFICADA: invitesView (linea 34)
|
|
|
|
La funcion padre invitesView ya era async (porque hace otras ops async).
|
|
El bloque del QR usa un IIFE async para poder hacer await dentro
|
|
de una expresion inline:
|
|
|
|
await (async () => {
|
|
if (!snhInvite) return null;
|
|
const qrSvg = snhInvite.code
|
|
? await QRCode.toString(snhInvite.code, { type: 'svg' })
|
|
: '';
|
|
return div({ class: 'snh-invite-box' },
|
|
h3({ class: 'snh-invite-name' }, snhInvite.name),
|
|
span({ class: 'snh-invite-code' }, snhInvite.code),
|
|
qrSvg ? div({ class: 'div-center qr-code', innerHTML: qrSvg }) : null
|
|
);
|
|
})()
|
|
|
|
POR QUE EL IIFE:
|
|
La estructura de hyperaxe es funcional (cada elemento es un argumento
|
|
pasado al elemento padre). No puedes poner un await suelto en medio
|
|
de una lista de argumentos. El IIFE resuelve eso sin reestructurar
|
|
toda la funcion.
|
|
|
|
ALTERNATIVA MAS LIMPIA (si se quisiera refactorizar):
|
|
Extraer la caja snhInvite a una funcion separada async
|
|
y llamarla antes de construir el arbol.
|
|
El IIFE funciona, pero es menos legible para alguien que no lo conozca.
|
|
|
|
DATO DEL CONTEXTO:
|
|
snhInvite viene de: src/configs/snh-invite-code.json
|
|
Formato: { name: string, code: string }
|
|
Es el invite code del pub publico de la instancia.
|
|
|
|
--------------------------------------------------------------
|
|
3. PERFIL DE USUARIO — inhabitants_view.js
|
|
--------------------------------------------------------------
|
|
TRES FUNCIONES MODIFICADAS:
|
|
|
|
--- renderInhabitantCard (linea 83) ---
|
|
async (user, filter, currentUserId)
|
|
QR SOLO se genera si isMe === true (el usuario viendo su propio perfil).
|
|
Razon: evitar generar 50 QRs en la lista de inhabitants.
|
|
|
|
const isMe = user.id === currentUserId;
|
|
const qrSvg = isMe && user.id
|
|
? await QRCode.toString(user.id, { type: 'svg' })
|
|
: '';
|
|
|
|
El QR aparece dentro de 'div.user-id-qr', junto al enlace user-link.
|
|
|
|
--- inhabitantsView (linea 177) ---
|
|
Debia hacerse async porque llama a renderInhabitantCard (que es async).
|
|
Usa Promise.all para no hacer await uno por uno:
|
|
|
|
await Promise.all(inhabitants.map(user =>
|
|
renderInhabitantCard(user, filter, currentUserId)
|
|
))
|
|
|
|
IMPORTANTE: Solo genera QR para isMe, asi que en la practica
|
|
solo hay 1 await real de QRCode.toString() en toda la lista.
|
|
El Promise.all es correcto igualmente para el futuro.
|
|
|
|
--- inhabitantsProfileView (linea 233) ---
|
|
QR se genera SIEMPRE (para cualquier perfil, no solo el propio).
|
|
Razon: la pagina de perfil individual es de una sola persona,
|
|
tiene sentido mostrar su QR para que otros lo escaneen y lo sigan.
|
|
|
|
const profileId = payload?.id || id || viewedId;
|
|
const qrSvg = profileId
|
|
? await QRCode.toString(profileId, { type: 'svg' })
|
|
: '';
|
|
|
|
Aparece en 'div.inhabitant-left' junto al avatar.
|
|
|
|
--------------------------------------------------------------
|
|
CSS PARA LOS QR
|
|
--------------------------------------------------------------
|
|
En style.css (al final del archivo):
|
|
.qr-code svg -> max-width: 200px, display block, centrado
|
|
.qr-code-inline svg -> max-width: 120px (en lista inhabitants, mas pequeno)
|
|
.qr-code-profile svg -> max-width: 150px (en perfil individual)
|
|
.user-id-qr -> flex column, align-items center
|
|
.invite-code-text -> font monospace, fondo sutil
|
|
|
|
En mobile.css:
|
|
.qr-code svg -> max-width: 180px
|
|
.qr-code-inline svg -> max-width: 120px
|
|
.qr-code-profile svg -> max-width: 150px
|
|
.qr-lightbox-overlay -> lightbox CSS puro (sin JS), para ampliar QR
|
|
.qr-lightbox-anchor -> ancla que activa el :target del overlay
|
|
|
|
En OasisMobile.css (tema oscuro):
|
|
.qr-code svg rect -> fill: #1A1A1A (fondo del QR oscuro)
|
|
.qr-code svg path -> fill: #FFD700 (puntos del QR en dorado)
|
|
|
|
--------------------------------------------------------------
|
|
QUE PUEDE GUSTAR
|
|
--------------------------------------------------------------
|
|
- No rompe nada: el backend ya tenia await en todas las rutas afectadas
|
|
- Libreria qrcode ya existia en el proyecto (no se añadio dependencia nueva)
|
|
- QR solo donde tiene sentido: no hay QRs innecesarios en cada elemento
|
|
- En lista de inhabitants, QR solo para el propio usuario = sin overhead
|
|
- SVG directo (no imagen base64 ni PNG), escala perfecto en cualquier pantalla
|
|
- El QR del perfil es el SSB ID completo (@xxx.ed25519), scaneable con
|
|
cualquier cliente SSB para seguir al usuario
|
|
- Lightbox CSS puro para ampliar QR en movil sin JS adicional
|
|
- Tema oscuro: colores del QR adaptados (#1A1A1A fondo, #FFD700 puntos)
|
|
|
|
--------------------------------------------------------------
|
|
QUE PUEDE NO GUSTAR / PUNTOS DE DISCUSION
|
|
--------------------------------------------------------------
|
|
1. El IIFE async en invites_view.js es un patron poco comun.
|
|
Funciona, pero si alguien lee el codigo por primera vez le puede
|
|
parecer raro. Alternativa: extraer a funcion separada.
|
|
|
|
2. El QR en inhabitantsProfileView se genera para TODOS los perfiles.
|
|
Si se visita el perfil de un usuario con un SSB ID muy largo,
|
|
el QR es pequeno y puede ser dificil de escanear. Considerar
|
|
mostrar solo para isMe si se quiere ser mas conservador.
|
|
|
|
3. El innerHTML con SVG crudo: hyperaxe lo inyecta tal cual.
|
|
Si el SVG tuviera algun caracter extraño o fallo de la libreria qrcode,
|
|
podria romper el HTML. En la practica qrcode es fiable, pero
|
|
es una inyeccion directa a tener en cuenta.
|
|
|
|
4. No hay boton "Copiar codigo" junto al QR.
|
|
En movil es util para pegar el invite en otro lado.
|
|
Pendiente de implementar.
|
|
|
|
5. El QR del tribe invite code es un string hex aleatorio (32 bytes = 64 chars).
|
|
No es una URL ni tiene deep link. Solo sirve para copiar/pegar en la app,
|
|
no para escanear con una app de QR generica de terceros.
|
|
El del SSB ID si que es estandar y reconocible.
|
|
|
|
6. No se ha implementado lectura de QR (solo generacion).
|
|
Leer QR desde la camara requiere acceso nativo Android.
|
|
Esto esta fuera del scope del Node.js embebido, habria que
|
|
hacerlo desde el wrapper Kotlin y pasar el resultado al servidor.
|