diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 67fc9870..98364266 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -1,6 +1,9 @@ { "sourceLanguage" : "en", "strings" : { + "" : { + + }, "\t%@" : { "localizations" : { "it" : { @@ -1767,59 +1770,6 @@ } } }, - "ago" : { - "comment" : "Three hours ago = Три сата пре", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "her" - } - }, - "fr" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "auparavant" - } - }, - "he" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "עברו" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "fa" - } - }, - "pl" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "temu" - } - }, - "se" : { - "stringUnit" : { - "state" : "needs_review", - "value" : "sedan" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "пре" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "之前" - } - } - } - }, "Airtime" : { "localizations" : { "de" : { @@ -2734,28 +2684,6 @@ } } }, - "Backup Database" : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Database di backup" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Резервна база података" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "备份数据库" - } - } - } - }, "Bandwidth" : { "localizations" : { "de" : { @@ -5380,98 +5308,6 @@ } } }, - "config.power.ls.secs" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Light Sleep Interval" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "Light Sleep Interval" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervallo di sospensione leggera" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Light Sleep Interval" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervall för Ljussömn" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Интервал благог спавања" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "轻度睡眠间隔" - } - } - } - }, - "config.power.min.wake.secs" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Minimum Wake Interval" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "Minimum Wake Interval" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intervallo minimo di risveglio" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Minimum Wake Interval" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Minsta Väckningsintervall" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Минимални интервал будног стања" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "最小唤醒间隔" - } - } - } - }, "config.power.saving" : { "localizations" : { "de" : { @@ -5622,58 +5458,6 @@ } } }, - "config.power.section.sleep" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schlafmodus" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sleep" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sleep" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sospensione" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sleep" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sömn" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Стане спавања" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "休眠" - } - } - } - }, "config.power.settings" : { "localizations" : { "de" : { @@ -5876,110 +5660,6 @@ } } }, - "config.power.wait.bluetooth.secs" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bluetooth Aus nach" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bluetooth Off After" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bluetooth Off After" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bluetooth spento dopo" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bluetooth Off After" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bluetooth Stängs Av Efter" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Блутут се искључује након" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "蓝牙关闭 After" - } - } - } - }, - "config.ringtone" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL Klingelton" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL Ringtone" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL Ringtone" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Suoneria RTTTL" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL Ringtone" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL Ringsignal" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL мелодија звона" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "RTTTL 铃声" - } - } - } - }, "config.ringtone.description" : { "localizations" : { "en" : { @@ -6594,64 +6274,6 @@ }, "Consent to Share Unencrypted Location Data via MQTT" : { - }, - "contacts" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kontakte" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contacts" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contacts" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "אנשי קשר" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contatti" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kontakty" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kontakter" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Контакти" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "联系人" - } - } - } }, "contacts %@" : { "localizations" : { @@ -10113,64 +9735,6 @@ } } }, - "email.address" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Email Adresse" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Email Address" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Adresse mail" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "כתובת דואר אלקטרוני" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Indirizzo e-mail" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Adres Email" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "E-postadress" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Имејл адреса" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "邮件地址" - } - } - } - }, "Emoji" : { "localizations" : { "it" : { @@ -10359,27 +9923,8 @@ } } }, - "Enables the store and forward module. Store and forward must be enabled on both client and router devices." : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abilita il modulo store and forward. Store and forward deve essere abilitato su entrambi i dispositivi client e router." - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Омогућава модул за чување и пренос. Чување и пренос мора бити омогућено на оба уређаја, клијенту и рутеру." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "启用存储和转发模块。客户端和路由器设备都必须启用存储和转发功能。" - } - } - } + "Enables the store and forward module." : { + }, "Enabling Ethernet will disable the bluetooth connection to the app." : { "localizations" : { @@ -11895,116 +11440,6 @@ } } }, - "gas" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gas" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gas" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gaz" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "דלק" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gas" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gaz" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gas" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Гас" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "气体" - } - } - } - }, - "gas.resistance" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gas Resistance" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Résistence du gaz" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gas Resistance" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resistenza ai gas" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Odporność na Gaz" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gasmotstånd" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Отпорност на гас" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "气体阻抗" - } - } - } - }, "generate.qr.code" : { "localizations" : { "de" : { @@ -13017,64 +12452,6 @@ } } }, - "heard.last" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zuletzt gehört" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Last Heard" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Capté pour la dernière fois" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "נשמע לאחרונה" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ultimo ascolto" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ostatnio Słyszane" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Senast Hörd" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Прво откривање" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "最后收到" - } - } - } - }, "Help with App Development" : { "localizations" : { "it" : { @@ -13519,28 +12896,6 @@ } } }, - "HUMIDITY" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "LUFTFEUCHTIGKEIT" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "UMIDITÀ" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "ВЛАЖНОСТ" - } - } - } - }, "hybrid" : { "localizations" : { "de" : { @@ -14672,28 +14027,6 @@ } } }, - "interval.eventytwo.hours" : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "intervallo.eventodue.ore" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Двадесет и два сата" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "七十二小时" - } - } - } - }, "interval.fifteen.minutes" : { "localizations" : { "de" : { @@ -15970,122 +15303,6 @@ } } }, - "interval.twenty.seconds" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zwanzig Sekunden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twenty Seconds" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vingt secondes" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "עשרים שניות" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Venti secondi" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dwadzieścia Sekund" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tjugo Sekunder" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Двадесет секунди" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "二十秒" - } - } - } - }, - "interval.twentyfive.seconds" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fünfundzwanzig Sekunden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twenty Five Seconds" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vingt cinq secondes" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "עשרים וחמש שניות" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Venticinque secondi" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dwadzieścia Pięć Sekund" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tjugofem Sekunder" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Двадесет пет секунди" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "二十五秒" - } - } - } - }, "interval.twentyfour.hours" : { "localizations" : { "de" : { @@ -16411,9 +15628,6 @@ } } } - }, - "Jump to present" : { - }, "Key" : { "localizations" : { @@ -16481,64 +15695,6 @@ } } }, - "keyboard.type" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keyboard Typ" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keyboard Type" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Type de clavier" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "סוג מקלדת" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tipo di tastiera" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Typ Klawiatury" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tangentbordstyp" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Тип тастатуре" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "键盘类型" - } - } - } - }, "Korea" : { "localizations" : { "en" : { @@ -17955,58 +17111,6 @@ } } }, - "map.centering" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Centering Mode" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mode centré" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מכשיר במרכז" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modalità di centratura" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tryb Wyśrodkowania" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Centreringsläge" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Режим центрирања" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "居中" - } - } - } - }, "map.tiles.delete" : { "localizations" : { "en" : { @@ -18059,180 +17163,6 @@ } } }, - "map.type" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kartentyp" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Default Type" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Type par défaut" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "סוג ברירת מחדל" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tipo predefinito" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Domyślny Typ" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Standardtyp" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Подразумевани тип" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "地图类型" - } - } - } - }, - "map.use.legacy" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Use Legacy Mesh Map" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Use Legacy Mesh Map" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utiliser l'ancienne génération de carte de maillage" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "השתמש במפה מדור קודם" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usare la mappa mesh legacy" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Use Legacy Mesh Map" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Använd Äldre Mesh Karta" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Користите легаси мрежну мапу" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "使用传统网状地图" - } - } - } - }, - "map.usertrackingmode" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "User tracking mode" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "User tracking mode" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mode suivre l'utilisateur" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מצב מעקב אחר משתמש" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modalità di tracciamento dell'utente" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tryb śledzenia użytkownika" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Spårningsläge för användare" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Мод праћења корисника" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "用户跟随模式" - } - } - } - }, "map.usertrackingmode.follow" : { "localizations" : { "de" : { @@ -18525,64 +17455,6 @@ } } }, - "mesh.log" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh Log" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh Log" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Journal du maillage" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "יומן מש" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Registro di mesh" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dziennik Sieci" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh-logg" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Логови мреже" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mesh 日志" - } - } - } - }, "mesh.log.ambientlighting.config %@" : { "localizations" : { "en" : { @@ -19069,64 +17941,6 @@ } } }, - "mesh.log.devicemetadata %@" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anforderung der Geräte Metadaten für %@" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Requesting Device Metadata for %@" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Demande des metadatas de l'appareil à %@" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מבקש מטא-דאטה עבור %@" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Richiesta dei metadati del dispositivo per %@" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Żądanie metadanych urządzenia dla %@" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Begär metadata för enhet för %@" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Захтевање метаподатака уређаја за %@" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Requesting Device Metadata for %@" - } - } - } - }, "mesh.log.display.config %@" : { "localizations" : { "de" : { @@ -20441,64 +19255,6 @@ } } }, - "mesh.log.traceroute.received.direct %@" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Traceroute Anforderung an Knoten gesendet: %@ wurde direkt empfangen." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trace Route request sent to node: %@ was recieived directly." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "La demande de Trace Route envoyée au noeud : %@ a été directement reçue." - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "בקשת בדיקת מסלול נשלחה למכשיר: %@ התקבל ישירות." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trace La richiesta di rotta inviata al nodo: %@ è stata ricevuta direttamente." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Żądanie śledzenia trasy wysłane do węzła: %@ zostało odebrane bezpośrednio." - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Spårruttförfrågan skickad till nod: %@ mottogs direkt." - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Захтев за тражење путања послат на чвор: %@ је примљен директно." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trace Route request sent to node: %@ was recieived directly." - } - } - } - }, "mesh.log.traceroute.received.route %@" : { "localizations" : { "de" : { @@ -22091,64 +20847,6 @@ } } }, - "New Node Notifications" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mitteilungen über neue Knoten" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "New Node Notifications" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "New Node Notifications" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "New Node Notifications" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notifiche di nuovi nodi" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "New Node Notifications" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "New Node Notifications" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обавештења о новим чворовима" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "新节点通知" - } - } - } - }, "New Zealand 865mhz" : { "localizations" : { "en" : { @@ -22355,64 +21053,6 @@ } } }, - "no.nodes" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Meshtastic Knoten gefunden" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No Meshtastic Nodes Found" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aucun noeud Meshtastic trouvé" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "לא נמצאו מכשירי משטסטיק" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nessun nodo Meshtastic trovato" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nie znaleziono węzłów Meshtastic" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Inga Meshtastic-noder hittades" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Нема пронађених Мештастик чворова" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "未找到 Meshtastic 节点" - } - } - } - }, "Node" : { "localizations" : { "de" : { @@ -22919,64 +21559,6 @@ } } }, - "numbers.punctuation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ziffern und Interpunktion" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Numbers and Punctuation" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nombres and Ponctuation" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מספרים וסימני פיסוק " - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Numeri e punteggiatura" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cyfry i interpunkcja" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Siffror och skiljetecken" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Бројеви и интерпункција" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "数字和标点符号" - } - } - } - }, "off" : { "localizations" : { "de" : { @@ -23035,64 +21617,6 @@ } } }, - "offline" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Offline" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Offline" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hors ligne" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מנותק" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Non in linea" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Offline" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Offline" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ван мреже" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "离线" - } - } - } - }, "OK" : { "localizations" : { "de" : { @@ -24135,64 +22659,6 @@ } } }, - "phone.gps.interval.description" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wie häufig das Telefon den Standort an das Gerät sendet. Standortaktualisierungen an das Mesh werden vom Gerät verwaltet." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "How frequently your phone will send your location to the device, location updates to the mesh are managed by the device." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "La fréquence à laquelle votre téléphone envoie votre position à l'appareil, les mises à jour de la position vers le maillage sont gérées par l'appareil." - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "כל כמה זמן מכשיר הטלפון ישלח את מיקומך למכשיר המשטסטיק. עדכוני מיקום למש מנוהלות על ידי המכשיר." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "La frequenza con cui il telefono invia la posizione al dispositivo, gli aggiornamenti della posizione alla rete sono gestiti dal dispositivo." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jak często Twój telefon będzie wysyłał swoją lokalizację do urządzenia, aktualizacje lokalizacji w sieci są zarządzane przez urządzenie." - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hur ofta din telefon skickar din plats till enheten, platsuppdateringar till mesh-nätverket hanteras av enheten." - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Колико често ваш телефон шаље вашу локацију уређају, ажурирања локације на мрежу се управљају од стране уређаја." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "电台通过手机获取定位的时间间隔,但是向 Mesh 网络中发送定位的时间间隔由电台控制。" - } - } - } - }, "Pin %lld" : { "localizations" : { "it" : { @@ -24740,28 +23206,6 @@ } } }, - "power.metrics.delete" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete all power metrics?" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cancellare tutte le metriche di potenza?" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обрисати све логове метрике снаге?" - } - } - } - }, "power.metrics.log" : { "localizations" : { "en" : { @@ -24828,64 +23272,6 @@ } } }, - "preferred.radio" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bevorzugtes Gerät" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Preferred Radio" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radio favorie" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "רדיו מועדף" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radio preferita" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Preferowane radio" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Föredragen Radio" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Преферирани радио" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "默认电台" - } - } - } - }, "Presets" : { "localizations" : { "it" : { @@ -25216,64 +23602,6 @@ } } }, - "Random PIN" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zufällige PIN" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Random PIN" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Code PIN aléatoire" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "קוד אקראי" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "PIN casuale" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Losowy PIN" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Slumpmässig PIN" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Насумичан ПИН" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "随机 PIN 码" - } - } - } - }, "range.test" : { "localizations" : { "de" : { @@ -25332,64 +23660,6 @@ } } }, - "range.test.blocked" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Block Range Test" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Block Range Test" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Test de portée bloqué" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "חסום בדיקות טווח" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Test di portata del blocco" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Block Range Test" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Blockera räckviddstest" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Тест домета блока" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "区块范围测试" - } - } - } - }, "range.test.config" : { "localizations" : { "de" : { @@ -26292,28 +24562,6 @@ } } }, - "restore" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wiederherstellen" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "ripristinare" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обнова" - } - } - } - }, "resume" : { "localizations" : { "de" : { @@ -26458,64 +24706,6 @@ } } }, - "ringtone.config" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Klingelton Konfiguration" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ringtone Config" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configuration de la sonnerie" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "הגדרות רינגטון" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Configurazione della suoneria" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Konfiguracja dzwonka" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ringsignalsinställningar" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Подешавање мелодије звона" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "铃声设置" - } - } - } - }, "Role" : { "localizations" : { "de" : { @@ -26706,38 +24896,6 @@ } } }, - "Router" : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Router" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Рутер" - } - } - } - }, - "Router Options" : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opzioni del router" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Опције рутера" - } - } - } - }, "Routes" : { "localizations" : { "it" : { @@ -27758,40 +25916,6 @@ } } }, - "routing.pkiunknownpubkey" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unbekannter öffentlicher Schlüssel" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unknown Public Key" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Chiave pubblica sconosciuta" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Непознат јавни кључ" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "未知的公钥" - } - } - } - }, "routing.timeout" : { "localizations" : { "de" : { @@ -28648,64 +26772,6 @@ } } }, - "select.contact" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kontakt wählen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Select a Contact" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sélectioner un contact" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "בחר איש קשר" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Selezionare un contatto" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wybierz kontakt" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Välj en kontakt" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Одабери контакт" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "选择一名联系人" - } - } - } - }, "select.node" : { "localizations" : { "de" : { @@ -29958,64 +28024,6 @@ } } }, - "share.position" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Position teilen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Share Position" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Partager la position" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "שתף מיקום" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Condividi Posizione" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Udostępnij pozycję" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dela position" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Подели позицију" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "分享位置" - } - } - } - }, "Shared Key" : { "localizations" : { "de" : { @@ -30880,38 +28888,6 @@ } } }, - "Store and forward clients can request history from routers on the network." : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "I client Store and forward possono richiedere la cronologia ai router della rete." - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Клијенти за складиштење и прослеђивање могу затражити историју од рутера на мрежи." - } - } - } - }, - "Store and forward router devices require a ESP32 device with PSRAM." : { - "localizations" : { - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "I dispositivi router Store and Forward richiedono un dispositivo ESP32 con PSRAM." - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Рутер за складиштење и прослеђивање захтева ESP32 уређај са PSRAM." - } - } - } - }, "Store and forward servers require an ESP32 device with PSRAM or Linux Native." : { "localizations" : { "en" : { @@ -32854,122 +30830,6 @@ } } }, - "tip.bluetooth.connect.message" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shows information for the Lora radio currently connected via bluetooth. You can swipe left to disconnect the radio and long press to view stats or start the live activity." - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shows information for the Lora radio connected via bluetooth. You can swipe left to disconnect the radio and long press to view stats or start the live activity." - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Affiche les informations de la radio Lora connectée via le bluetooth. Vous pouvez faire un glissé vers la gauche pour déconnecter la radio et un appui long pour voir les statistiques ou démarrer l'activité en direct." - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מראה מידע אודות מכשיר המשטסטיק המחובר כעת לבלוטוס. ניתן לגרור שמאלה להתנתקות או לחיצה ארוכה לראות סטטיסטיקה או להתחיל פעילות." - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostra le informazioni relative alla radio Lora collegata via bluetooth. È possibile scorrere il dito verso sinistra per scollegare la radio e premere a lungo per visualizzare le statistiche o avviare l'attività live." - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Shows information for the Lora radio currently connected via bluetooth. You can swipe left to disconnect the radio and long press to view stats or start the live activity." - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Visar information för LoRa-radion ansluten via bluetooth. Du kan svepa åt vänster för att koppla från radion och långtryck för att visa statistik eller starta liveaktivitet." - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Приказује информације за LoRA радио повезан преко Блутута. Можете превући лево да бисте одспојили радио и дуго притиснути да бисте погледали статистику или започели активност у реалном времену." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "显示当前通过蓝牙连接的 Lora 电台的信息。您可以向左滑动断开电台,长按查看统计信息或开始实时活动。" - } - } - } - }, - "tip.bluetooth.connect.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connected LoRa Radio" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connected Radio" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radio connectée" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מכשיר מחובר" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Radio connessa" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connected LoRa Radio" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ansluten Radio" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Радио повезан" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "电台已连接" - } - } - } - }, "tip.channel.admin.message" : { "localizations" : { "de" : { @@ -33726,64 +31586,6 @@ } } }, - "twitter" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "טוויטר" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "X.com" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" - } - } - } - }, "Two Hours" : { "localizations" : { "it" : { @@ -34622,64 +32424,6 @@ } } }, - "user.details" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Benutzer Details" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "User Details" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Détails de l'utilisateur" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "פרטי משתמש" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dettagli utente" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Szczegóły użytkownika" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Användaruppgifter" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Кориснички детаљи" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "用户信息" - } - } - } - }, "Uses pullup resistor" : { "localizations" : { "it" : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 4d6a0403..1ac17a2e 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -62,7 +62,6 @@ BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; }; BCB613852C68703800485544 /* NodePositionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613842C68703800485544 /* NodePositionIntent.swift */; }; BCB613872C69A0FB00485544 /* AppIntentErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613862C69A0FB00485544 /* AppIntentErrors.swift */; }; - BCD93CB82D9E0D9F006C9214 /* ScrollToBottomButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD93CB72D9E0D9F006C9214 /* ScrollToBottomButtonView.swift */; }; BCE2D3C32C7ADF42008E6199 /* ShutDownNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */; }; BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */; }; BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */; }; @@ -327,7 +326,6 @@ BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = ""; }; BCB613842C68703800485544 /* NodePositionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodePositionIntent.swift; sourceTree = ""; }; BCB613862C69A0FB00485544 /* AppIntentErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentErrors.swift; sourceTree = ""; }; - BCD93CB72D9E0D9F006C9214 /* ScrollToBottomButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToBottomButtonView.swift; sourceTree = ""; }; BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShutDownNodeIntent.swift; sourceTree = ""; }; BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestartNodeIntent.swift; sourceTree = ""; }; BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsProvider.swift; sourceTree = ""; }; @@ -1058,7 +1056,6 @@ DD5E523D298F5A7D00D21B61 /* Weather */, DD6F65712C6AB8EC0053C113 /* SecureInput.swift */, 8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */, - BCD93CB72D9E0D9F006C9214 /* ScrollToBottomButtonView.swift */, ); path = Helpers; sourceTree = ""; @@ -1281,9 +1278,7 @@ pl, he, fr, - "zh-Hant-TW", se, - "pt-PT", sr, it, ); @@ -1441,7 +1436,6 @@ B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */, DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */, 233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */, - BCD93CB82D9E0D9F006C9214 /* ScrollToBottomButtonView.swift in Sources */, DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */, DD1BD0F32C63C65E008C0C70 /* SecurityConfig.swift in Sources */, DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */, diff --git a/Meshtastic/Views/Helpers/ScrollToBottomButtonView.swift b/Meshtastic/Views/Helpers/ScrollToBottomButtonView.swift deleted file mode 100644 index 50ddee24..00000000 --- a/Meshtastic/Views/Helpers/ScrollToBottomButtonView.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// ScrollToBottomButtonView.swift -// Meshtastic -// -// Created by Benjamin Faershtein on 4/2/25. -// - -import SwiftUI - -struct ScrollToBottomButtonView: View { - var body: some View { - HStack(spacing: 4) { - Text("Jump to present") - .font(.caption) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .cornerRadius(12) - Image(systemName: "arrow.down") - .font(.title2) - .symbolRenderingMode(.hierarchical) - - } - .foregroundColor(.accentColor) - .shadow(radius: 2) - } -} - -#Preview { - ScrollToBottomButtonView() -} diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 9586d194..f567b7be 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -22,11 +22,6 @@ struct ChannelMessageList: View { @ObservedObject var channel: ChannelEntity @State private var replyMessageId: Int64 = 0 @AppStorage("preferredPeripheralNum") private var preferredPeripheralNum = -1 - - // Scroll state - @State private var showScrollToBottomButton = false - @State private var hasReachedBottom = false - @State private var gotFirstUnreadMessage: Bool = false var body: some View { VStack { @@ -71,136 +66,97 @@ struct ChannelMessageList: View { .offset(y: 8) } - HStack { - MessageText( - message: message, - tapBackDestination: .channel(channel), - isCurrentUser: currentUser - ) { - self.replyMessageId = message.messageId - self.messageFieldFocused = true - } + VStack(alignment: currentUser ? .trailing : .leading) { + let isDetectionSensorMessage = message.portNum == Int32(PortNum.detectionSensorApp.rawValue) - if currentUser && message.canRetry { - RetryButton(message: message, destination: .channel(channel)) - } + if !currentUser && message.fromUser != nil { + Text("\(message.fromUser?.longName ?? "unknown".localized ) (\(message.fromUser?.userId ?? "?"))") + .font(.caption) + .foregroundColor(.gray) + .offset(y: 8) + } + + HStack { + MessageText( + message: message, + tapBackDestination: .channel(channel), + isCurrentUser: currentUser + ) { + self.replyMessageId = message.messageId + self.messageFieldFocused = true } - TapbackResponses(message: message) { - appState.unreadChannelMessages = myInfo.unreadMessages - context.refresh(myInfo, mergeChanges: true) - } - - HStack { - let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) - if currentUser && message.receivedACK { - Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true) - .foregroundStyle(ackErrorVal?.color ?? Color.red) - .font(.caption2) - } else if currentUser && message.ackError == 0 { - // Empty Error - Text("Waiting to be acknowledged. . .").font( - .caption2) - .foregroundColor(.orange) - } else if currentUser && !isDetectionSensorMessage { - Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true) - .foregroundStyle(ackErrorVal?.color ?? Color.red) - .font(.caption2) - } + if currentUser && message.canRetry { + RetryButton(message: message, destination: .channel(channel)) } } - .padding(.bottom) - .id(channel.allPrivateMessages.firstIndex(of: message)) - if !currentUser { - Spacer(minLength: 50) + TapbackResponses(message: message) { + appState.unreadChannelMessages = myInfo.unreadMessages + context.refresh(myInfo, mergeChanges: true) } - } - .padding([.leading, .trailing]) - .frame(maxWidth: .infinity) - .id(message.messageId) - .onAppear { - if gotFirstUnreadMessage{ - if !message.read { - message.read = true - do { - for unreadMessage in channel.allPrivateMessages.filter({ !$0.read }) { - unreadMessage.read = true - } - try context.save() - Logger.data.info("📖 [App] Read message \(message.messageId, privacy: .public) ") - appState.unreadChannelMessages = myInfo.unreadMessages - context.refresh(myInfo, mergeChanges: true) - } catch { - Logger.data.error("Failed to read message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)") - } - } - // Check if we've reached the bottom message - if message.messageId == channel.allPrivateMessages.last?.messageId { - hasReachedBottom = true - showScrollToBottomButton = false + + HStack { + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) + if currentUser && message.receivedACK { + Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true) + .foregroundStyle(ackErrorVal?.color ?? Color.red) + .font(.caption2) + } else if currentUser && message.ackError == 0 { + // Empty Error + Text("Waiting to be acknowledged. . .").font( + .caption2) + .foregroundColor(.orange) + } else if currentUser && !isDetectionSensorMessage { + Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true) + .foregroundStyle(ackErrorVal?.color ?? Color.red) + .font(.caption2) } } } - } - // Invisible spacer to detect reaching bottom - Color.clear - .frame(height: 1) - .id("bottomAnchor") - .onAppear { - hasReachedBottom = true - showScrollToBottomButton = false + .padding(.bottom) + .id(channel.allPrivateMessages.firstIndex(of: message)) + + if !currentUser { + Spacer(minLength: 50) } - } - } - .scrollDismissesKeyboard(.interactively) - .onFirstAppear { - // Find first unread message - if let firstUnreadMessageId = channel.allPrivateMessages.first(where: { !$0.read })?.messageId { - withAnimation { - scrollView.scrollTo(firstUnreadMessageId, anchor: .top) - showScrollToBottomButton = true } - } else { - // If no unread messages, scroll to bottom - withAnimation { - scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom) - hasReachedBottom = true + .padding([.leading, .trailing]) + .frame(maxWidth: .infinity) + .id(message.messageId) + .onAppear { + if !message.read { + message.read = true + do { + for unreadMessage in channel.allPrivateMessages.filter({ !$0.read }) { + unreadMessage.read = true + } + try context.save() + Logger.data.info("📖 [App] Read message \(message.messageId, privacy: .public) ") + appState.unreadChannelMessages = myInfo.unreadMessages + context.refresh(myInfo, mergeChanges: true) + } catch { + Logger.data.error("Failed to read message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)") + } + } } } - gotFirstUnreadMessage = true } - .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { _ in - withAnimation { - scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom) - hasReachedBottom = true - showScrollToBottomButton = false - } + } + .scrollDismissesKeyboard(.interactively) + .onFirstAppear { + withAnimation { + scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom) } - .onChange(of: channel.allPrivateMessages) { - if hasReachedBottom { - withAnimation { - scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom) - } - } else { - showScrollToBottomButton = true - } + } + .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { _ in + withAnimation { + scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom) } - - // Scroll to bottom button - if showScrollToBottomButton { - Button { - withAnimation { - scrollView.scrollTo("bottomAnchor", anchor: .bottom) - hasReachedBottom = true - showScrollToBottomButton = false - } - } label: { - ScrollToBottomButtonView() - } - .padding(.bottom, 8) - .padding(.trailing, 16) - .transition(.opacity) + } + .onChange(of: channel.allPrivateMessages) { + withAnimation { + scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom) } } } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 6f995756..dea4586f 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -20,172 +20,115 @@ struct UserMessageList: View { // View State Items @ObservedObject var user: UserEntity @State private var replyMessageId: Int64 = 0 - - // Scroll state - @State private var showScrollToBottomButton = false - @State private var hasReachedBottom = false - @State private var gotFirstUnreadMessage: Bool = false var body: some View { VStack { ScrollViewReader { scrollView in - ZStack(alignment: .bottomTrailing) { - ScrollView { - LazyVStack { - ForEach( user.messageList ) { (message: MessageEntity) in - if user.num != bleManager.connectedPeripheral?.num ?? -1 { - let currentUser: Bool = (Int64(UserDefaults.preferredPeripheralNum) == message.fromUser?.num ?? -1 ? true : false) + ScrollView { + LazyVStack { + ForEach( user.messageList ) { (message: MessageEntity) in + if user.num != bleManager.connectedPeripheral?.num ?? -1 { + let currentUser: Bool = (Int64(UserDefaults.preferredPeripheralNum) == message.fromUser?.num ?? -1 ? true : false) - if message.replyID > 0 { - let messageReply = user.messageList.first(where: { $0.messageId == message.replyID }) + if message.replyID > 0 { + let messageReply = user.messageList.first(where: { $0.messageId == message.replyID }) + HStack { + Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.accentColor).font(.caption2) + .padding(10) + .overlay( + RoundedRectangle(cornerRadius: 18) + .stroke(Color.blue, lineWidth: 0.5) + ) + Image(systemName: "arrowshape.turn.up.left.fill") + .symbolRenderingMode(.hierarchical) + .imageScale(.large).foregroundColor(.accentColor) + .padding(.trailing) + } + } + HStack(alignment: .top) { + if currentUser { Spacer(minLength: 50) } + VStack(alignment: currentUser ? .trailing : .leading) { HStack { - Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.accentColor).font(.caption2) - .padding(10) - .overlay( - RoundedRectangle(cornerRadius: 18) - .stroke(Color.blue, lineWidth: 0.5) - ) - Image(systemName: "arrowshape.turn.up.left.fill") - .symbolRenderingMode(.hierarchical) - .imageScale(.large).foregroundColor(.accentColor) - .padding(.trailing) + MessageText( + message: message, + tapBackDestination: .user(user), + isCurrentUser: currentUser + ) { + self.replyMessageId = message.messageId + self.messageFieldFocused = true + } + + if currentUser && message.canRetry || (message.receivedACK && !message.realACK) { + RetryButton(message: message, destination: .user(user)) + } } - } - HStack(alignment: .top) { - if currentUser { Spacer(minLength: 50) } - VStack(alignment: currentUser ? .trailing : .leading) { - HStack { - MessageText( - message: message, - tapBackDestination: .user(user), - isCurrentUser: currentUser - ) { - self.replyMessageId = message.messageId - self.messageFieldFocused = true - } - if currentUser && message.canRetry || (message.receivedACK && !message.realACK) { - RetryButton(message: message, destination: .user(user)) - } - } + TapbackResponses(message: message) { + appState.unreadDirectMessages = user.unreadMessages + } - TapbackResponses(message: message) { - appState.unreadDirectMessages = user.unreadMessages - } - - HStack { - let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) - if currentUser && message.receivedACK { - // Ack Received - if message.realACK { - Text("\(ackErrorVal?.display ?? "Empty Ack Error")") - .font(.caption2) - .foregroundStyle(ackErrorVal?.color ?? Color.secondary) - } else { - Text("Acknowledged by another node").font(.caption2).foregroundColor(.orange) - } - } else if currentUser && message.ackError == 0 { - // Empty Error - Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.yellow) - } else if currentUser && message.ackError > 0 { - Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true) - .foregroundStyle(ackErrorVal?.color ?? Color.red) + HStack { + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) + if currentUser && message.receivedACK { + // Ack Received + if message.realACK { + Text("\(ackErrorVal?.display ?? "Empty Ack Error")") .font(.caption2) + .foregroundStyle(ackErrorVal?.color ?? Color.secondary) + } else { + Text("Acknowledged by another node").font(.caption2).foregroundColor(.orange) } + } else if currentUser && message.ackError == 0 { + // Empty Error + Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.yellow) + } else if currentUser && message.ackError > 0 { + Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true) + .foregroundStyle(ackErrorVal?.color ?? Color.red) + .font(.caption2) } } - .padding(.bottom) - .id(user.messageList.firstIndex(of: message)) + } + .padding(.bottom) + .id(user.messageList.firstIndex(of: message)) - if !currentUser { - Spacer(minLength: 50) - } - } - .padding([.leading, .trailing]) - .frame(maxWidth: .infinity) - .id(message.messageId) - .onAppear { - if gotFirstUnreadMessage { - if !message.read { - message.read = true - do { - for unreadMessage in user.messageList.filter({ !$0.read }) { - unreadMessage.read = true - } - try context.save() - Logger.data.info("📖 [App] Read message \(message.messageId, privacy: .public) ") - appState.unreadDirectMessages = user.unreadMessages - } catch { - Logger.data.error("Failed to read message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)") - } - } - // Check if we've reached the bottom message - if message.messageId == user.messageList.last?.messageId { - hasReachedBottom = true - showScrollToBottomButton = false - } - } + if !currentUser { + Spacer(minLength: 50) } } - } - // Invisible spacer to detect reaching bottom - Color.clear - .frame(height: 1) - .id("bottomAnchor") + .padding([.leading, .trailing]) + .frame(maxWidth: .infinity) + .id(message.messageId) .onAppear { - hasReachedBottom = true - showScrollToBottomButton = false + if !message.read { + message.read = true + do { + try context.save() + Logger.data.info("📖 [App] Read message \(message.messageId, privacy: .public) ") + appState.unreadDirectMessages = user.unreadMessages + + } catch { + Logger.data.error("Failed to read message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)") + } + } } - } - } - .scrollDismissesKeyboard(.interactively) - .onFirstAppear { - // Find first unread message - if let firstUnreadMessageId = user.messageList.first(where: { !$0.read })?.messageId { - withAnimation { - scrollView.scrollTo(firstUnreadMessageId, anchor: .top) - showScrollToBottomButton = true - } - } else { - // If no unread messages, scroll to bottom - withAnimation { - scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom) - hasReachedBottom = true } } - gotFirstUnreadMessage = true } - .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { _ in - withAnimation { - scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom) - hasReachedBottom = true - showScrollToBottomButton = false - } + } + .scrollDismissesKeyboard(.interactively) + .onFirstAppear { + withAnimation { + scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom) } - .onChange(of: user.messageList) { - if hasReachedBottom { - withAnimation { - scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom) - } - } else { - showScrollToBottomButton = true - } + } + .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { _ in + withAnimation { + scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom) } - - // Scroll to bottom button - if showScrollToBottomButton { - Button { - withAnimation { - scrollView.scrollTo("bottomAnchor", anchor: .bottom) - hasReachedBottom = true - showScrollToBottomButton = false - } - } label: { - ScrollToBottomButtonView() - } - .padding(.bottom, 8) - .padding(.trailing, 16) - .transition(.opacity) + } + .onChange(of: user.messageList) { + withAnimation { + scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom) } } } diff --git a/Widgets/WidgetsLiveActivity.swift b/Widgets/WidgetsLiveActivity.swift index 231efec2..3923fee7 100644 --- a/Widgets/WidgetsLiveActivity.swift +++ b/Widgets/WidgetsLiveActivity.swift @@ -31,23 +31,21 @@ struct WidgetsLiveActivity: Widget { } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { - if context.state.totalNodes >= 100 { - Text("100+ online") + if context.state.totalNodes > 0 { + Text(" \(context.state.nodesOnline) online") .font(.callout) .foregroundStyle(.secondary) .fixedSize() } else { - Text("\(context.state.nodesOnline) of \(context.state.totalNodes) online") + Text(" ") .font(.callout) .foregroundStyle(.secondary) .fixedSize() } - // Text("\(context.state.channelUtilization.map { String(format: "Ch. Util: %.2f", $0) } ?? "--")%") Text("Ch. Util: \(context.state.channelUtilization?.formatted(.number.precision(.fractionLength(2))) ?? Constants.nilValueIndicator)%") .font(.caption2) .foregroundStyle(.secondary) .fixedSize() - // Text("\(context.state.airtime.map { String(format: "Airtime: %.2f", $0) } ?? "--")%") Text("Airtime: \(context.state.airtime?.formatted(.number.precision(.fractionLength(2))) ?? Constants.nilValueIndicator)%") .font(.caption2) .foregroundStyle(.secondary) @@ -166,7 +164,7 @@ struct LiveActivityView: View { .frame(minWidth: 25, idealWidth: 45, maxWidth: 55) Spacer() NodeInfoView(isLuminanceReduced: _isLuminanceReduced, nodeName: nodeName, uptimeSeconds: uptimeSeconds, channelUtilization: channelUtilization, airtime: airtime, sentPackets: sentPackets, receivedPackets: receivedPackets, badReceivedPackets: badReceivedPackets, - dupeReceivedPackets: dupeReceivedPackets, packetsSentRelay: packetsSentRelay, packetsCanceledRelay: packetsCanceledRelay, nodesOnline: nodesOnline, totalNodes: totalNodes, timerRange: timerRange) + dupeReceivedPackets: dupeReceivedPackets, packetsSentRelay: packetsSentRelay, packetsCanceledRelay: packetsCanceledRelay, nodesOnline: nodesOnline, timerRange: timerRange) Spacer() } .tint(.primary) @@ -191,7 +189,6 @@ struct NodeInfoView: View { var packetsSentRelay: UInt32 var packetsCanceledRelay: UInt32 var nodesOnline: UInt32 - var totalNodes: UInt32 var timerRange: ClosedRange var body: some View { @@ -220,21 +217,14 @@ struct NodeInfoView: View { .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() - if totalNodes >= 100 { - Text("Connected: \(nodesOnline) nodes online") - .font(.caption) - .fontWeight(.medium) - .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.8 : 1.0) - .fixedSize() - } else { - Text("Connected: \(nodesOnline) of \(totalNodes) nodes online") - .font(.caption) - .fontWeight(.medium) - .foregroundStyle(.secondary) - .opacity(isLuminanceReduced ? 0.8 : 1.0) - .fixedSize() - } + + Text("Connected: \(nodesOnline) nodes online") + .font(.caption) + .fontWeight(.medium) + .foregroundStyle(.secondary) + .opacity(isLuminanceReduced ? 0.8 : 1.0) + .fixedSize() + let now = Date() Text("Last Heard: \(now.formatted())") .font(.caption)