movil: usabilidad tras la prueba en el Pixel

Lo que salio de recorrer la app como alguien de la acampada:

- borrar (tribus y todo lo que usa renderContentActions) pide un segundo
  toque; la X va la ultima de la fila
- Karvan: Llamar empieza solo con voz (si se negaba la camara no arrancaba
  nada); botones con icono y nombre; textos traducidos; ayuda para invitar
- votaciones: tipo Si/No, semaforo o termometro (el servidor ya aceptaba
  options); resultados en barras; aviso a quien la crea de que no puede votar
- workflow activistas con tribus, parlamento, votaciones y eventos
- seccion apagada: /modules dice cual era y la activa de un toque
- Modulos en el movil: agrupados como el lanzador, interruptores y Guardar fijo
- tribus en filas compactas; secciones de una tribu en tira de fichas + Mas
- lanzador y barra con iconos SVG en vez de letras vai
- Ajustes: idioma y aspecto primero; idiomas de un toque
- aviso de bienvenida solo en la portada y en una linea; Ahora no al final
- pie de una linea con el estado de sincronizacion en claro
- mobile_ux.css para los dos skins: un margen de 14px (eran 74 por lado en
  las fichas), Anton solo en titulos y boton principal, buscador en una linea,
  la barra de abajo se esconde con el teclado
This commit is contained in:
SITO 2026-10-19 12:37:58 +02:00
parent 2e3ff1e79f
commit 4dd573662c
14 changed files with 1279 additions and 54 deletions

View file

@ -139,6 +139,15 @@ const settingsView = (params) => {
);
}
// movil: primero lo que se toca (idioma, aspecto, tipo de uso, portada) y despues
// lo tecnico (identidades, red, indices...). Se reordena por id, sin tocar las secciones
const MOBILE_FIRST = ['update', 'language', 'theme', 'workflows', 'home-page'];
const mobileOrder = (parts) => {
const list = parts.filter(Boolean);
const rank = (el) => { const i = MOBILE_FIRST.indexOf(el && el.id); return i === -1 ? MOBILE_FIRST.length : i; };
return list.map((el, i) => ({ el, i })).sort((x, y) => rank(x.el) - rank(y.el) || x.i - y.i).map(x => x.el);
};
const filter = params.filter === 'multiverse' && modOn('fediverse') ? 'multiverse' : params.filter === 'content' ? 'content' : 'oasis';
const oasisParts = [
updatePanel,
@ -147,7 +156,13 @@ const settingsView = (params) => {
div({ class: "tags-header" },
h2(i18n.language),
p(i18n.languageDescription),
form(
// movil: cada idioma es un boton que se aplica al tocarlo (antes desplegable,
// elegir y otro boton mas abajo)
isMobile ? form({ action: "/language", method: "post", class: "lang-pick" },
...[["Español", "es"], ["Català", "ca"], ["Euskara", "eu"], ["Galego", "gl"], ["English", "en"], ["Français", "fr"],
["Português", "pt"], ["Italiano", "it"], ["Deutsch", "de"], ["Русский", "ru"], ["العربية", "ar"], ["हिन्दी", "hi"], ["中文", "zh"]]
.map(([name, code]) => button({ type: "submit", name: "language", value: code, class: code === selectedLanguage ? "lang-chip is-current" : "lang-chip", lang: code }, name))
) : form(
{ action: "/language", method: "post" },
select({ name: "language" }, [
languageOption("English", "en"),
@ -674,7 +689,7 @@ const settingsView = (params) => {
modOn('fediverse') ? a({ class: filter === 'multiverse' ? 'filter-btn active' : 'filter-btn', href: '/settings?filter=multiverse' }, String(i18n.multiverse).toUpperCase()) : null
)
),
...(filter === 'multiverse' ? multiverseParts : filter === 'content' ? contentParts : oasisParts)
...(filter === 'multiverse' ? multiverseParts : filter === 'content' ? contentParts : (isMobile ? mobileOrder(oasisParts) : oasisParts))
);
};