feat(touch): a list users can add regions to (#271)

The other half of the request. The previous commit made a scoped message
resolvable to a region NAME; this is the list that decides which names are on
offer. Until now the registry only filled itself from our own default region and
per-channel scope overrides, so a region you can see traffic from but do not
participate in could never be named -- which is most of them.

Settings -> Radio, under the region-scope field, opens "Known regions": a field
plus Add, and a row per region with Remove. Deliberately placed next to the
region we SEND under, because the distinction is the confusing part -- that field
is the region we transmit in, this list is the regions we can RECOGNISE, and a
region only has to be named to be recognised, since its key is SHA256 of that
name. You do not need to be in a region to identify its traffic.

Remove is a RETIRE, not a delete. The slot and name stay bound; the region simply
stops being matched from that point on. Freeing the slot for reuse would hand it
to the next region added and silently relabel every message already received
under the old one -- the exact failure #271 warned about, and the reason slots
were never list indices. Re-adding a retired region revives its original slot, so
its old and new messages stay one region rather than splitting in two.

The persisted slot file gains an active mask and a magic bump (RGS1 -> RGS2). An
RGS1 file still loads, with every assigned slot treated as live, which is what it
meant before the mask existed.

Two failures are reported rather than silently doing nothing: the list being full
(all 14 slot bindings used, retired ones included -- they are permanent), and a
name that canonicalises to nothing, like blank or a bare "#".

Not board-gated -- the page uses the standard app-page chrome, so it inherits the
tall title bar, the back chevron and focus-group navigation on the keyboard-only
boards the same way Blocked users does. Laid out and reasoned about on the T-Deck
for now; the short-panel boards want a look on real glass before anyone calls it
done there. The page title is untranslated, matching every other app page here
(Blocked users, Spectrum, Discover).

All 8 S3 envs build. Four new strings in 13 .lang files, English both sides per
4709c81; the audit's 3 remaining gaps are pre-existing on main from the beta_68
SDK work. Not hardware-tested: adding a region is verifiable on-device, but
whether a message then resolves to it needs real scoped traffic from a second
node.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Michael A. CojocariandClaude Opus 5 committed 2026-08-21 19:16:30 -04:00
1 parent afaebaacc8
commit dee32e4c2c
15 files changed
+273 -9

No files matched your search

+4
View File
@@ -282,6 +282,7 @@ End time Краен час
Enter SSID first Първо въведете SSID
Enter a name Въведете име
Enter a network name Въведете име на мрежа
Enter a region name Enter a region name
Enter adds a new line Enter добавя нов ред
Enter exactly 32 hex characters. Въведете точно 32 hex знака.
Enter key sends message Клавишът Enter изпраща съобщението
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Клавиатурна навиг.: WASDZ вкл.
Keyboard nav: off Клавиатурна навиг.: изкл.
Keyboard navigation Навигация с клавиатура
Keys
Known regions Known regions
LOS → %s Пряка видимост → %s
Landscape (desktop) Пейзаж (десктоп)
Landscape set - applies when remote mode starts. Пейзажът е зададен - прилага се при стартиране на отдалечения режим.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Няма намерени мрежи (само
No onboard GPS Няма вграден GPS
No other networks found Не са открити други мрежи
No position yet. A cold start needs a clear view of the sky and can take several minutes. Още няма позиция. Студен старт изисква чисто небе и може да отнеме няколко минути.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Няма отговор от %s
No reply. Check password, server,\nor device clock (device: %s) Няма отговор. Проверете паролата, сървъра\nили часовника на устройството (устройство: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Тук няма запазени плочки.\n\nСвържете се с Wi-Fi (Настройки → Wi-Fi),\nза да изтеглите този район.\nЗапазените плочки остават достъпни офлайн.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Наскоро чути
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Форматиране на SD като MESHCOMOD (FAT32)?\nВСИЧКИ данни на картата ще бъдат изтрити.
Region & scope Регион и обхват
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Регионален обхват (#tag) за този канал.\nПразно = обхват по подразбиране.
Region scope (#tag), blank = default. Обхват на регион (#tag), празно = по подразбиране.
Region scope (#tag, blank = none) Регионален обхват (#tag, празно = няма)
+4
View File
@@ -282,6 +282,7 @@ End time Endzeit
Enter SSID first Zuerst SSID eingeben
Enter a name Namen eingeben
Enter a network name Netzwerknamen eingeben
Enter a region name Enter a region name
Enter adds a new line Enter fügt neue Zeile ein
Enter exactly 32 hex characters. Genau 32 Hex-Zeichen eingeben.
Enter key sends message Enter-Taste sendet Nachricht
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Tastaturnav.: WASDZ an
Keyboard nav: off Tastaturnav.: aus
Keyboard navigation Tastaturnavigation
Keys Tasten
Known regions Known regions
LOS → %s Sichtlinie → %s
Landscape (desktop) Querformat (Desktop)
Landscape set - applies when remote mode starts. Querformat gesetzt - gilt beim Start des Fernmodus.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Keine Netzwerke gefunden (nur 2,4 GHz)
No onboard GPS Kein integriertes GPS
No other networks found Keine anderen Netzwerke gefunden
No position yet. A cold start needs a clear view of the sky and can take several minutes. Noch keine Position. Ein Kaltstart braucht freie Sicht zum Himmel und kann mehrere Minuten dauern.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Keine Antwort von %s
No reply. Check password, server,\nor device clock (device: %s) Keine Antwort. Prüfe Passwort, Server\noder Geräteuhr (Gerät: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Hier keine gespeicherten Kacheln.\n\nMit Wi-Fi verbinden (Einstellungen → Wi-Fi),\num dieses Gebiet zu laden.\nGespeicherte Kacheln bleiben offline verfügbar.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Kürzlich gehört
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. SD als MESHCOMOD (FAT32) neu formatieren?\nALLE Daten auf der Karte werden gelöscht.
Region & scope Region & Reichweite
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Regionsbereich (#tag) für diesen Kanal.\nLeer = Standardbereich verwenden.
Region scope (#tag), blank = default. Regionsbereich (#tag), leer = Standard.
Region scope (#tag, blank = none) Regionsbereich (#tag, leer = keiner)
+4
View File
@@ -282,6 +282,7 @@ End time Ώρα λήξης
Enter SSID first Εισαγάγετε πρώτα το SSID
Enter a name Εισαγάγετε όνομα
Enter a network name Εισαγάγετε όνομα δικτύου
Enter a region name Enter a region name
Enter adds a new line Το Enter προσθέτει γραμμή
Enter exactly 32 hex characters. Εισαγάγετε ακριβώς 32 hex χαρακτήρες.
Enter key sends message Το πλήκτρο Enter στέλνει το μήνυμα
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Πλοήγηση πληκτρ.: WASDZ ενεργό
Keyboard nav: off Πλοήγηση πληκτρ.: κλειστό
Keyboard navigation Πλοήγηση με πληκτρολόγιο
Keys
Known regions Known regions
LOS → %s Οπτική επαφή → %s
Landscape (desktop) Οριζόντια (desktop)
Landscape set - applies when remote mode starts. Ορίστηκε οριζόντια - ισχύει όταν ξεκινήσει η απομακρυσμένη λειτουργία.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Δεν βρέθηκαν δίκτυα (μόνο
No onboard GPS Χωρίς ενσωματωμένο GPS
No other networks found Δεν βρέθηκαν άλλα δίκτυα
No position yet. A cold start needs a clear view of the sky and can take several minutes. Δεν υπάρχει ακόμη θέση. Η ψυχρή εκκίνηση θέλει ανοιχτό ουρανό και μπορεί να πάρει λεπτά.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Καμία απάντηση από %s
No reply. Check password, server,\nor device clock (device: %s) Καμία απάντηση. Ελέγξτε κωδικό, διακομιστή\nή ρολόι συσκευής (συσκευή: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Δεν υπάρχουν αποθηκευμένα πλακίδια εδώ.\n\nΣυνδεθείτε στο Wi-Fi (Ρυθμίσεις → Wi-Fi)\nγια λήψη αυτής της περιοχής.\nΤα αποθηκευμένα πλακίδια μένουν διαθέσιμα εκτός σύνδεσης.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Ακούστηκε πρόσφατα
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Επαναδιαμόρφωση SD ως MESHCOMOD (FAT32);\nΟΛΑ τα δεδομένα στην κάρτα θα διαγραφούν.
Region & scope Περιοχή & εμβέλεια
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Εύρος περιοχής (#tag) για αυτό το κανάλι.\nΚενό = προεπιλεγμένο εύρος.
Region scope (#tag), blank = default. Εύρος περιοχής (#tag), κενό = προεπιλογή.
Region scope (#tag, blank = none) Εύρος περιοχής (#tag, κενό = κανένα)
+4
View File
@@ -282,6 +282,7 @@ End time Hora de fin
Enter SSID first Introduce primero el SSID
Enter a name Introduce un nombre
Enter a network name Introduce un nombre de red
Enter a region name Enter a region name
Enter adds a new line Enter añade una línea
Enter exactly 32 hex characters. Introduce exactamente 32 caracteres hex.
Enter key sends message La tecla Enter envía el mensaje
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Nav. teclado: WASDZ act.
Keyboard nav: off Nav. teclado: desact.
Keyboard navigation Navegación por teclado
Keys
Known regions Known regions
LOS → %s Línea de vista → %s
Landscape (desktop) Horizontal (escritorio)
Landscape set - applies when remote mode starts. Horizontal fijado - se aplica al iniciar el modo remoto.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) No se encontraron redes (solo 2.4 GHz)
No onboard GPS Sin GPS integrado
No other networks found No se encontraron otras redes
No position yet. A cold start needs a clear view of the sky and can take several minutes. Aún sin posición. Un arranque en frío necesita cielo despejado y puede tardar varios minutos.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Sin respuesta de %s
No reply. Check password, server,\nor device clock (device: %s) Sin respuesta. Revisa contraseña, servidor\no reloj del dispositivo (dispositivo: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. No hay teselas guardadas aquí.\n\nConéctate a Wi-Fi (Ajustes → Wi-Fi)\npara descargar esta zona.\nLas teselas guardadas quedan disponibles sin conexión.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Escuchado hace poco
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. ¿Reformatear la SD como MESHCOMOD (FAT32)?\nSe borrarán TODOS los datos de la tarjeta.
Region & scope Región y alcance
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Alcance de región (#tag) para este canal.\nVacío = alcance predeterminado.
Region scope (#tag), blank = default. Ámbito de región (#tag), vacío = predeterminado.
Region scope (#tag, blank = none) Alcance de región (#tag, vacío = ninguno)
+4
View File
@@ -282,6 +282,7 @@ End time Heure de fin
Enter SSID first Saisissez d'abord le SSID
Enter a name Saisissez un nom
Enter a network name Saisir un nom de réseau
Enter a region name Enter a region name
Enter adds a new line Entrée ajoute une ligne
Enter exactly 32 hex characters. Saisissez exactement 32 caractères hex.
Enter key sends message La touche Entrée envoie le message
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Nav. clavier : WASDZ activé
Keyboard nav: off Nav. clavier : désactivé
Keyboard navigation Navigation clavier
Keys
Known regions Known regions
LOS → %s Vue directe → %s
Landscape (desktop) Paysage (bureau)
Landscape set - applies when remote mode starts. Paysage défini - s'applique au démarrage du mode distant.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Aucun réseau trouvé (2,4 GHz uniquement)
No onboard GPS Pas de GPS intégré
No other networks found Aucun autre réseau trouvé
No position yet. A cold start needs a clear view of the sky and can take several minutes. Pas encore de position. Un démarrage à froid demande un ciel dégagé et peut prendre plusieurs minutes.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Pas de réponse de %s
No reply. Check password, server,\nor device clock (device: %s) Pas de réponse. Vérifiez mot de passe, serveur\nou horloge de l'appareil (appareil : %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Aucune tuile enregistrée ici.\n\nConnectez-vous au Wi-Fi (Réglages → Wi-Fi)\npour télécharger cette zone.\nLes tuiles enregistrées restent hors ligne.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Entendu récemment
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Reformater la SD en MESHCOMOD (FAT32) ?\nTOUTES les données de la carte seront effacées.
Region & scope Région et portée
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Portée régionale (#tag) pour ce canal.\nVide = portée par défaut.
Region scope (#tag), blank = default. Portée de région (#tag), vide = défaut.
Region scope (#tag, blank = none) Portée régionale (#tag, vide = aucune)
+4
View File
@@ -286,6 +286,7 @@ End time Befejezés
Enter SSID first Először adja meg az SSID-t
Enter a name Írjon be egy nevet
Enter a network name Adjon meg egy hálózatnevet
Enter a region name Enter a region name
Enter adds a new line Az Enter új sort ad hozzá
Enter exactly 32 hex characters. Pontosan 32 hex karaktert adjon meg.
Enter key sends message Az Enter billentyű elküldi az üzenetet
@@ -412,6 +413,7 @@ Keyboard nav: WASDZ on Billentyűzet navigáció: WASDZ bekapcsolva
Keyboard nav: off Billentyűzetnavigáció: kikapcsolva
Keyboard navigation Billentyűzetnavigáció
Keys Kulcsok
Known regions Known regions
LOS → %s Rálátás → %s
Landscape (desktop) Fekvő (asztali)
Landscape set - applies when remote mode starts. Fekvő beállítva - a távoli mód indulásakor lép életbe.
@@ -539,6 +541,7 @@ No networks found (2.4 GHz only) Nem található hálózat (csak 2,4 GHz)
No onboard GPS Nincs beépített GPS
No other networks found Nem található más hálózat
No position yet. A cold start needs a clear view of the sky and can take several minutes. Még nincs pozíció. A hidegindításhoz szabad égbolt kell, és több percig is tarthat.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Nincs válasz %s-tól
No reply. Check password, server,\nor device clock (device: %s) Nincs válasz. Ellenőrizze a jelszót, a szervert,\nvagy az eszköz óráját (eszköz: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Nincsenek mentett térképszeletek. \n\nCsatlakozzon Wi-Fi-hez (Beállítások → Wi-Fi) a terület letöltéséhez.\nA mentett szeletek offline is elérhetők maradnak.
@@ -649,6 +652,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Nemrég hallottam
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Újraformázza az SD kártyát MESHCOMOD (FAT32) formátumban?\nA kártyán lévő ÖSSZES adat törlődik.
Region & scope Régió és hatókör
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Régió hatóköre (#tag) ehhez a csatornához.\nÜres = az alapértelmezett hatókör használata.
Region scope (#tag), blank = default. Régió hatóköre (#tag), üres = alapértelmezett.
Region scope (#tag, blank = none) Régió hatóköre (#címke, üres = nincs)
+4
View File
@@ -282,6 +282,7 @@ End time Ora di fine
Enter SSID first Inserisci prima l'SSID
Enter a name Inserisci un nome
Enter a network name Inserisci un nome di rete
Enter a region name Enter a region name
Enter adds a new line Invio aggiunge una riga
Enter exactly 32 hex characters. Inserisci esattamente 32 caratteri hex.
Enter key sends message Il tasto Invio invia il messaggio
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Nav. tastiera: WASDZ on
Keyboard nav: off Nav. tastiera: off
Keyboard navigation Navigazione da tastiera
Keys
Known regions Known regions
LOS → %s Vista diretta → %s
Landscape (desktop) Orizzontale (desktop)
Landscape set - applies when remote mode starts. Orizzontale impostato - vale all'avvio della modalità remota.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Nessuna rete trovata (solo 2.4 GHz)
No onboard GPS Nessun GPS integrato
No other networks found Nessun'altra rete trovata
No position yet. A cold start needs a clear view of the sky and can take several minutes. Ancora nessuna posizione. Un avvio a freddo richiede cielo libero e può durare diversi minuti.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Nessuna risposta da %s
No reply. Check password, server,\nor device clock (device: %s) Nessuna risposta. Controlla password, server\no orologio del dispositivo (dispositivo: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Nessuna tessera salvata qui.\n\nConnettiti al Wi-Fi (Impostazioni → Wi-Fi)\nper scaricare quest'area.\nLe tessere salvate restano disponibili offline.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Sentito di recente
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Riformattare la SD come MESHCOMOD (FAT32)?\nTUTTI i dati sulla scheda saranno cancellati.
Region & scope Regione e ambito
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Ambito regione (#tag) per questo canale.\nVuoto = ambito predefinito.
Region scope (#tag), blank = default. Ambito regione (#tag), vuoto = predefinito.
Region scope (#tag, blank = none) Ambito regione (#tag, vuoto = nessuno)
+4
View File
@@ -282,6 +282,7 @@ End time Eindtijd
Enter SSID first Voer eerst SSID in
Enter a name Voer een naam in
Enter a network name Voer een netwerknaam in
Enter a region name Enter a region name
Enter adds a new line Enter voegt een nieuwe regel toe
Enter exactly 32 hex characters. Voer precies 32 hex-tekens in.
Enter key sends message Enter verstuurt bericht
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Toetsenbordnav.: WASDZ aan
Keyboard nav: off Toetsenbordnav.: uit
Keyboard navigation Toetsenbordnavigatie
Keys Toetsen
Known regions Known regions
LOS → %s Zichtlijn → %s
Landscape (desktop) Liggend (desktop)
Landscape set - applies when remote mode starts. Liggend ingesteld - geldt zodra de externe modus start.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Geen netwerken gevonden (alleen 2.4 GHz)
No onboard GPS Geen ingebouwde GPS
No other networks found Geen andere netwerken gevonden
No position yet. A cold start needs a clear view of the sky and can take several minutes. Nog geen positie. Een koude start heeft vrij zicht op de lucht nodig en kan enkele minuten duren.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Geen antwoord van %s
No reply. Check password, server,\nor device clock (device: %s) Geen antwoord. Controleer wachtwoord, server,\nof apparaatklok (apparaat: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Hier geen opgeslagen kaarttegels.\n\nMaak verbinding met Wi-Fi (Instellingen → Wi-Fi)\nom dit gebied te downloaden.\nOpgeslagen tegels blijven offline beschikbaar.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Recent gehoord
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. SD herformatteren als MESHCOMOD (FAT32)?\nALLE data op de kaart wordt gewist.
Region & scope Regio en bereik
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Regiobereik (#tag) voor dit kanaal.\nLeeg = standaardbereik gebruiken.
Region scope (#tag), blank = default. Regiobereik (#tag), leeg = standaard.
Region scope (#tag, blank = none) Regiobereik (#tag, leeg = geen)
+4
View File
@@ -282,6 +282,7 @@ End time Hora de término
Enter SSID first Digite o SSID primeiro
Enter a name Digite um nome
Enter a network name Digite um nome de rede
Enter a region name Enter a region name
Enter adds a new line Enter adiciona uma linha
Enter exactly 32 hex characters. Digite exatamente 32 caracteres hex.
Enter key sends message A tecla Enter envia a mensagem
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Nav. teclado: WASDZ ligado
Keyboard nav: off Nav. teclado: desligado
Keyboard navigation Navegação por teclado
Keys
Known regions Known regions
LOS → %s Linha de visada → %s
Landscape (desktop) Paisagem (desktop)
Landscape set - applies when remote mode starts. Paisagem definida - vale quando o modo remoto iniciar.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Nenhuma rede encontrada (só 2.4 GHz)
No onboard GPS Sem GPS integrado
No other networks found Nenhuma outra rede encontrada
No position yet. A cold start needs a clear view of the sky and can take several minutes. Ainda sem posição. Uma partida a frio precisa de céu aberto e pode levar vários minutos.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Sem resposta de %s
No reply. Check password, server,\nor device clock (device: %s) Sem resposta. Verifique senha, servidor\nou relógio do dispositivo (dispositivo: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Sem blocos salvos aqui.\n\nConecte-se ao Wi-Fi (Ajustes → Wi-Fi)\npara baixar esta área.\nOs blocos salvos ficam disponíveis offline.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Ouvido recentemente
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Reformatar o SD como MESHCOMOD (FAT32)?\nTODOS os dados do cartão serão apagados.
Region & scope Região e alcance
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Escopo de região (#tag) para este canal.\nVazio = usar o escopo padrão.
Region scope (#tag), blank = default. Escopo da região (#tag), vazio = padrão.
Region scope (#tag, blank = none) Escopo de região (#tag, vazio = nenhum)
+4
View File
@@ -282,6 +282,7 @@ End time Ora de sfârşit
Enter SSID first Introdu mai întâi SSID-ul
Enter a name Introdu un nume
Enter a network name Introdu un nume de reţea
Enter a region name Enter a region name
Enter adds a new line Enter adaugă un rând nou
Enter exactly 32 hex characters. Introdu exact 32 caractere hex.
Enter key sends message Tasta Enter trimite mesajul
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Navigare tastatură: WASDZ pornit
Keyboard nav: off Navigare tastatură: oprită
Keyboard navigation Navigare cu tastatura
Keys
Known regions Known regions
LOS → %s Linie vizuală → %s
Landscape (desktop) Peisaj (desktop)
Landscape set - applies when remote mode starts. Peisaj setat - se aplică la pornirea modului la distanţă.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Nicio reţea găsită (doar 2,4 GHz)
No onboard GPS Fără GPS integrat
No other networks found Nicio altă reţea găsită
No position yet. A cold start needs a clear view of the sky and can take several minutes. Încă nicio poziţie. O pornire la rece are nevoie de cer liber şi poate dura câteva minute.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Niciun răspuns de la %s
No reply. Check password, server,\nor device clock (device: %s) Niciun răspuns. Verifică parola, serverul\nsau ceasul dispozitivului (dispozitiv: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Fără dale salvate aici.\n\nConectează Wi-Fi (Setări → Wi-Fi)\npentru a descărca zona.\nDalele salvate rămân offline.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Auzite recent
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Reformatezi SD ca MESHCOMOD (FAT32)?\nTOATE datele de pe card vor fi şterse.
Region & scope Regiune şi acoperire
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Domeniu regional (#tag) pentru acest canal.\nGol = domeniul implicit.
Region scope (#tag), blank = default. Domeniu regional (#tag), gol = implicit.
Region scope (#tag, blank = none) Domeniu regional (#tag, gol = niciunul)
+4
View File
@@ -282,6 +282,7 @@ End time Время окончания
Enter SSID first Сначала введите SSID
Enter a name Введите имя
Enter a network name Введите имя сети
Enter a region name Enter a region name
Enter adds a new line Enter добавляет строку
Enter exactly 32 hex characters. Введите ровно 32 hex-символа.
Enter key sends message Клавиша Enter отправляет сообщение
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Навигация клавиатурой: WASDZ вкл.
Keyboard nav: off Навигация клавиатурой: выкл.
Keyboard navigation Навигация клавиатурой
Keys
Known regions Known regions
LOS → %s Прямая видимость → %s
Landscape (desktop) Альбомная (десктоп)
Landscape set - applies when remote mode starts. Альбомная выбрана - применится при запуске удалённого режима.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Сети не найдены (только 2.4
No onboard GPS Нет встроенного GPS
No other networks found Другие сети не найдены
No position yet. A cold start needs a clear view of the sky and can take several minutes. Позиции пока нет. Холодный старт требует открытого неба и может занять несколько минут.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Нет ответа от %s
No reply. Check password, server,\nor device clock (device: %s) Нет ответа. Проверьте пароль, сервер\nили часы устройства (устройство: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Здесь нет сохранённых тайлов.\n\nПодключитесь к Wi-Fi (Настройки → Wi-Fi),\nчтобы загрузить этот район.\nСохранённые тайлы остаются доступны офлайн.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Недавно слышно
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Отформатировать SD как MESHCOMOD (FAT32)?\nВСЕ данные на карте будут стёрты.
Region & scope Регион и охват
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Регион (#tag) для этого канала.\nПусто = область по умолчанию.
Region scope (#tag), blank = default. Область региона (#tag), пусто = по умолчанию.
Region scope (#tag, blank = none) Регион (#tag, пусто = нет)
+4
View File
@@ -282,6 +282,7 @@ End time Време краја
Enter SSID first Прво унесите SSID
Enter a name Унесите име
Enter a network name Унесите име мреже
Enter a region name Enter a region name
Enter adds a new line Enter додаје нови ред
Enter exactly 32 hex characters. Унесите тачно 32 hex знака.
Enter key sends message Тастер Enter шаље поруку
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Навиг. тастатуром: WASDZ укљ.
Keyboard nav: off Навиг. тастатуром: искљ.
Keyboard navigation Навигација тастатуром
Keys
Known regions Known regions
LOS → %s Оптичка видљивост → %s
Landscape (desktop) Положено (десктоп)
Landscape set - applies when remote mode starts. Положено подешено - важи кад крене даљински режим.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Нема мрежа (само 2.4 GHz)
No onboard GPS Нема уграђеног GPS
No other networks found Нема других мрежа
No position yet. A cold start needs a clear view of the sky and can take several minutes. Још нема позиције. Хладан старт тражи чист поглед на небо и може да потраје неколико минута.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Нема одговора од %s
No reply. Check password, server,\nor device clock (device: %s) Нема одговора. Проверите лозинку, сервер\nили сат уређаја (уређај: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Овде нема сачуваних плочица.\n\nПовежите се на Wi-Fi (Подешавања → Wi-Fi)\nда преузмете ову област.\nСачуване плочице остају доступне ван мреже.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Недавно чути
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Форматирати SD као MESHCOMOD (FAT32)?\nСВИ подаци на картици биће обрисани.
Region & scope Регион и опсег
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Опсег региона (#tag) за овај канал.\nПразно = подразумевани опсег.
Region scope (#tag), blank = default. Опсег региона (#tag), празно = подразумевано.
Region scope (#tag, blank = none) Опсег региона (#tag, празно = ниједан)
+4
View File
@@ -282,6 +282,7 @@ End time Час завершення
Enter SSID first Спочатку введіть SSID
Enter a name Введіть ім'я
Enter a network name Введіть назву мережі
Enter a region name Enter a region name
Enter adds a new line Enter додає новий рядок
Enter exactly 32 hex characters. Введіть рівно 32 hex-символи.
Enter key sends message Клавіша Enter надсилає повідомлення
@@ -407,6 +408,7 @@ Keyboard nav: WASDZ on Навігація клавіатурою: WASDZ увім
Keyboard nav: off Навігація клавіатурою: вимк.
Keyboard navigation Навігація клавіатурою
Keys
Known regions Known regions
LOS → %s Пряма видимість → %s
Landscape (desktop) Альбомна (десктоп)
Landscape set - applies when remote mode starts. Альбомну вибрано - застосується під час запуску віддаленого режиму.
@@ -533,6 +535,7 @@ No networks found (2.4 GHz only) Мережі не знайдено (лише 2.
No onboard GPS Немає вбудованого GPS
No other networks found Інших мереж не знайдено
No position yet. A cold start needs a clear view of the sky and can take several minutes. Позиції ще немає. Холодний старт потребує відкритого неба і може тривати кілька хвилин.
No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details. No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details.
No reply from %s Немає відповіді від %s
No reply. Check password, server,\nor device clock (device: %s) Немає відповіді. Перевірте пароль, сервер\nабо годинник пристрою (пристрій: %s)
No saved map tiles here.\n\nConnect to Wi-Fi (Settings → Wi-Fi)\nto download this area.\nSaved tiles stay available offline. Тут немає збережених плиток.\n\nПідключіться до Wi-Fi (Налаштування → Wi-Fi),\nщоб завантажити цю ділянку.\nЗбережені плитки лишаються доступні офлайн.
@@ -641,6 +644,7 @@ Receive unreleased test firmware — newer features, but less tested. The update
Recently heard Нещодавно чуто
Reformat SD as MESHCOMOD (FAT32)?\nALL data on the card will be erased. Переформатувати SD як MESHCOMOD (FAT32)?\nУСІ дані на картці будуть стерті.
Region & scope Регіон і охоплення
Region list is full Region list is full
Region scope (#tag) for this channel.\nBlank = use the default scope. Регіон (#tag) для цього каналу.\nПорожньо = область за умовчанням.
Region scope (#tag), blank = default. Область регіону (#tag), порожньо = за замовчуванням.
Region scope (#tag, blank = none) Регіон (#tag, порожньо = немає)
+64 -9
View File
@@ -49,13 +49,20 @@
#define REGION_REG_MAP_PATH "/region_map.dat"
#define REGION_REG_SLOT_PATH "/region_slots.dat"
#define REGION_REG_SLOT_MAGIC 0x52475331UL // "RGS1"
#define REGION_REG_SLOT_MAGIC 0x52475331UL // "RGS1" — slots only
#define REGION_REG_SLOT_MAGIC2 0x52475332UL // "RGS2" — slots + active mask
class RegionRegistry {
TransportKeyStore _keys;
RegionMap _map;
// index = slot (1..REGION_SLOT_MAX); value = RegionEntry::id, 0 = free.
// A slot binding is PERMANENT once assigned. Removing a region clears its bit
// in _slot_active -- it stops being matched at RX -- but the binding stays, so
// messages received while it WAS registered still resolve to the right name.
// Freeing the slot for reuse would silently relabel that history, which is the
// failure #271 explicitly warns about.
uint16_t _slot_id[REGION_SLOT_MAX + 1];
uint16_t _slot_active = 0; // bit (slot-1) set => participates in matching
FILESYSTEM* _fs = nullptr;
bool _ready = false;
@@ -80,14 +87,23 @@ class RegionRegistry {
void loadSlots() {
memset(_slot_id, 0, sizeof _slot_id);
_slot_active = 0;
if (!_fs) return;
File f = _fs->open(REGION_REG_SLOT_PATH, "r");
if (!f) return;
uint32_t magic = 0;
if (f.read((uint8_t*)&magic, sizeof magic) == (int)sizeof magic && magic == REGION_REG_SLOT_MAGIC)
if (f.read((uint8_t*)&magic, sizeof magic) == (int)sizeof magic
&& (magic == REGION_REG_SLOT_MAGIC || magic == REGION_REG_SLOT_MAGIC2)) {
f.read((uint8_t*)_slot_id, sizeof _slot_id);
else
if (magic == REGION_REG_SLOT_MAGIC2) {
f.read((uint8_t*)&_slot_active, sizeof _slot_active);
} else {
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) // pre-mask file: all assigned slots were live
if (_slot_id[s]) _slot_active |= (uint16_t)(1u << (s - 1));
}
} else {
memset(_slot_id, 0, sizeof _slot_id);
}
f.close();
}
@@ -109,9 +125,10 @@ public:
_map.save(_fs, REGION_REG_MAP_PATH);
File f = _fs->open(REGION_REG_SLOT_PATH, "w");
if (!f) return;
const uint32_t magic = REGION_REG_SLOT_MAGIC;
const uint32_t magic = REGION_REG_SLOT_MAGIC2;
f.write((const uint8_t*)&magic, sizeof magic);
f.write((const uint8_t*)_slot_id, sizeof _slot_id);
f.write((const uint8_t*)&_slot_active, sizeof _slot_active);
f.close();
}
@@ -127,10 +144,22 @@ public:
if (!e) e = _map.putRegion(tag, 0);
if (!e) return REGION_SLOT_NONE;
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) // already slotted?
if (_slot_id[s] == e->id) return s;
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) { // already slotted?
if (_slot_id[s] == e->id) {
// Re-adding a retired region revives its ORIGINAL slot rather than
// taking a new one, so its old messages and its new ones stay one region.
_slot_active |= (uint16_t)(1u << (s - 1));
save();
return s;
}
}
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) { // take the first free
if (_slot_id[s] == 0) { _slot_id[s] = e->id; save(); return s; }
if (_slot_id[s] == 0) {
_slot_id[s] = e->id;
_slot_active |= (uint16_t)(1u << (s - 1));
save();
return s;
}
}
return REGION_SLOT_NONE; // full: the message records "unknown", never a wrong name
}
@@ -144,7 +173,8 @@ public:
uint8_t hit = REGION_SLOT_NONE;
uint8_t n = 0;
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) {
if (_slot_id[s] == 0) continue;
if (_slot_id[s] == 0 || !isActive(s)) continue; // retired regions keep their
// name for history, but stop matching
RegionEntry* e = const_cast<RegionMap&>(_map).findById(_slot_id[s]);
if (!e) continue;
TransportKey keys[4];
@@ -170,9 +200,34 @@ public:
return (e && e->name[0]) ? e->name : nullptr;
}
/** True when this slot currently takes part in matching. A retired slot still
* resolves via nameForSlot() so old messages keep their region name. */
bool isActive(uint8_t slot) const {
if (slot == REGION_SLOT_NONE || slot > REGION_SLOT_MAX) return false;
return (_slot_active & (uint16_t)(1u << (slot - 1))) != 0;
}
/** Stop matching this region. The slot binding and name are KEPT on purpose --
* see the note on _slot_id. Returns false if the slot was never assigned. */
bool retire(uint8_t slot) {
if (!_ready || slot == REGION_SLOT_NONE || slot > REGION_SLOT_MAX) return false;
if (_slot_id[slot] == 0) return false;
_slot_active &= (uint16_t)~(1u << (slot - 1));
save();
return true;
}
/** Live regions (what the user sees as "the list"). */
int count() const {
int n = 0;
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) if (_slot_id[s]) n++;
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) if (_slot_id[s] && isActive(s)) n++;
return n;
}
/** True when every slot is assigned, so no NEW region can be added. Retired
* slots still count: their bindings are permanent. */
bool isFull() const {
for (uint8_t s = 1; s <= REGION_SLOT_MAX; ++s) if (_slot_id[s] == 0) return false;
return true;
}
};
+157
View File
@@ -4829,6 +4829,7 @@ static void chanScopeClose(); // fwd
static bool blockedModalIsOpen();// fwd: the bar's Back chevron also closes the blocked-users sheet
static void blockedModalClose(); // fwd
static void openBlockedUsersModal(); // ignore-list manager (unblock)
static void openRegionsModal(); // known-regions manager (#271)
static bool overlayBlocksTabSwipe(); // theme/channel-scope pickers swallow tab swipes
static bool drawerPopupOpen(); // popups floating over the app drawer (signal/mentions/power/files)
static void refreshContactsList();
@@ -10600,6 +10601,25 @@ static void buildRadioSettings() {
lv_obj_update_layout(note);
y += lv_obj_get_height(note) + SC(8);
}
// #271: the regions above are the ones we SEND under. This opens the list of
// regions we can RECOGNISE — naming them in message details needs only the
// name, since the key is derived from it.
{
lv_obj_t* rb = lv_btn_create(body);
lv_obj_set_size(rb, s_settings_content_w - SC(4), SC(34));
lv_obj_set_pos(rb, 2, y);
styleButton(rb);
lv_obj_add_event_cb(rb, [](lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
closeSettingsModal();
openRegionsModal();
}, LV_EVENT_CLICKED, nullptr);
lv_obj_t* rl = lv_label_create(rb);
lv_label_set_text(rl, TR(LV_SYMBOL_LIST " Known regions"));
lv_obj_set_style_text_font(rl, &g_font_14, LV_PART_MAIN);
lv_obj_center(rl);
y += SC(40);
}
// Opt-in: also scope DIRECT messages / logins / remote-management floods to the region
// (not just channels). For networks where your only repeater re-floods a single region.
@@ -44094,6 +44114,143 @@ static void blockedUnblockNameCb(lv_event_t* e) {
}
openBlockedUsersModal(); // rebuild the list
}
// ---- Known regions manager (#271) -------------------------------------------
// The list that makes scope naming possible. A region's key is derived from its
// NAME (SHA256("#tag")), so holding the name is enough to recompute an incoming
// packet's transport code and recognise it -- which is why adding a region here
// names its traffic even though we do not participate in it.
//
// Adding is the whole point of the feature; removing is deliberately "retire":
// the slot and name are kept so messages already received under that region keep
// resolving, it just stops being matched from now on. See RegionRegistry.
static lv_obj_t* s_regions_modal = nullptr;
static lv_obj_t* s_regions_ta = nullptr;
static void openRegionsModal(); // fwd: the add/remove callbacks rebuild the page
static void regionsModalClose() {
if (s_regions_modal) { hideKb(); popupClose(&s_regions_modal); }
s_regions_ta = nullptr;
if (s_apppage_close == regionsModalClose) {
s_apppage_title = nullptr; s_apppage_close = nullptr;
statusBarSetTall(false); updateGlobalStatusBar();
}
}
static void regionsAddCb(lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
if (!s_regions_ta) return;
const char* t = lv_textarea_get_text(s_regions_ta);
if (!t || !t[0]) { if (g_lv.task) g_lv.task->showAlert(TR("Enter a region name"), 1400); return; }
RegionRegistry& reg = the_mesh.regionRegistry();
if (reg.ensureRegion(t) == REGION_SLOT_NONE) {
// Two different failures, and the user can only act on one of them: the list
// being full, or the name canonicalising away to nothing (blank, or a bare
// "#"). Say which rather than silently not adding a row.
if (g_lv.task) g_lv.task->showAlert(reg.isFull() ? TR("Region list is full")
: TR("Enter a region name"), 2000);
return;
}
openRegionsModal(); // rebuild (closes first)
}
static void regionsRemoveCb(lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
const uint8_t slot = (uint8_t)(intptr_t)lv_event_get_user_data(e);
the_mesh.regionRegistry().retire(slot);
openRegionsModal(); // rebuild
}
static void openRegionsModal() {
regionsModalClose();
const lv_coord_t sw = lv_disp_get_hor_res(nullptr);
const lv_coord_t sh = lv_disp_get_ver_res(nullptr);
s_regions_modal = lv_obj_create(lv_layer_top());
lv_obj_remove_style_all(s_regions_modal);
lv_obj_set_size(s_regions_modal, sw, sh - chatBarH());
lv_obj_set_pos(s_regions_modal, 0, chatBarH());
lv_obj_set_style_bg_color(s_regions_modal, lv_color_hex(COLOR_BG), LV_PART_MAIN);
lv_obj_set_style_bg_opa(s_regions_modal, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_clear_flag(s_regions_modal, LV_OBJ_FLAG_SCROLLABLE);
s_apppage_title = "Known regions";
s_apppage_close = regionsModalClose;
statusBarSetTall(true);
updateGlobalStatusBar();
lv_obj_move_foreground(s_regions_modal);
if (g_statusbar.root) lv_obj_move_foreground(g_statusbar.root);
const lv_coord_t pad = SC(6);
lv_coord_t y = SC(6);
// Add row: field + button on one line, so the common action is reachable
// without scrolling on the short panels too.
const lv_coord_t btn_w = SC(64);
s_regions_ta = lv_textarea_create(s_regions_modal);
lv_textarea_set_one_line(s_regions_ta, true);
lv_textarea_set_max_length(s_regions_ta, TOUCH_REGION_SCOPE_MAXLEN - 1);
lv_obj_set_size(s_regions_ta, sw - pad * 3 - btn_w, SC(32));
lv_obj_set_pos(s_regions_ta, pad, y);
taSetPlaceholder(s_regions_ta, TR("#region"));
attachSettingsTaEvents(s_regions_ta);
lv_obj_t* add = lv_btn_create(s_regions_modal);
lv_obj_set_size(add, btn_w, SC(32));
lv_obj_set_pos(add, sw - pad - btn_w, y);
styleButton(add);
lv_obj_add_event_cb(add, regionsAddCb, LV_EVENT_CLICKED, nullptr);
{ lv_obj_t* l = lv_label_create(add); lv_label_set_text(l, TR("Add"));
lv_obj_set_style_text_font(l, &g_font_12, LV_PART_MAIN); lv_obj_center(l); }
y += SC(38);
RegionRegistry& reg = the_mesh.regionRegistry();
if (reg.count() <= 0) {
lv_obj_t* empty = lv_label_create(s_regions_modal);
lv_label_set_text(empty, TR("No regions yet.\n\nAdd a #region to have messages\nscoped to it named in message details."));
lv_label_set_long_mode(empty, LV_LABEL_LONG_WRAP);
lv_obj_set_width(empty, sw - pad * 2);
lv_obj_set_style_text_color(empty, lv_color_hex(COLOR_SUB), LV_PART_MAIN);
lv_obj_set_style_text_font(empty, &g_font_12, LV_PART_MAIN);
lv_obj_set_pos(empty, pad, y);
return;
}
lv_obj_t* list = lv_obj_create(s_regions_modal);
lv_obj_remove_style_all(list);
lv_obj_set_size(list, sw - pad * 2, sh - chatBarH() - y - SC(6));
lv_obj_set_pos(list, pad, y);
lv_obj_set_flex_flow(list, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(list, SC(5), LV_PART_MAIN);
lv_obj_set_scroll_dir(list, LV_DIR_VER);
for (uint8_t slot = 1; slot <= REGION_SLOT_MAX; ++slot) {
if (!reg.isActive(slot)) continue;
const char* nm = reg.nameForSlot(slot);
if (!nm || !nm[0]) continue;
lv_obj_t* row = lv_obj_create(list);
lv_obj_remove_style_all(row);
lv_obj_set_size(row, sw - pad * 2 - SC(4), SC(36));
lv_obj_set_style_bg_color(row, lv_color_hex(COLOR_PANEL), LV_PART_MAIN);
lv_obj_set_style_bg_opa(row, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_radius(row, 6, LV_PART_MAIN);
lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t* lbl = lv_label_create(row);
lv_label_set_text(lbl, nm);
lv_label_set_long_mode(lbl, LV_LABEL_LONG_DOT);
lv_obj_set_width(lbl, sw - pad * 2 - SC(4) - SC(84));
lv_obj_set_style_text_color(lbl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
lv_obj_set_style_text_font(lbl, &g_font_14, LV_PART_MAIN);
lv_obj_align(lbl, LV_ALIGN_LEFT_MID, SC(6), 0);
lv_obj_t* rm = lv_btn_create(row);
lv_obj_set_size(rm, SC(72), SC(28));
lv_obj_align(rm, LV_ALIGN_RIGHT_MID, -SC(5), 0);
styleButton(rm);
lv_obj_add_event_cb(rm, regionsRemoveCb, LV_EVENT_CLICKED, (void*)(intptr_t)slot);
{ lv_obj_t* l = lv_label_create(rm); lv_label_set_text(l, TR("Remove"));
lv_obj_set_style_text_font(l, &g_font_12, LV_PART_MAIN); lv_obj_center(l); }
}
}
static void openBlockedUsersModal() {
blockedModalClose();
const lv_coord_t sw = lv_disp_get_hor_res(nullptr);