diff --git a/README.md b/README.md index 9c7fd71..4d969c1 100644 --- a/README.md +++ b/README.md @@ -18,33 +18,38 @@ está qué se puede tocar y qué no. En corto: el backend, el middleware, los mo las traducciones son de epsylon y no se tocan; lo nuestro va en hojas, vistas y rutas propias. -Versión base: **Oasis 1.3.3** · Rama: `PRUEBAS` · +Versión base: Oasis 1.3.3 · Rama: `PRUEBAS` · [Documentación completa](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki) · [Versión de escritorio](https://gitea.laenre.net/hacklab/OASIS_LINUX) ![Oasis Mobile: lanzador, filtros y barra de atajos](docs/capturas/00-portada.png) -**[⬇ Descargar la APK](https://gitea.laenre.net/hacklab/OASIS_MOBILE/releases)** · +[⬇ Descargar la APK](https://gitea.laenre.net/hacklab/OASIS_MOBILE/releases) · Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo ### Novedades de esta versión -- **Al día con Oasis 1.3.3**: karma en el perfil, salas con directo, fotos selladas en +- Al día con Oasis 1.3.3: karma en el perfil, salas con directo, fotos selladas en chats privados y tribus, visibilidad de menciones y las traducciones nuevas. -- **Tema Acampada por defecto**: blanco, negro y cian, títulos en Anton y botones que - se hunden al pulsar, con los colores de acampada26s.net. Los otros temas siguen en - *Ajustes → Tema*. -- **Arranca en modo acampada**: chats, teléfono, salas, pads, foro, wiki, tribus, - parlamento, votaciones y eventos. El resto se activa en *Ajustes → Workflows*. -- **Llamadas y salas del dev**: fuera Karvan, nuestras salas propias. Ahora se usan el - Teléfono y las Salas de Oasis, con el audio y la cámara de Android enchufados por la - rama. -- **Filtros en fichas**: en chats, feed, foros o pads los filtros ya no son botones - gigantes que tapan el contenido, sino fichas pequeñas en fila. -- **Tarjetas más limpias**: borrar, fijar, enlazar y abrir van en una fila de iconos. -- **Fotos más seguras**: si una foto no se puede limpiar de metadatos (ubicación, - cámara…), no se sube y se avisa. Antes se subía tal cual. -- **DNS**: primero el resolutor del sistema, que respeta la VPN y el DNS privado de +- Arranca en modo acampada: chats, teléfono, salas, pads, foro, wiki, tribus, + parlamento, votaciones y eventos. El resto se enciende en *Módulos* o en + *Ajustes → Workflows*. +- Las llamadas son las de Oasis. Fuera Karvan, el módulo propio de salas: ahora se usan + el Teléfono y las Salas del dev, con el micrófono y la cámara del móvil enchufados. +- Votaciones como en asamblea: al crear una se elige Sí/No, semáforo (verde, amarillo, + rojo) o termómetro (del 1 al 5). Los resultados salen en barras. +- Borrar pide confirmación. Antes la X de una tribu la borraba de un toque. +- Iconos que se entienden en el lanzador y en la barra, en vez de letras del alfabeto + vai. La barra marca dónde estás y se esconde al escribir. +- Tribus en filas: doce comisiones caben en tres pantallas, no en diez. Dentro de una + tribu, las secciones van en una tira de fichas. +- Módulos con interruptores, agrupados como en el lanzador. Si entras en una sección + apagada, te lo dice y la enciendes de un toque. +- Pantallas más aprovechadas: un margen de 14 px en vez de los 74 que se sumaban, una + sola letra para botones, buscador en una línea y el buzón en pocas líneas por mensaje. +- Fotos más seguras: si una foto no se puede limpiar de metadatos (ubicación, + cámara…), no se sube y se avisa. +- DNS: primero el resolutor del sistema, que respeta la VPN y el DNS privado de Android; los públicos quedan de respaldo. --- @@ -53,54 +58,57 @@ Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo | | Oasis oficial | Esta rama | |---|---|---| -| **Llamadas y salas** | Teléfono y Salas, sin micrófono ni cámara en Android | Los mismos, con el audio y la cámara del móvil | -| **Interfaz de móvil** | El menú de escritorio en una pantalla pequeña | Tema Acampada, lanzador de módulos, filtros en fichas y barra de cuatro atajos | -| **Primer arranque** | Casi sesenta módulos encendidos | Modo acampada: lo justo para organizarse | -| **Varias identidades** | Una por instalación | Varias en el mismo móvil, con aviso si dos comparten clave | -| **Respuesta citada** | — | Responder a un mensaje concreto dentro de un chat | -| **Envoltorio Android** | APK sin permisos de cámara ni micrófono | Proyecto Gradle propio: permisos, subida de ficheros, servicio que sobrevive | -| **Metadatos de las fotos** | Se quitan solo si está `sharp`; en Android **nunca** | Se quitan siempre: GPS, marca, modelo, número de serie | +| Llamadas y salas | Teléfono y Salas, pero sin micrófono ni cámara en Android | Los mismos, con el micrófono y la cámara del móvil | +| Interfaz de móvil | El menú de escritorio en una pantalla pequeña | Tema Acampada, lanzador con iconos, fichas, barra de cuatro atajos | +| Primer arranque | Casi sesenta módulos encendidos | Modo acampada: lo justo para organizarse | +| Votaciones | Seis opciones fijas en una tabla | Sí/No, semáforo o termómetro, con barras | +| Varias identidades | Una por instalación | Varias en el mismo móvil, con aviso si dos comparten clave | +| Envoltorio Android | APK sin permisos de cámara ni micrófono | Proyecto Gradle propio: permisos, subida de ficheros, servicio que sobrevive | +| Metadatos de las fotos | Se quitan solo si está `sharp`; en Android nunca | Se quitan siempre: GPS, marca, modelo, número de serie | Los nombres de las categorías, los módulos y el resto de la aplicación son los de -epsylon, sin tocar. +epsylon, sin tocar. Con el tema OasisMobile se ve el del propio dev. --- ## Qué es Oasis, en cuatro dibujos -Una red social **sin servidor**: publicas en tu propio equipo y los demás se traen una +Una red social sin servidor: publicas en tu propio equipo y los demás se traen una copia. No hay cuenta, no hay contraseña y nadie puede borrarte. ![Qué es Oasis](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/raw/img/esq/01-red.png) -Los mensajes viajan **de vecino en vecino**, así que llegan a gente con la que nunca te +Los mensajes viajan de vecino en vecino, así que llegan a gente con la que nunca te has conectado. Por eso funciona incluso sin internet, entre dos móviles en la misma wifi. ![Cómo llega un mensaje](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/raw/img/esq/02-gossip.png) -Un **pub** es solo un nodo que no se apaga nunca. No es un servidor con tus datos: no +Un pub es solo un nodo que no se apaga nunca. No es un servidor con tus datos: no guarda tu identidad, no decide qué ves y no puede borrarte. ![Qué es un pub](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/raw/img/esq/04-pub.png) -Todo lo que publicas se encadena y va **firmado con tu clave**. Nadie puede colar un +Todo lo que publicas se encadena y va firmado con tu clave. Nadie puede colar un mensaje en tu nombre ni cambiar lo que dijiste. ![Tu registro](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/raw/img/esq/14-registro.png) > La explicación completa —los saltos, las tribus, el LARP, los módulos— está en -> **[Entender Oasis en diez minutos](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Entender)**. +> [Entender Oasis en diez minutos](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Entender). --- ## Llamadas y salas +![Llamadas: las del dev, con el móvil enchufado](docs/capturas/esquema-llamadas.png) + El Teléfono (llamadas de uno a uno, con buzón de voz) y las Salas (hasta cincuenta personas, con vídeo, grabación y emisión) son de Oasis. La APK oficial no pide -permisos de micrófono ni de cámara, así que en Android no se pueden usar. Esta rama -trae su propio envoltorio, que sí los pide, y enchufa el audio, la cámara y la emisión -del móvil en los puntos que el propio Oasis deja para eso (`setAudioFactory` y -compañía). La rama no tiene llamadas propias. +permisos de micrófono ni de cámara, así que en Android no suenan. Esta rama trae su +propio envoltorio, que sí los pide, y enchufa el audio y la cámara del móvil en los +puntos que el propio Oasis deja para eso. La rama no tiene llamadas propias. + +En la barra de arriba, junto al avatar, está el botón de *No molestar* del Teléfono. --- @@ -109,20 +117,42 @@ compañía). La rama no tiene llamadas propias. ![La interfaz por dentro](docs/capturas/esquema-interfaz.png)

- - - + + + + +

+

+ + + +

-Casi sesenta módulos no caben en la pantalla de un teléfono: la portada los ordena por -secciones en una rejilla de iconos, como el cajón de apps del móvil. Abajo -queda una barra de cuatro atajos que eliges tú. Dentro de cada módulo los filtros van en fichas pequeñas, -para que el contenido se vea sin tener que bajar. +Lanzador, tribus, una votación de semáforo, módulos, ajustes, la barra de atajos, las +salas del dev y el mismo lanzador con el tema OasisMobile del dev. -Solo aparecen los módulos activos. En **Ajustes → Workflows** se eligen de golpe: el de -**acampada** deja lo justo para organizarse (chats, teléfono, salas, pads, foro, wiki, -tribus, parlamento, votaciones y eventos). En el móvil un workflow cambia los módulos, -no el aspecto. +Casi sesenta módulos no caben en la pantalla de un teléfono: la portada los ordena por +secciones en una rejilla de iconos, como el cajón de apps del móvil. Abajo queda una +barra de cuatro atajos que eliges tú. Dentro de cada módulo los filtros van en fichas +pequeñas, para que el contenido se vea sin tener que bajar. + +Solo aparecen los módulos activos. En *Ajustes → Workflows* se eligen de golpe: el de +acampada deja lo justo para organizarse. En el móvil un workflow cambia los módulos, +no el aspecto: el aspecto se elige en *Ajustes → Tema* (Acampada u OasisMobile). + +--- + +## Cómo está hecha la rama + +![Cómo va la rama encima de Oasis](docs/capturas/esquema-rama.png) + +Oasis lo desarrolla epsylon y saca versión casi cada día. Para no tener que rehacer la +rama en cada una, lo nuestro va en ficheros propios: hojas de estilo, unas pocas +vistas, un router que va delante del suyo y el envoltorio de Android. El backend, el +middleware, los modelos y las traducciones son suyos y no se tocan. Las normas están +en la [guía de desarrollo](GUIA_DE_DESARROLLO.md) y las cifras en +[docs/FRONTERA_UPSTREAM.md](docs/FRONTERA_UPSTREAM.md). --- @@ -132,28 +162,28 @@ no el aspecto. | | | |---|---| -| **[oasis-project.pub](https://oasis-project.pub)** | Directorio de pubs, con invitaciones y estado de cada uno | -| **[0asis.net](https://0asis.net)** | El proyecto explicado desde el principio | -| **[wiki.solarnethub.com](https://wiki.solarnethub.com/socialnet/overview)** | La documentación oficial, módulo por módulo | +| [oasis-project.pub](https://oasis-project.pub) | Directorio de pubs, con invitaciones y estado de cada uno | +| [0asis.net](https://0asis.net) | El proyecto explicado desde el principio | +| [wiki.solarnethub.com](https://wiki.solarnethub.com/socialnet/overview) | La documentación oficial, módulo por módulo | --- ## Instalar -La APK **no está en ninguna tienda**: se descarga de las *releases* de este repositorio -y se instala a mano, con orígenes desconocidos activado. Se instala **junto al Oasis -oficial** sin pisarlo —el identificador es `net.laenre.oasis`—, así que puedes tener +La APK no está en ninguna tienda: se descarga de las *releases* de este repositorio +y se instala a mano, con orígenes desconocidos activado. Se instala junto al Oasis +oficial sin pisarlo —el identificador es `net.laenre.oasis`—, así que puedes tener los dos y comparar. -Necesita unos **350 MB libres**. El primer arranque tarda entre 15 y 60 segundos porque +Necesita unos 350 MB libres. El primer arranque tarda entre 15 y 60 segundos porque descomprime el servidor. -> **Verifica la huella de la firma** antes de instalar una APK bajada de un servidor +> Verifica la huella de la firma antes de instalar una APK bajada de un servidor > propio: `apksigner verify --print-certs` ### Compilar -**Con Android Studio**, lo más fácil: +Con Android Studio, lo más fácil: ```sh ./android/scripts/android-studio.sh # trae el Oasis oficial y su APK, y empaqueta el backend @@ -163,7 +193,7 @@ Después, en Android Studio: *File → Open →* la carpeta `android/`, esperar Sync, y *Build → Build App Bundle(s) / APK(s) → Build APK(s)*. Si cambias algo en `src/` o en `main.js`, vuelve a lanzar el script antes de compilar. -**Desde la terminal**, paso a paso: +Desde la terminal, paso a paso: ```sh cd android @@ -187,13 +217,13 @@ Para desarrollar, en el propio repositorio: | [docs/USABILIDAD_PAD.txt](docs/USABILIDAD_PAD.txt) | Pruebas de usabilidad en el móvil | Todo lo de arriba, en detalle, está en la -**[wiki](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki)**: +[wiki](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki): | | | |---|---| | [Entender Oasis](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Entender) | La red, el gossip, los saltos, los pubs, las tribus y el LARP | | [Empezar desde cero](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Empezar) | Invitación, seguir gente, abrir tu blog o tu tienda | -| [Llamadas](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Llamadas) | Cómo se llama y cómo montar un TURN | +| [Llamadas](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Llamadas) | Teléfono y Salas en el móvil | | [La interfaz](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Interfaz) | Botón por botón | | [Identidades](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Identidades) | Varias cuentas en el mismo móvil | | [Compilar la APK](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/Compilar) | Y cómo se publica una versión | @@ -205,12 +235,12 @@ Todo lo de arriba, en detalle, está en la ## El proyecto original -Esta rama existe **gracias a** y **por encima de** el trabajo de epsylon. El código, la +Esta rama existe gracias a y por encima de el trabajo de epsylon. El código, la arquitectura, los módulos y la red son suyos. -- **Código:** https://github.com/epsylon/oasis -- **Proyecto:** https://solarnethub.com -- **Wiki:** https://wiki.solarnethub.com/socialnet/overview +- Código: https://github.com/epsylon/oasis +- Proyecto: https://solarnethub.com +- Wiki: https://wiki.solarnethub.com/socialnet/overview ## Licencia diff --git a/docs/capturas/00-portada.png b/docs/capturas/00-portada.png index 23118e0..2b4a052 100644 Binary files a/docs/capturas/00-portada.png and b/docs/capturas/00-portada.png differ diff --git a/docs/capturas/01-portada.png b/docs/capturas/01-portada.png index 4e31078..8f6e7d2 100644 Binary files a/docs/capturas/01-portada.png and b/docs/capturas/01-portada.png differ diff --git a/docs/capturas/02-chats.png b/docs/capturas/02-chats.png deleted file mode 100644 index 4c68f10..0000000 Binary files a/docs/capturas/02-chats.png and /dev/null differ diff --git a/docs/capturas/02-tribus.png b/docs/capturas/02-tribus.png new file mode 100644 index 0000000..43874dc Binary files /dev/null and b/docs/capturas/02-tribus.png differ diff --git a/docs/capturas/03-karvan.png b/docs/capturas/03-karvan.png deleted file mode 100644 index 91e9bfd..0000000 Binary files a/docs/capturas/03-karvan.png and /dev/null differ diff --git a/docs/capturas/03-votacion.png b/docs/capturas/03-votacion.png new file mode 100644 index 0000000..cea3076 Binary files /dev/null and b/docs/capturas/03-votacion.png differ diff --git a/docs/capturas/04-foros.png b/docs/capturas/04-foros.png deleted file mode 100644 index c20a66f..0000000 Binary files a/docs/capturas/04-foros.png and /dev/null differ diff --git a/docs/capturas/04-modulos.png b/docs/capturas/04-modulos.png new file mode 100644 index 0000000..a7cb348 Binary files /dev/null and b/docs/capturas/04-modulos.png differ diff --git a/docs/capturas/05-ajustes.png b/docs/capturas/05-ajustes.png new file mode 100644 index 0000000..3a2278f Binary files /dev/null and b/docs/capturas/05-ajustes.png differ diff --git a/docs/capturas/05-feed.png b/docs/capturas/05-feed.png deleted file mode 100644 index 845bee9..0000000 Binary files a/docs/capturas/05-feed.png and /dev/null differ diff --git a/docs/capturas/06-barra.png b/docs/capturas/06-barra.png index 245ce11..d65b309 100644 Binary files a/docs/capturas/06-barra.png and b/docs/capturas/06-barra.png differ diff --git a/docs/capturas/07-karvan-salas.png b/docs/capturas/07-karvan-salas.png deleted file mode 100644 index 09501c5..0000000 Binary files a/docs/capturas/07-karvan-salas.png and /dev/null differ diff --git a/docs/capturas/07-oasismobile.png b/docs/capturas/07-oasismobile.png new file mode 100644 index 0000000..383609a Binary files /dev/null and b/docs/capturas/07-oasismobile.png differ diff --git a/docs/capturas/08-salas.png b/docs/capturas/08-salas.png new file mode 100644 index 0000000..f309c89 Binary files /dev/null and b/docs/capturas/08-salas.png differ diff --git a/docs/capturas/esquema-interfaz.png b/docs/capturas/esquema-interfaz.png index 0f6d51e..7813bd9 100644 Binary files a/docs/capturas/esquema-interfaz.png and b/docs/capturas/esquema-interfaz.png differ diff --git a/docs/capturas/esquema-llamadas.png b/docs/capturas/esquema-llamadas.png new file mode 100644 index 0000000..273318a Binary files /dev/null and b/docs/capturas/esquema-llamadas.png differ diff --git a/docs/capturas/esquema-rama.png b/docs/capturas/esquema-rama.png new file mode 100644 index 0000000..da69c17 Binary files /dev/null and b/docs/capturas/esquema-rama.png differ diff --git a/docs/capturas/generar_portada.py b/docs/capturas/generar_portada.py index cf72516..716568b 100644 --- a/docs/capturas/generar_portada.py +++ b/docs/capturas/generar_portada.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 -"""Portada y esquema de la interfaz del README, con la paleta del tema Acampada. +"""Portada y esquemas del README, con la paleta del tema Acampada. -Las capturas de movil (01..07) se hacen aparte, con la app arrancada; este script -solo compone 00-portada.png a partir de ellas y dibuja esquema-interfaz.png. +Las capturas de movil (01..08) se hacen aparte, con la app arrancada en un nodo de +pruebas a 412x915 y escala 2; este script compone 00-portada.png a partir de ellas y +dibuja esquema-interfaz.png, esquema-rama.png y esquema-llamadas.png. El texto vive aqui: se cambia y se relanza, nunca se retocan los PNG a mano. python3 docs/capturas/generar_portada.py @@ -47,8 +48,8 @@ def portada(): d.rectangle((80, bottom + 12, 420, bottom + 20), fill=FG) d.text((80, 196), "Rama PRUEBAS del hacklab · base Oasis 1.3.3 · tema Acampada, pensado para el pulgar", font=font(SANS, 27), fill=MUTED) - shots = [("01-portada.png", "Lanzador de módulos"), ("03-karvan.png", "Karvan: salas efímeras"), - ("04-foros.png", "Filtros en fichas"), ("06-barra.png", "Tu barra de 4 atajos")] + shots = [("01-portada.png", "Lanzador de módulos"), ("02-tribus.png", "Tribus en filas"), + ("03-votacion.png", "Votar en semáforo"), ("07-oasismobile.png", "O el tema del dev")] pw, gap, top = 320, 50, 270 x = (W - (4 * (pw + 20) + 3 * gap)) // 2 cap = font(ANTON, 34) @@ -87,8 +88,8 @@ def esquema(): d.rounded_rectangle((px0, py0, px1, py1), 30, outline=FG, width=5) d.rounded_rectangle((px0 + 16, py0 + 20, px1 - 16, py0 + 84), 14, fill=BG, outline=FG, width=3) s = font(SANS, 15) - d.text((px0 + 46, py0 + 42), "logo · (Personal | Social) · avatar", font=s, fill=FG) - secciones = [("COMUNIDAD", 8), ("BIBLIOTECA", 2), ("HERRAMIENTAS", 5)] + d.text((px0 + 46, py0 + 42), "logo · no molestar · avatar", font=s, fill=FG) + secciones = [("COMUNIDAD", 8), ("BIBLIOTECA", 2), ("GOBERNANZA", 2)] y = py0 + 110 for nombre, n in secciones: d.text((px0 + 40, y), nombre, font=font(ANTON, 17), fill=FG) @@ -108,12 +109,12 @@ def esquema(): cajas = [ ((bx0, 190, bx1, 340), "ARRIBA — LA BARRA SUPERIOR", ["El logo vuelve al inicio y el avatar abre tu perfil.", - "Con muchos módulos activos salen los filtros Personal y Social;", - "en modo activistas todo cabe en una pantalla y no hacen falta."], BG), + "En medio, el botón de No molestar del Teléfono.", + "Con muchos módulos activos salen también los filtros Personal y Social."], BG), ((bx0, 375, bx1, 565), "EN MEDIO — EL LANZADOR", ["Los módulos activos en una rejilla de iconos, por secciones,", "como el cajón de apps del móvil. Los nombres son los del Oasis", - "oficial: Comunidad, Biblioteca, Herramientas…", + "oficial: Comunidad, Biblioteca, Gobernanza…", "Solo en la pantalla de inicio; dentro de cada módulo, filtros en fichas."], BG), ((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS", ["Cuatro módulos a tu elección: ni uno más, que es para el pulgar.", @@ -132,6 +133,79 @@ def esquema(): out.save(HERE / "esquema-interfaz.png", optimize=True) +def flecha(d, a, b, ancho=4): + ang = math.atan2(b[1] - a[1], b[0] - a[0]) + tip = lambda da, r: (b[0] - r * math.cos(ang + da), b[1] - r * math.sin(ang + da)) + d.line((a, tip(0, 12)), fill=FG, width=ancho) + d.polygon([b, tip(0.45, 20), tip(-0.45, 20)], fill=FG) + + +def titulo(d, texto, sub): + d.text((46, 26), texto, font=font(ANTON, 46), fill=FG) + bottom = d.textbbox((46, 26), texto, font=font(ANTON, 46))[3] + d.rectangle((46, bottom + 10, 260, bottom + 16), fill=FG) + d.text((46, bottom + 30), sub, font=font(SANS, 18), fill=MUTED) + + +def esquema_rama(): + """Como va la rama encima de Oasis: lo del dev abajo, lo nuestro encima.""" + W, H = 1400, 760 + out = Image.new("RGB", (W, H), BG) + d = ImageDraw.Draw(out) + titulo(d, "CÓMO VA LA RAMA ENCIMA DE OASIS", + "Oasis lo cambia el dev, casi una versión al día. La rama solo cambia cómo se ve y se usa en el móvil.") + capa = [ + ((60, 210, 380, 400), "ASPECTO", + ["mobile_base.css: la maqueta", "mobile_ux.css: márgenes, letra,", "botones y piezas nuevas", + "Acampada.css: el skin"], NEON), + ((400, 210, 720, 400), "PANTALLAS", + ["mobile_ui.js y las vistas", "mobile_*.js. En una vista", "del dev, como mucho una", "línea que las llama"], NEON), + ((740, 210, 1060, 400), "RUTAS", + ["fork_routes.js y", "mobile_layer.js, delante", "del router del dev: lo que", "no atienden pasa al suyo"], NEON), + ((1080, 210, 1340, 400), "ANDROID", + ["envoltorio propio:", "permisos de micro y", "cámara, y el puente", "de audio del Teléfono"], NEON), + ] + for box, t, l, r in capa: + caja(d, box, t, l, r) + d.text((60, 166), "LO DE LA RAMA", font=font(ANTON, 26), fill=FG) + for x in (220, 560, 900, 1210): + flecha(d, (x, 418), (x, 478)) + caja(d, (60, 500, 1340, 690), "OASIS DEL DEV: NO SE TOCA", + ["backend.js y su middleware · los modelos (lo que se publica y cómo se cuenta) · SSB y su arranque", + "las traducciones · style.css y mobile.css · los temas, también OasisMobile.css", + "De 6.400 líneas cambiadas en 43 ficheros suyos (octubre) a 1.045 en 40. Ver GUIA_DE_DESARROLLO.md"], BG) + out.save(HERE / "esquema-rama.png", optimize=True) + + +def esquema_llamadas(): + """Las llamadas son las del dev; la rama solo enchufa el movil.""" + W, H = 1400, 700 + out = Image.new("RGB", (W, H), BG) + d = ImageDraw.Draw(out) + titulo(d, "LLAMADAS: LAS DEL DEV, CON EL MÓVIL ENCHUFADO", + "Teléfono y Salas son de Oasis. La rama no tiene llamadas propias: solo hace que suenen en Android.") + cajas = [ + ((50, 200, 360, 430), "TELÉFONO Y SALAS", + ["De Oasis, tal cual.", "Llamadas de uno a uno", "con buzón de voz; salas", "de hasta 50, con vídeo,", "grabación y emisión."], BG), + ((400, 200, 700, 430), "LOS ENCHUFES", + ["setAudioFactory", "setVideoFactory", "setStreamFactory", "", "Los deja el propio dev."], BG), + ((740, 200, 1040, 430), "EL PUENTE", + ["main.js enchufa ahí", "phone_*_android.js:", "el audio y la cámara", "pasan por la app de", "Android."], NEON), + ((1080, 200, 1350, 430), "EL MÓVIL", + ["Micrófono, cámara", "y altavoz. Los permisos", "se piden la primera vez", "que llamas o entras", "en una sala."], NEON), + ] + for box, t, l, r in cajas: + caja(d, box, t, l, r) + for a, b in (((362, 315), (398, 315)), ((702, 315), (738, 315)), ((1042, 315), (1078, 315))): + flecha(d, a, b) + caja(d, (50, 480, 1350, 630), "¿Y CON LA APK OFICIAL?", + ["No declara micrófono ni cámara y su navegador interno deniega esas peticiones:", + "en Android las llamadas no pueden sonar. Por eso esta rama trae su propio envoltorio."], BG) + out.save(HERE / "esquema-llamadas.png", optimize=True) + + if __name__ == "__main__": portada() esquema() + esquema_rama() + esquema_llamadas() diff --git a/src/client/assets/styles/mobile_ux.css b/src/client/assets/styles/mobile_ux.css index d56e0d0..a313301 100644 --- a/src/client/assets/styles/mobile_ux.css +++ b/src/client/assets/styles/mobile_ux.css @@ -469,3 +469,40 @@ body .main-content { min-height: calc(100dvh - 300px); } .main-column .search-filters-row > table.search-filters-table { flex: 0 0 auto !important; } .main-column .pm-card div.pm-actions.pm-actions { width: auto !important; max-width: max-content; display: inline-flex !important; vertical-align: top; } .main-column .pm-card .pm-info-table tr:nth-child(1) td, .main-column .pm-card .pm-info-table tr:nth-child(1) a { white-space: nowrap !important; overflow: hidden; text-overflow: ellipsis; min-width: 0; max-width: 100%; text-align: left !important; } + +/* ---------- el interruptor Abierto / No molestar del Telefono del dev ---------- */ +/* salia centrado en una fila propia arriba de cada pagina (unos 60px); va en la + barra de arriba, junto al avatar */ +form.oasis-phone-state { + position: fixed !important; top: calc(env(safe-area-inset-top, 0px) + 14px); right: 62px; z-index: 1001; + margin: 0 !important; padding: 0 !important; width: auto !important; +} +form.oasis-phone-state .oasis-phone-state-btn { + width: 40px !important; height: 40px !important; min-height: 40px !important; padding: 0 !important; margin: 0 !important; + display: inline-flex !important; align-items: center; justify-content: center; + border-radius: 50% !important; box-shadow: none !important; font-size: 20px !important; +} +.oasis-mobile-topbar .omt-menus { padding-right: 46px; } + +/* barras de la votacion: juntas, y las etiquetas del dev (CONFUNDIDO, SEGUIR + MAYORIA) en el mismo tipo que las nuestras (Si, No) */ +.main-column div.vote-bar { margin: 0 !important; padding: 0 !important; min-height: 0 !important; gap: 4px 10px !important; } +.main-column .vote-bar > * { margin: 0 !important; } +.main-column .vote-bar-meter { height: 12px !important; min-height: 0 !important; } +.main-column .vote-bar-label { text-transform: lowercase; } +.main-column .vote-bar-label::first-letter { text-transform: uppercase; } + +/* personalizar la barra de abajo: una fila por atajo, como el resto de listas */ +.main-column .bb-manage-list, .main-column .bb-pick-grid { display: flex !important; flex-direction: column !important; gap: 8px !important; align-items: stretch !important; } +.main-column .bb-manage-list form, .main-column .bb-pick-grid form { margin: 0 !important; width: 100% !important; } +.main-column button.bb-manage-btn, .main-column button.bb-pick-btn { + width: 100% !important; min-height: 52px !important; margin: 0 !important; padding: 8px 14px !important; + display: flex !important; align-items: center; gap: 12px; justify-content: flex-start !important; + font-family: var(--ux-text) !important; font-weight: 700 !important; font-size: 16px !important; + text-transform: none !important; letter-spacing: 0 !important; box-shadow: none !important; +} +.main-column .bb-manage-btn .bb-manage-lbl, .main-column .bb-pick-btn .bb-pick-lbl { flex: 1 1 auto; text-align: left; } +.main-column .bb-manage-btn .bb-manage-x { color: var(--ux-danger) !important; font-size: 18px; } +.main-column .bb-manage h3, .main-column .bb-manage-add h3 { font-size: 20px !important; margin: 18px 0 10px !important; } +.main-column a.bb-manage-done { display: block !important; width: 100% !important; margin-top: 18px !important; text-align: center; } +.main-column div.bb-manage, .main-column div.bb-manage-add, .main-column div.bb-manage-list, .main-column div.bb-pick-grid { padding: 0 !important; margin: 0 !important; border: 0 !important; } diff --git a/src/views/mobile_ui.js b/src/views/mobile_ui.js index dded4a2..4b2df21 100644 --- a/src/views/mobile_ui.js +++ b/src/views/mobile_ui.js @@ -119,9 +119,14 @@ const settingsOrder = (parts) => { // cada idioma un boton que se aplica al tocarlo (antes desplegable y otro boton) const LANGS = [['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']]; -const languagePick = (selected) => form({ action: '/language', method: 'post', class: 'lang-pick' }, - ...LANGS.map(([name, code]) => button({ type: 'submit', name: 'language', value: code, class: code === selected ? 'lang-chip is-current' : 'lang-chip', lang: code }, name)) -); +// el idioma se lee al pintar: settings_view desestructura selectedLanguage al cargar y +// se queda con el valor de entonces (siempre "en") +const languagePick = () => { + const selected = require('./main_views').getLanguage(); + return form({ action: '/language', method: 'post', class: 'lang-pick' }, + ...LANGS.map(([name, code]) => button({ type: 'submit', name: 'language', value: code, class: code === selected ? 'lang-chip is-current' : 'lang-chip', lang: code }, name)) + ); +}; // ---------- acciones de tarjeta ---------- diff --git a/src/views/settings_view.js b/src/views/settings_view.js index 78ec925..1bc0f73 100644 --- a/src/views/settings_view.js +++ b/src/views/settings_view.js @@ -148,7 +148,7 @@ const settingsView = (params) => { div({ class: "tags-header" }, h2(i18n.language), p(i18n.languageDescription), - isMobile ? mobileUi.languagePick(selectedLanguage) : form( + isMobile ? mobileUi.languagePick() : form( { action: "/language", method: "post" }, select({ name: "language" }, [ languageOption("English", "en"),