readme: capturas y dibujos nuevos, sin Karvan

- capturas de un nodo de prueba con contenido de acampada: lanzador, tribus,
  votacion de semaforo, modulos, ajustes, barra, salas y el tema del dev
- portada y esquema de la interfaz al dia; dos esquemas nuevos: como va la
  rama encima de Oasis y como funcionan las llamadas
- el README cuenta las novedades de esta version y explica la rama

Arreglos que salieron al hacer las capturas:
- el boton de No molestar del Telefono ocupaba una fila entera arriba de cada
  pagina; ahora va en la barra de arriba, junto al avatar
- Ajustes marcaba English aunque el idioma fuera otro (settings_view se queda
  con el idioma del arranque): el selector lo lee al pintar
- barras de la votacion juntas y con las etiquetas en el mismo tipo
- Personalizar la barra: una fila por atajo, sin sangria
This commit is contained in:
hacklab 2026-10-19 13:49:21 +02:00
parent d4e6b6c8e8
commit e6ea3f4b58
22 changed files with 221 additions and 75 deletions

152
README.md
View file

@ -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)
<p align="center">
<img src="docs/capturas/01-portada.png" width="240">
<img src="docs/capturas/04-foros.png" width="240">
<img src="docs/capturas/06-barra.png" width="240">
<img src="docs/capturas/01-portada.png" width="200">
<img src="docs/capturas/02-tribus.png" width="200">
<img src="docs/capturas/03-votacion.png" width="200">
<img src="docs/capturas/04-modulos.png" width="200">
</p>
<p align="center">
<img src="docs/capturas/05-ajustes.png" width="200">
<img src="docs/capturas/06-barra.png" width="200">
<img src="docs/capturas/08-salas.png" width="200">
<img src="docs/capturas/07-oasismobile.png" width="200">
</p>
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 280 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

BIN
docs/capturas/02-tribus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
docs/capturas/08-salas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View file

@ -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()

View file

@ -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; }

View file

@ -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' },
// 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 ----------

View file

@ -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"),