Merge pull request #1162 from meshtastic/2.5.22

2.5.22 Working Changes
This commit is contained in:
Garth Vander Houwen 2025-04-01 20:20:53 -07:00 committed by GitHub
commit 8e68f3bbd5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 262 additions and 1450 deletions

View file

@ -5717,6 +5717,9 @@
}
}
}
},
"Connected Radio" : {
},
"connected.radio" : {
"localizations" : {
@ -9315,6 +9318,9 @@
}
}
}
},
"Enable this device as a Store and Forward server. Requires an ESP32 device with PSRAM." : {
},
"enabled" : {
"localizations" : {
@ -9412,21 +9418,8 @@
}
}
},
"Enables the store and forward module. Store and forward must be enabled on both client and router devices." : {
"localizations" : {
"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" : {
@ -24952,26 +24945,6 @@
}
}
},
"Router" : {
"localizations" : {
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Рутер"
}
}
}
},
"Router Options" : {
"localizations" : {
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Опције рутера"
}
}
}
},
"Routes" : {
"localizations" : {
"sr" : {
@ -27036,6 +27009,9 @@
}
}
}
},
"Send a heartbeat to advertise the server's presence." : {
},
"Send a message to a certain meshtastic channel" : {
"localizations" : {
@ -27785,6 +27761,9 @@
}
}
}
},
"Server Option" : {
},
"Set" : {
"localizations" : {
@ -27943,6 +27922,9 @@
}
}
}
},
"Settings" : {
},
"Share QR Code & Link" : {
"localizations" : {
@ -28295,6 +28277,9 @@
}
}
}
},
"Shows information for the Lora radio connected via bluetooth. You can swipe left to disconnect the radio and long press start the live activity." : {
},
"Shut Down" : {
"localizations" : {
@ -28785,25 +28770,8 @@
}
}
},
"Store and forward clients can request history from routers on the network." : {
"localizations" : {
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Клијенти за складиштење и прослеђивање могу затражити историју од рутера на мрежи."
}
}
}
},
"Store and forward router devices require a ESP32 device with PSRAM." : {
"localizations" : {
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Рутер за складиштење и прослеђивање захтева ESP32 уређај са PSRAM."
}
}
}
"Store and forward servers require an ESP32 device with PSRAM or Linux Native." : {
},
"storeforward.heartbeat" : {
"localizations" : {
@ -30501,134 +30469,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" : "מראה מידע אודות מכשיר המשטסטיק המחובר כעת לבלוטוס. ניתן לגרור שמאלה להתנתקות או לחיצה ארוכה לראות סטטיסטיקה או להתחיל פעילות."
}
},
"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."
}
},
"pt-PT" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostra informações para o rádio LoRa conectado via bluetooth. Você pode deslizar para a esquerda para desconectar o rádio e pressionar por um longo período para ver estatísticas ou iniciar a atividade ao vivo."
}
},
"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 电台的信息。您可以向左滑动断开电台,长按查看统计信息或开始实时活动。"
}
},
"zh-Hant-TW" : {
"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" : "מכשיר מחובר"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Connected LoRa Radio"
}
},
"pt-PT" : {
"stringUnit" : {
"state" : "translated",
"value" : "Rádio Conectado"
}
},
"se" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ansluten Radio"
}
},
"sr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Радио повезан"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "电台已连接"
}
},
"zh-Hant-TW" : {
"stringUnit" : {
"state" : "translated",
"value" : "連接到 LoRa 電台"
}
}
}
},
"tip.channel.admin.message" : {
"localizations" : {
"de" : {

View file

@ -73,7 +73,6 @@
D93068DB2B81C85E0066FBC8 /* PowerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068DA2B81C85E0066FBC8 /* PowerConfig.swift */; };
D93068DD2B81CA820066FBC8 /* ConfigHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93068DC2B81CA820066FBC8 /* ConfigHeader.swift */; };
D93069082B81DF040066FBC8 /* SaveConfigButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93069072B81DF040066FBC8 /* SaveConfigButton.swift */; };
D9BC22DB2B7DE8E2006A37D5 /* TileDownloadStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9BC22DA2B7DE8E2006A37D5 /* TileDownloadStatus.swift */; };
D9C9839D2B79CFD700BDBE6A /* TextMessageSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C9839C2B79CFD700BDBE6A /* TextMessageSize.swift */; };
D9C983A02B79D0E800BDBE6A /* AlertButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C9839F2B79D0E800BDBE6A /* AlertButton.swift */; };
D9C983A22B79D1A600BDBE6A /* RequestPositionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9C983A12B79D1A600BDBE6A /* RequestPositionButton.swift */; };
@ -99,7 +98,6 @@
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553562855B02500E55709 /* LoRaConfig.swift */; };
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; };
DD268D8E2BCC90E2008073AE /* RouteEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD268D8D2BCC90E2008073AE /* RouteEnums.swift */; };
DD2AD8A8296D2DF9001FF0E7 /* MapViewSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */; };
DD33DB622B3D27C7003E1EA0 /* FirmwareApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD33DB612B3D27C7003E1EA0 /* FirmwareApi.swift */; };
DD3501892852FC3B000FC853 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3501882852FC3B000FC853 /* Settings.swift */; };
DD354FD92BD96A0B0061A25F /* IAQScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD354FD82BD96A0B0061A25F /* IAQScale.swift */; };
@ -153,9 +151,7 @@
DD93800E2BA74D0C008BEC06 /* ChannelForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD93800D2BA74D0C008BEC06 /* ChannelForm.swift */; };
DD94B7402ACCE3BE00DCD1D1 /* MapSettingsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */; };
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */; };
DD964FBF296E76EF007C176F /* WaypointFormMapKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */; };
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */; };
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC32974767D007C176F /* MapViewFitExtension.swift */; };
DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD964FC52975DBFD007C176F /* QueryCoreData.swift */; };
DD97E96628EFD9820056DDA4 /* MeshtasticLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */; };
DD97E96828EFE9A00056DDA4 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD97E96728EFE9A00056DDA4 /* About.swift */; };
@ -212,8 +208,6 @@
DDDB26422AABF655003AFCB7 /* NodeListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB26412AABF655003AFCB7 /* NodeListItem.swift */; };
DDDB26442AAC0206003AFCB7 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB26432AAC0206003AFCB7 /* NodeDetail.swift */; };
DDDB26462AACC0B7003AFCB7 /* NodeInfoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB26452AACC0B7003AFCB7 /* NodeInfoItem.swift */; };
DDDB26482AACD6D1003AFCB7 /* NodeMapMapkit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB26472AACD6D1003AFCB7 /* NodeMapMapkit.swift */; };
DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB443529F6287000EE2349 /* MapButtons.swift */; };
DDDB444029F79AB000EE2349 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB443F29F79AB000EE2349 /* UserDefaults.swift */; };
DDDB444229F8A88700EE2349 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB444129F8A88700EE2349 /* Double.swift */; };
DDDB444429F8A8DD00EE2349 /* Float.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB444329F8A8DD00EE2349 /* Float.swift */; };
@ -344,7 +338,6 @@
D93068DC2B81CA820066FBC8 /* ConfigHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigHeader.swift; sourceTree = "<group>"; };
D93069062B81D8900066FBC8 /* MeshtasticDataModelV 27.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 27.xcdatamodel"; sourceTree = "<group>"; };
D93069072B81DF040066FBC8 /* SaveConfigButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveConfigButton.swift; sourceTree = "<group>"; };
D9BC22DA2B7DE8E2006A37D5 /* TileDownloadStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TileDownloadStatus.swift; sourceTree = "<group>"; };
D9C9839C2B79CFD700BDBE6A /* TextMessageSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageSize.swift; sourceTree = "<group>"; };
D9C9839F2B79D0E800BDBE6A /* AlertButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertButton.swift; sourceTree = "<group>"; };
D9C983A12B79D1A600BDBE6A /* RequestPositionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestPositionButton.swift; sourceTree = "<group>"; };
@ -379,7 +372,6 @@
DD268D8D2BCC90E2008073AE /* RouteEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteEnums.swift; sourceTree = "<group>"; };
DD295CE92B323ED9002CC4AC /* MeshtasticDataModelV22.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV22.xcdatamodel; sourceTree = "<group>"; };
DD2984A82C5AEF7500B1268D /* MeshtasticDataModelV 41.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 41.xcdatamodel"; sourceTree = "<group>"; };
DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewSwiftUI.swift; sourceTree = "<group>"; };
DD2CC2E52ABFE04E00EDFDA7 /* MeshtasticDataModelV19.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV19.xcdatamodel; sourceTree = "<group>"; };
DD31B04D2BDC6FD30024FA63 /* MeshtasticDataModelV 36.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 36.xcdatamodel"; sourceTree = "<group>"; };
DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 23.xcdatamodel"; sourceTree = "<group>"; };
@ -451,10 +443,8 @@
DD93800D2BA74D0C008BEC06 /* ChannelForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelForm.swift; sourceTree = "<group>"; };
DD94B73F2ACCE3BE00DCD1D1 /* MapSettingsForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsForm.swift; sourceTree = "<group>"; };
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiOnlyTextField.swift; sourceTree = "<group>"; };
DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointFormMapKit.swift; sourceTree = "<group>"; };
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV6.xcdatamodel; sourceTree = "<group>"; };
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointEntityExtension.swift; sourceTree = "<group>"; };
DD964FC32974767D007C176F /* MapViewFitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewFitExtension.swift; sourceTree = "<group>"; };
DD964FC52975DBFD007C176F /* QueryCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryCoreData.swift; sourceTree = "<group>"; };
DD9681A22BBB22BE00FD2C47 /* MeshtasticDataModelV32.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV32.xcdatamodel; sourceTree = "<group>"; };
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticLogo.swift; sourceTree = "<group>"; };
@ -528,9 +518,7 @@
DDDB26412AABF655003AFCB7 /* NodeListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeListItem.swift; sourceTree = "<group>"; };
DDDB26432AAC0206003AFCB7 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = "<group>"; };
DDDB26452AACC0B7003AFCB7 /* NodeInfoItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoItem.swift; sourceTree = "<group>"; };
DDDB26472AACD6D1003AFCB7 /* NodeMapMapkit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMapMapkit.swift; sourceTree = "<group>"; };
DDDB26492AAD743E003AFCB7 /* MeshtasticDataModelV18.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV18.xcdatamodel; sourceTree = "<group>"; };
DDDB443529F6287000EE2349 /* MapButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapButtons.swift; sourceTree = "<group>"; };
DDDB443F29F79AB000EE2349 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
DDDB444129F8A88700EE2349 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
DDDB444329F8A8DD00EE2349 /* Float.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Float.swift; sourceTree = "<group>"; };
@ -702,27 +690,6 @@
path = AppIntents;
sourceTree = "<group>";
};
C9483F6B2773016700998F6B /* MapKitMap */ = {
isa = PBXGroup;
children = (
C9A7BC0E27759A6800760B50 /* Custom */,
DDDB26472AACD6D1003AFCB7 /* NodeMapMapkit.swift */,
DD964FBE296E76EF007C176F /* WaypointFormMapKit.swift */,
);
path = MapKitMap;
sourceTree = "<group>";
};
C9A7BC0E27759A6800760B50 /* Custom */ = {
isa = PBXGroup;
children = (
DD964FC32974767D007C176F /* MapViewFitExtension.swift */,
DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */,
DDDB443529F6287000EE2349 /* MapButtons.swift */,
D9BC22DA2B7DE8E2006A37D5 /* TileDownloadStatus.swift */,
);
path = Custom;
sourceTree = "<group>";
};
D9C9839E2B79D0C600BDBE6A /* TextMessageField */ = {
isa = PBXGroup;
children = (
@ -1023,7 +990,6 @@
isa = PBXGroup;
children = (
DD6D5A312CA1176A00ED3032 /* Layouts */,
C9483F6B2773016700998F6B /* MapKitMap */,
DDC2E18D26CE25CB0042C5E4 /* Helpers */,
DD47E3D726F2F21A00029299 /* Bluetooth */,
DDC2E18B26CE25A70042C5E4 /* Messages */,
@ -1415,11 +1381,9 @@
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */,
D93069082B81DF040066FBC8 /* SaveConfigButton.swift in Sources */,
DD5E523F298F5A9E00D21B61 /* AirQualityIndex.swift in Sources */,
DD964FBF296E76EF007C176F /* WaypointFormMapKit.swift in Sources */,
DDD5BB182C2F9C36007E03CA /* OSLogEntryLog.swift in Sources */,
DD3501892852FC3B000FC853 /* Settings.swift in Sources */,
DDDC22382BA92344002C44F1 /* MeshMapContent.swift in Sources */,
DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */,
DD5D0A9C2931B9F200F7EA61 /* EthernetModes.swift in Sources */,
6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */,
DD798B072915928D005217CD /* ChannelMessageList.swift in Sources */,
@ -1464,7 +1428,6 @@
DD354FD92BD96A0B0061A25F /* IAQScale.swift in Sources */,
DDDB445429F8AD1600EE2349 /* Data.swift in Sources */,
DDDB26462AACC0B7003AFCB7 /* NodeInfoItem.swift in Sources */,
DD2AD8A8296D2DF9001FF0E7 /* MapViewSwiftUI.swift in Sources */,
DDE5B4042B2279A700FCDD05 /* TraceRouteLog.swift in Sources */,
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */,
DDDB263F2AABEE20003AFCB7 /* NodeList.swift in Sources */,
@ -1474,7 +1437,6 @@
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */,
DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */,
233E99C32D849D7A00CC3A77 /* WeightCompactWidget.swift in Sources */,
DDDB26482AACD6D1003AFCB7 /* NodeMapMapkit.swift in Sources */,
DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */,
DD1BD0F32C63C65E008C0C70 /* SecurityConfig.swift in Sources */,
DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */,
@ -1518,7 +1480,6 @@
D93068DB2B81C85E0066FBC8 /* PowerConfig.swift in Sources */,
D93068D32B8129510066FBC8 /* MessageContextMenuItems.swift in Sources */,
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */,
BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */,
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
@ -1537,7 +1498,6 @@
D93068DD2B81CA820066FBC8 /* ConfigHeader.swift in Sources */,
251926872C3BAE2200249DF5 /* NodeAlertsButton.swift in Sources */,
DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */,
D9BC22DB2B7DE8E2006A37D5 /* TileDownloadStatus.swift in Sources */,
DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */,
BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */,
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
@ -1845,7 +1805,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5.21;
MARKETING_VERSION = 2.5.22;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1879,7 +1839,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.5.21;
MARKETING_VERSION = 2.5.22;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1911,7 +1871,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.5.21;
MARKETING_VERSION = 2.5.22;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1944,7 +1904,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.5.21;
MARKETING_VERSION = 2.5.22;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View file

@ -16,7 +16,7 @@ class AppIntentErrors {
var localizedStringResource: LocalizedStringResource {
switch self {
case let .message(message):
Logger.services.error("App Intent: \(message)")
Logger.services.error("App Intent: \(message,privacy: .public)")
return "Error: \(message)"
case .notConnected:
Logger.services.error("App Intent: No Connected Node")

View file

@ -119,7 +119,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
self.isConnected = false
self.isConnecting = false
self.lastConnectionError = "🚨 " + String.localizedStringWithFormat("Connection failed after %d attempts to connect to %@. You may need to forget your device under Settings > Bluetooth.".localized, timeoutTimerCount, name)
Logger.services.error("\(self.lastConnectionError)")
Logger.services.error("\(self.lastConnectionError, privacy: .public)")
self.timeoutTimerCount = 0
self.startScanning()
} else {
@ -295,7 +295,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
} else {
// Disconnected without error which indicates user intent to disconnect
// Happens when swiping to disconnect
Logger.services.info(" [BLE] Disconnected: \(peripheral.name ?? "Unknown".localized, privacy: .public): \(String(describing: "User Initiated Disconnect".localized))")
Logger.services.info(" [BLE] Disconnected: \(peripheral.name ?? "Unknown".localized, privacy: .public): \(String(describing: "User Initiated Disconnect".localized), privacy: .public)")
}
// Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake
self.startScanning()
@ -485,7 +485,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
let logString = String.localizedStringWithFormat("mesh.log.traceroute.sent %@".localized, destNum.toHex())
Logger.mesh.info("🪧 \(logString)")
Logger.mesh.info("🪧 \(logString, privacy: .public)")
} catch {
@ -498,14 +498,14 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
guard connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected else { return }
if FROMRADIO_characteristic == nil {
Logger.mesh.error("🚨 \("firmware.version.unsupported".localized)")
Logger.mesh.error("🚨 \("firmware.version.unsupported".localized, privacy: .public)")
invalidVersion = true
return
} else {
let nodeName = connectedPeripheral?.peripheral.name ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.wantconfig %@".localized, nodeName)
Logger.mesh.info("🛎️ \(logString)")
Logger.mesh.info("🛎️ \(logString, privacy: .public)")
// BLE Characteristics discovered, issue wantConfig
var toRadio: ToRadio = ToRadio()
configNonce += 1
@ -537,7 +537,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if let coordsMatch = try CommonRegex.COORDS_REGEX.firstMatch(in: logString) {
log = "\(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces))"
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
Logger.radio.debug("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
Logger.radio.debug("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private(mask: .none)) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
} else {
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
Logger.radio.debug("🕵🏻‍♂️ \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
@ -750,7 +750,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
}
// Log any other unknownApp calls
if !nowKnown { Logger.mesh.info("🕸️ MESH PACKET received for Unknown App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") }
if !nowKnown { Logger.mesh.info("🕸️ MESH PACKET received for Unknown App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)") }
case .textMessageApp, .detectionSensorApp:
textMessageAppPacket(
packet: decodedInfo.packet,
@ -769,7 +769,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
appState: appState
)
case .remoteHardwareApp:
Logger.mesh.info("🕸️ MESH PACKET received for Remote Hardware App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received for Remote Hardware App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .positionApp:
upsertPositionPacket(packet: decodedInfo.packet, context: context)
case .waypointApp:
@ -794,11 +794,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
case .serialApp:
Logger.mesh.info("🕸️ MESH PACKET received for Serial App UNHANDLED UNHANDLED")
case .storeForwardApp:
if wantStoreAndForwardPackets {
storeAndForwardPacket(packet: decodedInfo.packet, connectedNodeNum: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), context: context)
} else {
Logger.mesh.info("🕸️ MESH PACKET received for Store and Forward App - Store and Forward is disabled.")
}
storeAndForwardPacket(packet: decodedInfo.packet, connectedNodeNum: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), context: context)
case .rangeTestApp:
if wantRangeTestPackets {
textMessageAppPacket(
@ -971,33 +967,33 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
Logger.data.error("Error Updating Core Data TraceRouteHop: \(nsError, privacy: .public)")
}
let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString)
Logger.mesh.info("🪧 \(logString)")
Logger.mesh.info("🪧 \(logString, privacy: .public)")
}
case .neighborinfoApp:
if let neighborInfo = try? NeighborInfo(serializedBytes: decodedInfo.packet.decoded.payload) {
Logger.mesh.info("🕸️ MESH PACKET received for Neighbor Info App UNHANDLED \((try? neighborInfo.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received for Neighbor Info App UNHANDLED \((try? neighborInfo.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
}
case .paxcounterApp:
paxCounterPacket(packet: decodedInfo.packet, context: context)
case .mapReportApp:
Logger.mesh.info("🕸️ MESH PACKET received Map Report App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received Map Report App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .UNRECOGNIZED:
Logger.mesh.info("🕸️ MESH PACKET received UNRECOGNIZED App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received UNRECOGNIZED App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .max:
Logger.services.info("MAX PORT NUM OF 511")
case .atakPlugin:
Logger.mesh.info("🕸️ MESH PACKET received for ATAK Plugin App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received for ATAK Plugin App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .powerstressApp:
Logger.mesh.info("🕸️ MESH PACKET received for Power Stress App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received for Power Stress App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
case .reticulumTunnelApp:
Logger.mesh.info("🕸️ MESH PACKET received for Reticulum Tunnel App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.info("🕸️ MESH PACKET received for Reticulum Tunnel App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
}
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce {
invalidVersion = false
lastConnectionError = ""
isSubscribed = true
Logger.mesh.info("🤜 [BLE] Want Config Complete. ID:\(decodedInfo.configCompleteID)")
Logger.mesh.info("🤜 [BLE] Want Config Complete. ID:\(decodedInfo.configCompleteID, privacy: .public)")
if sendTime() {
}
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
@ -1031,7 +1027,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
} catch {
Logger.data.error("Failed to find a node info for the connected node \(error.localizedDescription)")
Logger.data.error("Failed to find a node info for the connected node \(error.localizedDescription, privacy: .public)")
}
}
@ -1075,7 +1071,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
let nodeName = connectedPeripheral?.peripheral.name ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.textmessage.send.failed %@".localized, nodeName)
Logger.mesh.info("🚫 \(logString)")
Logger.mesh.info("🚫 \(logString, privacy: .public)")
success = false
} else if message.count < 1 {
@ -1162,7 +1158,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat("mesh.log.textmessage.sent %@ %@ %@".localized, String(newMessage.messageId), fromUserNum.toHex(), toUserNum.toHex())
Logger.mesh.info("💬 \(logString)")
Logger.mesh.info("💬 \(logString, privacy: .public)")
do {
try context.save()
Logger.data.info("💾 Saved a new sent message from \(self.connectedPeripheral.num.toHex(), privacy: .public) to \(toUserNum.toHex(), privacy: .public)")
@ -1209,7 +1205,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
return false
}
let logString = String.localizedStringWithFormat("mesh.log.waypoint.sent %@".localized, String(fromNodeNum))
Logger.mesh.info("📍 \(logString)")
Logger.mesh.info("📍 \(logString, privacy: .public)")
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
@ -1373,7 +1369,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat("mesh.log.sharelocation %@".localized, String(fromNodeNum))
Logger.services.debug("📍 \(logString)")
Logger.services.debug("📍 \(logString, privacy: .public)")
return true
} else {
Logger.services.error("Device no longer connected. Unable to send position information.")
@ -1689,7 +1685,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
}
}
} catch {
Logger.data.error("Failed to find a node MyInfo to save these channels to: \(error.localizedDescription)")
Logger.data.error("Failed to find a node MyInfo to save these channels to: \(error.localizedDescription, privacy: .public)")
}
}
@ -1728,7 +1724,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat("mesh.log.channel.sent %@ %d".localized, String(connectedPeripheral.num), chan.index)
Logger.mesh.info("🎛️ \(logString)")
Logger.mesh.info("🎛️ \(logString, privacy: .public)")
}
}
// Save the LoRa Config and the device will reboot
@ -1757,7 +1753,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat("mesh.log.lora.config.sent %@".localized, String(connectedPeripheral.num))
Logger.mesh.info("📻 \(logString)")
Logger.mesh.info("📻 \(logString, privacy: .public)")
}
if self.connectedPeripheral != nil {
@ -1834,7 +1830,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("🚫 Error deleting node from core data: \(nsError)")
Logger.data.error("🚫 Error deleting node from core data: \(nsError, privacy: .public)")
}
}
return false
@ -2677,7 +2673,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat("mesh.log.cannedmessages.messages.get %@".localized, String(connectedPeripheral.num))
Logger.mesh.info("🥫 \(logString)")
Logger.mesh.info("🥫 \(logString, privacy: .public)")
return true
}
@ -3257,7 +3253,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
Logger.mesh.debug("\(adminDescription)")
Logger.mesh.debug("\(adminDescription, privacy: .public)")
return true
}
return false
@ -3454,7 +3450,7 @@ extension BLEManager: CBCentralManagerDelegate {
default:
status = "default"
}
Logger.services.info("📜 [BLE] Bluetooth status: \(status)")
Logger.services.info("📜 [BLE] Bluetooth status: \(status, privacy: .public)")
}
// Called each time a peripheral is discovered

View file

@ -75,7 +75,7 @@ class LocalNotificationManager {
UNUserNotificationCenter.current().add(request) { error in
if let error {
Logger.services.error("Error Scheduling Notification: \(error.localizedDescription)")
Logger.services.error("Error Scheduling Notification: \(error.localizedDescription, privacy: .public)")
}
}
}

View file

@ -69,7 +69,7 @@ import OSLog
}
}
} catch {
Logger.services.error("💥 [App] Could not start location updates: \(error.localizedDescription)")
Logger.services.error("💥 [App] Could not start location updates: \(error.localizedDescription, privacy: .public)")
}
return
}
@ -84,15 +84,15 @@ import OSLog
if smartPostion {
let age = -location.timestamp.timeIntervalSinceNow
if age > 10 {
Logger.services.info("📍 [App] Smart Position - Bad Location: Too Old \(age, privacy: .public) seconds ago \(location, privacy: .private)")
Logger.services.info("📍 [App] Smart Position - Bad Location: Too Old \(age, privacy: .public) seconds ago \(location, privacy: .private(mask: .none))")
return false
}
if location.horizontalAccuracy < 0 {
Logger.services.info("📍 [App] Smart Position - Bad Location: Horizontal Accuracy: \(location.horizontalAccuracy) \(location, privacy: .private)")
Logger.services.info("📍 [App] Smart Position - Bad Location: Horizontal Accuracy: \(location.horizontalAccuracy) \(location, privacy: .private(mask: .none))")
return false
}
if location.horizontalAccuracy > 5 {
Logger.services.info("📍 [App] Smart Position - Bad Location: Horizontal Accuracy: \(location.horizontalAccuracy) \(location, privacy: .private)")
Logger.services.info("📍 [App] Smart Position - Bad Location: Horizontal Accuracy: \(location.horizontalAccuracy) \(location, privacy: .private(mask: .none))")
return false
}
}

View file

@ -104,7 +104,7 @@ func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNu
func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedObjectContext) -> MyInfoEntity? {
let logString = String.localizedStringWithFormat("mesh.log.myinfo %@".localized, String(myInfo.myNodeNum))
Logger.mesh.info(" \(logString)")
Logger.mesh.info(" \(logString, privacy: .public)")
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(myInfo.myNodeNum))
@ -155,7 +155,7 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled {
let logString = String.localizedStringWithFormat("mesh.log.channel.received %d %@".localized, channel.index, String(fromNum))
Logger.mesh.info("🎛️ \(logString)")
Logger.mesh.info("🎛️ \(logString, privacy: .public)")
let fetchedMyInfoRequest = MyInfoEntity.fetchRequest()
fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum)
@ -194,7 +194,7 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
} catch {
Logger.data.error("💥 Failed to save channel: \(error.localizedDescription, privacy: .public)")
}
Logger.data.info("💾 Updated MyInfo channel \(channel.index) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)")
Logger.data.info("💾 Updated MyInfo channel \(channel.index, privacy: .public) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum, privacy: .public)")
} else if channel.role.rawValue > 0 {
Logger.data.error("💥Trying to save a channel to a MyInfo that does not exist: \(fromNum.toHex(), privacy: .public)")
}
@ -210,7 +210,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPass
if metadata.isInitialized {
let logString = String.localizedStringWithFormat("mesh.log.device.metadata.received %@".localized, fromNum.toHex())
Logger.mesh.info("🏷️ \(logString)")
Logger.mesh.info("🏷️ \(logString, privacy: .public)")
let fetchedNodeRequest = NodeInfoEntity.fetchRequest()
fetchedNodeRequest.predicate = NSPredicate(format: "num == %lld", fromNum)
@ -262,7 +262,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPass
func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? {
let logString = String.localizedStringWithFormat("mesh.log.nodeinfo.received %@".localized, String(nodeInfo.num))
Logger.mesh.info("📟 \(logString)")
Logger.mesh.info("📟 \(logString, privacy: .public)")
guard nodeInfo.num > 0 else { return nil }
@ -350,12 +350,12 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
}
do {
try context.save()
Logger.data.info("💾 Saved a new Node Info For: \(String(nodeInfo.num))")
Logger.data.info("💾 Saved a new Node Info For: \(String(nodeInfo.num), privacy: .public)")
return newNode
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving Core Data NodeInfoEntity: \(nsError)")
Logger.data.error("Error Saving Core Data NodeInfoEntity: \(nsError, privacy: .public)")
}
} catch {
Logger.data.error("Fetch MyInfo Error")
@ -473,7 +473,7 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
if !cmmc.messages.isEmpty {
let logString = String.localizedStringWithFormat("mesh.log.cannedmessages.messages.received %@".localized, packet.from.toHex())
Logger.mesh.info("🥫 \(logString)")
Logger.mesh.info("🥫 \(logString, privacy: .public)")
let fetchNodeRequest = NodeInfoEntity.fetchRequest()
fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
@ -548,7 +548,7 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let ringtone = adminMessage.getRingtoneResponse
upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: Int64(packet.from), context: context)
} else {
Logger.mesh.error("🕸️ MESH PACKET received Admin App UNHANDLED \((try? packet.decoded.jsonString()) ?? "JSON Decode Failure")")
Logger.mesh.error("🕸️ MESH PACKET received Admin App UNHANDLED \((try? packet.decoded.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
}
// Save an ack for the admin message log for each admin message response received as we stopped sending acks if there is also a response to reduce airtime.
adminResponseAck(packet: packet, context: context)
@ -573,17 +573,17 @@ func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) {
do {
try context.save()
} catch {
Logger.data.error("Failed to save admin message response as an ack: \(error.localizedDescription)")
Logger.data.error("Failed to save admin message response as an ack: \(error.localizedDescription, privacy: .public)")
}
}
} catch {
Logger.data.error("Failed to fetch admin message by requestID: \(error.localizedDescription)")
Logger.data.error("Failed to fetch admin message by requestID: \(error.localizedDescription, privacy: .public)")
}
}
func paxCounterPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.paxcounter %@".localized, String(packet.from))
Logger.mesh.info("🧑‍🤝‍🧑 \(logString)")
Logger.mesh.info("🧑‍🤝‍🧑 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
@ -608,7 +608,7 @@ func paxCounterPacket (packet: MeshPacket, context: NSManagedObjectContext) {
do {
try context.save()
} catch {
Logger.data.error("Failed to save pax: \(error.localizedDescription)")
Logger.data.error("Failed to save pax: \(error.localizedDescription, privacy: .public)")
}
} else {
Logger.data.info("Node Info Not Found")
@ -627,7 +627,7 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
let routingErrorString = routingError?.display ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.routing.message %@ %@".localized, String(packet.decoded.requestID), routingErrorString)
Logger.mesh.info("🕸️ \(logString)")
Logger.mesh.info("🕸️ \(logString, privacy: .public)")
let fetchMessageRequest = MessageEntity.fetchRequest()
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
@ -674,11 +674,11 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
return
}
try context.save()
Logger.data.info("💾 ACK Saved for Message: \(packet.decoded.requestID)")
Logger.data.info("💾 ACK Saved for Message: \(packet.decoded.requestID, privacy: .public)")
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving ACK for message: \(packet.id) Error: \(nsError)")
Logger.data.error("Error Saving ACK for message: \(packet.id, privacy: .public) Error: \(nsError, privacy: .public)")
}
}
}
@ -688,7 +688,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
if let telemetryMessage = try? Telemetry(serializedBytes: packet.decoded.payload) {
let logString = String.localizedStringWithFormat("mesh.log.telemetry.received %@".localized, String(packet.from))
Logger.mesh.info("📈 \(logString)")
Logger.mesh.info("📈 \(logString, privacy: .public)")
if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) && telemetryMessage.variant != Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) {
/// Other unhandled telemetry packets
@ -782,7 +782,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
}
try context.save()
Logger.data.info("💾 [TelemetryEntity] of type \(MetricsTypes(rawValue: Int(telemetry.metricsType))?.name ?? "Unknown Metrics Type") Saved for Node: \(packet.from.toHex())")
Logger.data.info("💾 [TelemetryEntity] of type \(MetricsTypes(rawValue: Int(telemetry.metricsType))?.name ?? "Unknown Metrics Type", privacy: .public) Saved for Node: \(packet.from.toHex(), privacy: .public)")
if telemetry.metricsType == 0 {
// Connected Device Metrics
// ------------------------
@ -883,7 +883,7 @@ func textMessageAppPacket(
}
if messageText?.count ?? 0 > 0 {
Logger.mesh.info("💬 \("mesh.log.textmessage.received".localized)")
Logger.mesh.info("💬 \("mesh.log.textmessage.received".localized, privacy: .public)")
let messageUsers = UserEntity.fetchRequest()
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
do {
@ -909,11 +909,14 @@ func textMessageAppPacket(
if packet.decoded.replyID > 0 {
newMessage.replyID = Int64(packet.decoded.replyID)
}
// Updated logic for handling toUser
if fetchedUsers.first(where: { $0.num == packet.to }) != nil && packet.to != Constants.maximumNodeNum {
if !storeForwardBroadcast {
newMessage.toUser = fetchedUsers.first(where: { $0.num == packet.to })
} else if storeForwardBroadcast {
// For S&F broadcast messages, treat as a channel message (not a DM)
newMessage.toUser = nil
} else {
/// Make a new to user if they are unknown
newMessage.toUser = createUser(num: Int64(truncatingIfNeeded: packet.to), context: context)
}
}
@ -959,7 +962,7 @@ func textMessageAppPacket(
var messageSaved = false
do {
try context.save()
Logger.data.info("💾 Saved a new message for \(newMessage.messageId)")
Logger.data.info("💾 Saved a new message for \(newMessage.messageId, privacy: .public)")
messageSaved = true
if messageSaved {
@ -972,7 +975,7 @@ func textMessageAppPacket(
appState.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0
}
if !(newMessage.fromUser?.mute ?? false) {
// Create an iOS Notification for the received DM message and schedule it immediately
// Create an iOS Notification for the received DM message
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
@ -989,23 +992,21 @@ func textMessageAppPacket(
)
]
manager.schedule()
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized, privacy: .public)")
}
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode))
do {
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest)
if !fetchedMyInfo.isEmpty {
appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
if channel.index == newMessage.channel {
context.refresh(channel, mergeChanges: true)
}
if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications {
// Create an iOS Notification for the received private channel message and schedule it immediately
// Create an iOS Notification for the received channel message
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
@ -1018,10 +1019,11 @@ func textMessageAppPacket(
messageId: newMessage.messageId,
channel: newMessage.channel,
userNum: Int64(newMessage.fromUser?.userId ?? "0"),
critical: critical)
critical: critical
)
]
manager.schedule()
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized, privacy: .public)")
}
}
}
@ -1033,7 +1035,7 @@ func textMessageAppPacket(
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Failed to save new MessageEntity \(nsError)")
Logger.data.error("Failed to save new MessageEntity \(nsError, privacy: .public)")
}
} catch {
Logger.data.error("Fetch Message To and From Users Error")
@ -1044,7 +1046,7 @@ func textMessageAppPacket(
func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.waypoint.received %@".localized, String(packet.from))
Logger.mesh.info("📍 \(logString)")
Logger.mesh.info("📍 \(logString, privacy: .public)")
let fetchWaypointRequest = WaypointEntity.fetchRequest()
fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id))
@ -1071,7 +1073,7 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
waypoint.created = Date()
do {
try context.save()
Logger.data.info("💾 Added Node Waypoint App Packet For: \(waypoint.id)")
Logger.data.info("💾 Added Node Waypoint App Packet For: \(waypoint.id, privacy: .public)")
let manager = LocalNotificationManager()
let icon = String(UnicodeScalar(Int(waypoint.icon)) ?? "📍")
let latitude = Double(waypoint.latitudeI) / 1e7
@ -1086,12 +1088,12 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
path: "meshtastic:///map?waypointid=\(waypoint.id)"
)
]
Logger.data.debug("meshtastic:///map?waypointid=\(waypoint.id)")
Logger.data.debug("meshtastic:///map?waypointid=\(waypoint.id, privacy: .public)")
manager.schedule()
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError)")
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError, privacy: .public)")
}
} else {
fetchedWaypoint[0].id = Int64(packet.id)
@ -1109,11 +1111,11 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
fetchedWaypoint[0].lastUpdated = Date()
do {
try context.save()
Logger.data.info("💾 Updated Node Waypoint App Packet For: \(fetchedWaypoint[0].id)")
Logger.data.info("💾 Updated Node Waypoint App Packet For: \(fetchedWaypoint[0].id, privacy: .public)")
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError)")
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError, privacy: .public)")
}
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24D70" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23231" systemVersion="23G80" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
@ -373,7 +373,8 @@
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="isRouter" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="isServer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>

View file

@ -38,6 +38,8 @@ struct MeshtasticAppleApp: App {
// Wire up router
self.appDelegate.router = appState.router
// Show Tips
try? Tips.resetDatastore()
}
var body: some Scene {
@ -55,7 +57,7 @@ struct MeshtasticAppleApp: App {
.presentationDragIndicator(.visible)
}
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
Logger.mesh.debug("URL received \(userActivity)")
Logger.mesh.debug("URL received \(userActivity, privacy: .public)")
self.incomingUrl = userActivity.webpageURL
if (self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/e/#")) != nil {
@ -72,18 +74,18 @@ struct MeshtasticAppleApp: App {
}
self.channelSettings = cs
}
Logger.services.debug("Add Channel \(self.addChannels)")
Logger.services.debug("Add Channel \(self.addChannels, privacy: .public)")
}
self.saveChannels = true
Logger.mesh.debug("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link")")
}
if self.saveChannels {
Logger.mesh.debug("User wants to open Channel Settings URL: \(String(describing: self.incomingUrl!.relativeString))")
Logger.mesh.debug("User wants to open Channel Settings URL: \(String(describing: self.incomingUrl!.relativeString), privacy: .public)")
}
}
.onOpenURL(perform: { (url) in
Logger.mesh.debug("Some sort of URL was received \(url)")
Logger.mesh.debug("Some sort of URL was received \(url, privacy: .public)")
self.incomingUrl = url
if url.absoluteString.lowercased().contains("meshtastic.org/e/#") {
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
@ -99,21 +101,15 @@ struct MeshtasticAppleApp: App {
}
self.channelSettings = cs
}
Logger.services.debug("Add Channel \(self.addChannels)")
Logger.services.debug("Add Channel \(self.addChannels, privacy: .public)")
}
self.saveChannels = true
Logger.mesh.debug("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link")")
Logger.mesh.debug("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link", privacy: .public)")
} else if url.absoluteString.lowercased().contains("meshtastic:///") {
appState.router.route(url: url)
}
})
.task {
#if DEBUG
/// Optionally, call `Tips.resetDatastore()` before `Tips.configure()` to reset the state of all tips. This will allow tips to re-appear even after they have been dismissed by the user.
/// This is for testing only, and should not be enabled in release builds.
try? Tips.resetDatastore()
#endif
try? Tips.configure(
[
// Reset which tips have been shown and what parameters have been tracked, useful during testing and for this sample project

View file

@ -97,10 +97,10 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
if let targetValue = userInfo["target"] as? String,
let deepLink = userInfo["path"] as? String,
let url = URL(string: deepLink) {
Logger.services.info("userNotificationCenter didReceiveResponse \(targetValue) \(deepLink)")
Logger.services.info("userNotificationCenter didReceiveResponse \(targetValue, privacy: .public) \(deepLink, privacy: .public)")
router?.route(url: url)
} else {
Logger.services.error("Failed to handle notification response: \(userInfo)")
Logger.services.error("Failed to handle notification response: \(userInfo, privacy: .public)")
}
completionHandler()
}

View file

@ -94,7 +94,6 @@ class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplacea
if let minimumSpan = aSeries.minumumYAxisSpan,
let currentRange = range[aSeries] {
let currentSpan = currentRange.upperBound - currentRange.lowerBound
//Logger.data.info("Updated \(aSeries.id) to \(range[aSeries] ?? 0...0) span=\(currentSpan)")
if currentSpan < minimumSpan {
// Calculate the center of the range
let centerOfRange = currentRange.lowerBound + (currentSpan / 2)
@ -118,7 +117,6 @@ class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplacea
return globalLower...globalUpper
}
// Collection conformance
typealias Index = Int
typealias Element = MetricsChartSeries

View file

@ -48,7 +48,7 @@ class PersistenceController {
if let error = error as NSError? {
Logger.data.error("CoreData Error: \(error.localizedDescription). Now attempting to truncate CoreData database. All app data will be lost.")
Logger.data.error("CoreData Error: \(error.localizedDescription, privacy: .public). Now attempting to truncate CoreData database. All app data will be lost.")
self.clearDatabase()
}
})
@ -65,11 +65,11 @@ class PersistenceController {
do {
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch let error {
Logger.data.error("Failed to re-create CoreData database: \(error.localizedDescription)")
Logger.data.error("Failed to re-create CoreData database: \(error.localizedDescription, privacy: .public)")
}
} catch let error {
Logger.data.error("Failed to destroy CoreData database, delete the app and re-install to clear data. Attempted to clear persistent store: \(error.localizedDescription)")
Logger.data.error("Failed to destroy CoreData database, delete the app and re-install to clear data. Attempted to clear persistent store: \(error.localizedDescription, privacy: .public)")
}
}
}

View file

@ -85,7 +85,7 @@ public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObje
}
try context.save()
} catch let error as NSError {
Logger.data.error("\(error.localizedDescription)")
Logger.data.error("\(error.localizedDescription, privacy: .public)")
}
}
@ -98,7 +98,7 @@ public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext
}
try context.save()
} catch let error as NSError {
Logger.data.error("\(error.localizedDescription)")
Logger.data.error("\(error.localizedDescription, privacy: .public)")
}
}
@ -122,7 +122,7 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext, includeRoutes
do {
try context.executeAndMergeChanges(using: deleteRequest)
} catch {
Logger.data.error("\(error.localizedDescription)")
Logger.data.error("\(error.localizedDescription, privacy: .public)")
}
}
}
@ -130,7 +130,7 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext, includeRoutes
func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.nodeinfo.received %@".localized, packet.from.toHex())
Logger.mesh.info("📟 \(logString)")
Logger.mesh.info("📟 \(logString, privacy: .public)")
guard packet.from > 0 else { return }
@ -313,7 +313,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.position.received %@".localized, String(packet.from))
Logger.mesh.info("📍 \(logString)")
Logger.mesh.info("📍 \(logString, privacy: .public)")
let fetchNodePositionRequest = NodeInfoEntity.fetchRequest()
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
@ -407,7 +407,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
func upsertBluetoothConfigPacket(config: Config.BluetoothConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.bluetooth.config %@".localized, String(nodeNum))
Logger.mesh.info("📶 \(logString)")
Logger.mesh.info("📶 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -451,7 +451,7 @@ func upsertBluetoothConfigPacket(config: Config.BluetoothConfig, nodeNum: Int64,
func upsertDeviceConfigPacket(config: Config.DeviceConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.device.config %@".localized, String(nodeNum))
Logger.mesh.info("📟 \(logString)")
Logger.mesh.info("📟 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -506,7 +506,7 @@ func upsertDeviceConfigPacket(config: Config.DeviceConfig, nodeNum: Int64, sessi
func upsertDisplayConfigPacket(config: Config.DisplayConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.display.config %@".localized, nodeNum.toHex())
Logger.data.info("🖥️ \(logString)")
Logger.data.info("🖥️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -551,19 +551,15 @@ func upsertDisplayConfigPacket(config: Config.DisplayConfig, nodeNum: Int64, ses
Logger.data.info("💾 [DisplayConfigEntity] Updated for node: \(nodeNum.toHex(), privacy: .public)")
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("💥 [DisplayConfigEntity] Error Updating Core Data: \(nsError, privacy: .public)")
}
} else {
Logger.data.error("💥 [DisplayConfigEntity] No Nodes found in local database matching node \(nodeNum.toHex()) unable to save Display Config")
Logger.data.error("💥 [DisplayConfigEntity] No Nodes found in local database matching node \(nodeNum.toHex(), privacy: .public) unable to save Display Config")
}
} catch {
let nsError = error as NSError
Logger.data.error("💥 [DisplayConfigEntity] Fetching node for core data failed: \(nsError, privacy: .public)")
}
@ -572,7 +568,7 @@ func upsertDisplayConfigPacket(config: Config.DisplayConfig, nodeNum: Int64, ses
func upsertLoRaConfigPacket(config: Config.LoRaConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.lora.config %@".localized, nodeNum.toHex())
Logger.data.info("📻 \(logString)")
Logger.data.info("📻 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", nodeNum)
@ -643,7 +639,7 @@ func upsertLoRaConfigPacket(config: Config.LoRaConfig, nodeNum: Int64, sessionPa
func upsertNetworkConfigPacket(config: Config.NetworkConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.network.config %@".localized, String(nodeNum))
Logger.data.info("🌐 \(logString)")
Logger.data.info("🌐 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -692,7 +688,7 @@ func upsertNetworkConfigPacket(config: Config.NetworkConfig, nodeNum: Int64, ses
func upsertPositionConfigPacket(config: Config.PositionConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.position.config %@".localized, String(nodeNum))
Logger.data.info("🗺️ \(logString)")
Logger.data.info("🗺️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -755,7 +751,7 @@ func upsertPositionConfigPacket(config: Config.PositionConfig, nodeNum: Int64, s
func upsertPowerConfigPacket(config: Config.PowerConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.power.config %@".localized, String(nodeNum))
Logger.data.info("🗺️ \(logString)")
Logger.data.info("🗺️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -807,7 +803,7 @@ func upsertPowerConfigPacket(config: Config.PowerConfig, nodeNum: Int64, session
func upsertSecurityConfigPacket(config: Config.SecurityConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.security.config %@".localized, String(nodeNum))
Logger.data.info("🛡️ \(logString)")
Logger.data.info("🛡️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -868,7 +864,7 @@ func upsertSecurityConfigPacket(config: Config.SecurityConfig, nodeNum: Int64, s
func upsertAmbientLightingModuleConfigPacket(config: ModuleConfig.AmbientLightingConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.ambientlighting.config %@".localized, String(nodeNum))
Logger.data.info("🏮 \(logString)")
Logger.data.info("🏮 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -921,7 +917,7 @@ func upsertAmbientLightingModuleConfigPacket(config: ModuleConfig.AmbientLightin
func upsertCannedMessagesModuleConfigPacket(config: ModuleConfig.CannedMessageConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.cannedmessage.config %@".localized, String(nodeNum))
Logger.data.info("🥫 \(logString)")
Logger.data.info("🥫 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -980,7 +976,7 @@ func upsertCannedMessagesModuleConfigPacket(config: ModuleConfig.CannedMessageCo
func upsertDetectionSensorModuleConfigPacket(config: ModuleConfig.DetectionSensorConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.detectionsensor.config %@".localized, String(nodeNum))
Logger.data.info("🕵️ \(logString)")
Logger.data.info("🕵️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1037,7 +1033,7 @@ func upsertDetectionSensorModuleConfigPacket(config: ModuleConfig.DetectionSenso
func upsertExternalNotificationModuleConfigPacket(config: ModuleConfig.ExternalNotificationConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.externalnotification.config %@".localized, String(nodeNum))
Logger.data.info("📣 \(logString)")
Logger.data.info("📣 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1106,7 +1102,7 @@ func upsertExternalNotificationModuleConfigPacket(config: ModuleConfig.ExternalN
func upsertPaxCounterModuleConfigPacket(config: ModuleConfig.PaxcounterConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.paxcounter.config %@".localized, String(nodeNum))
Logger.data.info("🧑‍🤝‍🧑 \(logString)")
Logger.data.info("🧑‍🤝‍🧑 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1148,7 +1144,7 @@ func upsertPaxCounterModuleConfigPacket(config: ModuleConfig.PaxcounterConfig, n
func upsertRtttlConfigPacket(ringtone: String, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.ringtone.config %@".localized, String(nodeNum))
Logger.data.info("⛰️ \(logString)")
Logger.data.info("⛰️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1188,7 +1184,7 @@ func upsertRtttlConfigPacket(ringtone: String, nodeNum: Int64, sessionPasskey: D
func upsertMqttModuleConfigPacket(config: ModuleConfig.MQTTConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.mqtt.config %@".localized, String(nodeNum))
Logger.data.info("🌉 \(logString)")
Logger.data.info("🌉 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1250,7 +1246,7 @@ func upsertMqttModuleConfigPacket(config: ModuleConfig.MQTTConfig, nodeNum: Int6
func upsertRangeTestModuleConfigPacket(config: ModuleConfig.RangeTestConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.rangetest.config %@".localized, String(nodeNum))
Logger.data.info("⛰️ \(logString)")
Logger.data.info("⛰️ \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1294,7 +1290,7 @@ func upsertRangeTestModuleConfigPacket(config: ModuleConfig.RangeTestConfig, nod
func upsertSerialModuleConfigPacket(config: ModuleConfig.SerialConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.serial.config %@".localized, String(nodeNum))
Logger.data.info("🤖 \(logString)")
Logger.data.info("🤖 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1349,7 +1345,7 @@ func upsertSerialModuleConfigPacket(config: ModuleConfig.SerialConfig, nodeNum:
func upsertStoreForwardModuleConfigPacket(config: ModuleConfig.StoreForwardConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.storeforward.config %@".localized, String(nodeNum))
Logger.data.info("📬 \(logString)")
Logger.data.info("📬 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -1365,6 +1361,7 @@ func upsertStoreForwardModuleConfigPacket(config: ModuleConfig.StoreForwardConfi
newConfig.records = Int32(config.records)
newConfig.historyReturnMax = Int32(config.historyReturnMax)
newConfig.historyReturnWindow = Int32(config.historyReturnWindow)
newConfig.isRouter = config.isServer
fetchedNode[0].storeForwardConfig = newConfig
} else {
fetchedNode[0].storeForwardConfig?.enabled = config.enabled
@ -1397,7 +1394,7 @@ func upsertStoreForwardModuleConfigPacket(config: ModuleConfig.StoreForwardConfi
func upsertTelemetryModuleConfigPacket(config: ModuleConfig.TelemetryConfig, nodeNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat("mesh.log.telemetry.config %@".localized, String(nodeNum))
Logger.data.info("📈 \(logString)")
Logger.data.info("📈 \(logString, privacy: .public)")
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))

View file

@ -44,7 +44,7 @@ class Router: ObservableObject {
} else if components.path.hasPrefix("/settings") {
routeSettings(components)
} else {
Logger.services.warning("Failed to route url: \(url)")
Logger.services.warning("Failed to route url: \(url, privacy: .public)")
}
}

View file

@ -13,10 +13,10 @@ struct BluetoothConnectionTip: Tip {
return "tip.bluetooth.connect"
}
var title: Text {
Text("tip.bluetooth.connect.title")
Text("Connected Radio")
}
var message: Text? {
Text("tip.bluetooth.connect.message")
Text("Shows information for the Lora radio connected via bluetooth. You can swipe left to disconnect the radio and long press start the live activity.")
}
var image: Image? {
Image(systemName: "flipphone")

View file

@ -35,7 +35,7 @@ struct Connect: View {
if success {
Logger.services.info("Notifications are all set!")
} else if let error = error {
Logger.services.error("\(error.localizedDescription)")
Logger.services.error("\(error.localizedDescription, privacy: .public)")
}
}
}
@ -324,7 +324,7 @@ struct Connect: View {
isUnsetRegion = false
}
} catch {
Logger.data.error("💥 Error fetching node info: \(error.localizedDescription)")
Logger.data.error("💥 Error fetching node info: \(error.localizedDescription, privacy: .public)")
}
}
}
@ -361,7 +361,7 @@ struct Connect: View {
pushType: nil)
Logger.services.info("Requested MyActivity live activity. ID: \(myActivity.id)")
} catch {
Logger.services.error("Error requesting live activity: \(error.localizedDescription)")
Logger.services.error("Error requesting live activity: \(error.localizedDescription, privacy: .public)")
}
}

View file

@ -64,7 +64,7 @@ struct LocalWeatherConditions: View {
attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL
}
} catch {
Logger.services.error("Could not gather weather information: \(error.localizedDescription)")
Logger.services.error("Could not gather weather information: \(error.localizedDescription, privacy: .public)")
condition = .clear
symbolName = "cloud.fill"
}

View file

@ -35,7 +35,7 @@ struct NodeWeatherForecastView: View {
)
})
} catch {
Logger.services.error("Could not load weather: \(error.localizedDescription)")
Logger.services.error("Could not load weather: \(error.localizedDescription, privacy: .public)")
}
}
}

View file

@ -1,64 +0,0 @@
//
//// MapButtons.swift
//// Meshtastic
////
//// Copyright © Garth Vander Houwen 4/23/23.
////
//
//import SwiftUI
//
//struct MapButtons: View {
// let buttonWidth: CGFloat = 22
// let width: CGFloat = 45
// @Binding var tracking: UserTrackingModes
// @Binding var isPresentingInfoSheet: Bool
// var body: some View {
// VStack {
// let impactLight = UIImpactFeedbackGenerator(style: .light)
// Button(action: {
// self.isPresentingInfoSheet.toggle()
// }) {
// Image(systemName: isPresentingInfoSheet ? "info.circle.fill" : "info.circle")
// .resizable()
// .frame(width: buttonWidth, height: buttonWidth, alignment: .center)
// .offset(y: -2)
// }
// Divider()
// Button(action: {
// switch self.tracking {
// case .none:
// self.tracking = .follow
// case .follow:
// self.tracking = .followWithHeading
// case .followWithHeading:
// self.tracking = .none
// }
// impactLight.impactOccurred()
// }) {
// Image(systemName: tracking.icon)
// .frame(width: buttonWidth, height: buttonWidth, alignment: .center)
// .offset(y: 3)
// }
// }
// .frame(width: width, height: width*2, alignment: .center)
// .background(Color(UIColor.systemBackground))
// .cornerRadius(8)
// .shadow(radius: 1)
// .offset(x: 3, y: 25)
// }
//}
//
//// MARK: Previews
//// struct MapControl_Previews: PreviewProvider {
//// @State static var tracking: UserTrackingModes = .none
//// @State static var isPresentingInfoSheet = false
//// static var previews: some View {
//// Group {
//// MapButtons(tracking: $tracking, isPresentingInfoSheet: $isPresentingInfoSheet)
//// .environment(\.colorScheme, .light)
//// MapButtons(tracking: $tracking, isPresentingInfoSheet: $isPresentingInfoSheet)
//// .environment(\.colorScheme, .dark)
//// }
//// .previewLayout(.fixed(width: 60, height: 100))
//// }
//// }

View file

@ -1,37 +0,0 @@
//
// MapViewFitExtension.swift
// Meshtastic
//
// Created by Garth Vander Houwen on 1/15/23.
//
import MapKit
extension MKMapView {
func fitAllAnnotations(with padding: UIEdgeInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)) {
var zoomRect: MKMapRect = .null
annotations.forEach({
let annotationPoint = MKMapPoint($0.coordinate)
let pointRect = MKMapRect(x: annotationPoint.x, y: annotationPoint.y, width: 0.01, height: 0.01)
zoomRect = zoomRect.union(pointRect)
})
setVisibleMapRect(zoomRect, edgePadding: padding, animated: true)
}
func fit(annotations: [MKAnnotation], andShow show: Bool, with padding: UIEdgeInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)) {
var zoomRect: MKMapRect = .null
annotations.forEach({
let aPoint = MKMapPoint($0.coordinate)
let rect = MKMapRect(x: aPoint.x, y: aPoint.y, width: 0.1, height: 0.1)
zoomRect = zoomRect.isNull ? rect : zoomRect.union(rect)
})
if show {
addAnnotations(annotations)
}
setVisibleMapRect(zoomRect, edgePadding: padding, animated: true)
}
}

View file

@ -1,434 +0,0 @@
////
//// MapViewSwitUI.swift
//// Meshtastic
////
//// Copyright(c) Josh Pirihi & Garth Vander Houwen 1/16/22.
//
//import Foundation
//import SwiftUI
//import MapKit
//import OSLog
//
//struct PolygonInfo: Codable {
// let stroke: String?
// let strokeWidth, strokeOpacity: Int?
// let fill: String?
// let fillOpacity: Double?
// let title, subtitle: String?
//}
//
//func degreesToRadians(_ number: Double) -> Double {
// return number * .pi / 180
//}
//var currentMapLayer: MapLayer?
//
//struct MapViewSwiftUI: UIViewRepresentable {
// var onLongPress: (_ waypointCoordinate: CLLocationCoordinate2D) -> Void
// var onWaypointEdit: (_ waypointId: Int ) -> Void
// let mapView = MKMapView()
// // Parameters
// let selectedMapLayer: MapLayer
// let selectedWeatherLayer: MapOverlayServer = UserDefaults.mapOverlayServer
// let positions: [PositionEntity]
// let waypoints: [WaypointEntity]
// let userTrackingMode: MKUserTrackingMode
// let showNodeHistory: Bool
// let showRouteLines: Bool
// let mapViewType: MKMapType = MKMapType.standard
// // Offline Map Tiles
// @AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0
// @State private var loadedLastUpdatedLocalMapFile = 0
// var customMapOverlay: CustomMapOverlay?
// @State private var presentCustomMapOverlayHash: CustomMapOverlay?
// // MARK: Private methods
// private func configureMap(mapView: MKMapView) {
// // Map View Parameters
// mapView.mapType = mapViewType
// mapView.addAnnotations(waypoints)
// // Do the initial map centering
// let latest = positions
// .filter { $0.latest == true }
// .sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 }
// let span = MKCoordinateSpan(latitudeDelta: 0.003, longitudeDelta: 0.003)
// let center = (latest.count > 0 && userTrackingMode == MKUserTrackingMode.none) ? latest[0].coordinate : LocationHelper.currentLocation
// let region = MKCoordinateRegion(center: center, span: span)
// mapView.addAnnotations(showNodeHistory ? positions : latest)
// mapView.setRegion(region, animated: true)
// // Set user (phone gps) tracking options
// mapView.setUserTrackingMode(userTrackingMode, animated: true)
// if userTrackingMode == MKUserTrackingMode.none {
// if latest.count == 1 {
// mapView.fit(annotations: showNodeHistory ? positions: latest, andShow: false)
// } else {
// mapView.fitAllAnnotations()
// }
// mapView.showsUserLocation = false
// } else {
// mapView.showsUserLocation = true
// }
// // Other MKMapView Settings
// mapView.preferredConfiguration.elevationStyle = .realistic// .flat
// mapView.pointOfInterestFilter = MKPointOfInterestFilter.excludingAll
// mapView.isPitchEnabled = true
// mapView.isRotateEnabled = true
// mapView.isScrollEnabled = true
// mapView.isZoomEnabled = true
// mapView.showsBuildings = true
// mapView.showsScale = true
// mapView.showsTraffic = true
//
// mapView.showsCompass = false
// let compass = MKCompassButton(mapView: mapView)
// compass.translatesAutoresizingMaskIntoConstraints = false
// #if targetEnvironment(macCatalyst)
// // Show the default always visible compass and the mac only controls
// compass.compassVisibility = .visible
// mapView.addSubview(compass)
// mapView.showsZoomControls = true
// mapView.showsPitchControl = true
// compass.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -115).isActive = true
// compass.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -5).isActive = true
// #else
// compass.compassVisibility = .adaptive
// mapView.addSubview(compass)
// compass.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -5).isActive = true
// compass.topAnchor.constraint(equalTo: mapView.topAnchor, constant: 145).isActive = true
// #endif
// }
// private func setMapBaseLayer(mapView: MKMapView) {
// // Avoid refreshing UI if selectedLayer has not changed
// guard currentMapLayer != selectedMapLayer else { return }
// currentMapLayer = selectedMapLayer
// for overlay in mapView.overlays where overlay is MKTileOverlay {
// mapView.removeOverlay(overlay)
// }
// switch selectedMapLayer {
// case .offline:
// mapView.mapType = .standard
// let overlay = TileOverlay()
// overlay.canReplaceMapContent = false
// overlay.minimumZ = UserDefaults.mapTileServer.zoomRange.startIndex
// overlay.maximumZ = UserDefaults.mapTileServer.zoomRange.endIndex
// mapView.addOverlay(overlay, level: UserDefaults.mapTilesAboveLabels ? .aboveLabels : .aboveRoads)
// case .satellite:
// mapView.mapType = .satellite
// case .hybrid:
// mapView.mapType = .hybrid
// default:
// mapView.mapType = .standard
// }
// }
// private func setMapOverlays(mapView: MKMapView) {
// // Weather radar
// if UserDefaults.enableOverlayServer {
// let locale = Locale.current
// if locale.region?.identifier ?? "no locale" == "US" {
// let overlay = MKTileOverlay(urlTemplate: selectedWeatherLayer.tileUrl)
// overlay.canReplaceMapContent = false
// overlay.minimumZ = selectedWeatherLayer.zoomRange.startIndex
// overlay.maximumZ = selectedWeatherLayer.zoomRange.endIndex
// mapView.addOverlay(overlay, level: .aboveLabels)
// }
// }
// }
//
// func makeUIView(context: Context) -> MKMapView {
// currentMapLayer = nil
// mapView.delegate = context.coordinator
// self.configureMap(mapView: mapView)
// return mapView
// }
// func updateUIView(_ mapView: MKMapView, context: Context) {
// // Set selected map base layer
// setMapBaseLayer(mapView: mapView)
// // Set map tile server and weather overlay layers
// setMapOverlays(mapView: mapView)
// let latest = positions
// .filter { $0.latest == true }
// .sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 }
// // Node Route Lines
// if showRouteLines {
// // Remove all existing PolyLine Overlays
// for overlay in mapView.overlays where overlay is MKPolyline {
// mapView.removeOverlay(overlay)
// }
// var lineIndex = 0
// for position in latest {
// let nodePositions = positions.filter { $0.nodeCoordinate != nil && $0.nodePosition?.num ?? 0 == position.nodePosition?.num ?? -1 }
// let lineCoords = nodePositions.compactMap({(position) -> CLLocationCoordinate2D in
// return position.nodeCoordinate ?? LocationHelper.DefaultLocation
// })
// let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count)
// polyline.title = "\(String(position.nodePosition?.num ?? 0))"
// mapView.addOverlay(polyline, level: .aboveLabels)
// lineIndex += 1
// // There are 18 colors for lines, start over if we are at index 17
// if lineIndex > 17 {
// lineIndex = 0
// }
// }
// } else {
// // Remove all existing PolyLine Overlays
// for overlay in mapView.overlays where overlay is MKPolyline {
// mapView.removeOverlay(overlay)
// }
// }
// let annotationCount = waypoints.count + (showNodeHistory ? positions.count : latest.count)
// if annotationCount != mapView.annotations.count {
// Logger.services.info("Annotation Count: \(annotationCount) Map Annotations: \(mapView.annotations.count)")
// mapView.removeAnnotations(mapView.annotations)
// mapView.addAnnotations(waypoints)
// }
// mapView.addAnnotations(showNodeHistory ? positions : latest)
// if userTrackingMode == MKUserTrackingMode.none {
// mapView.showsUserLocation = false
// if UserDefaults.enableMapRecentering {
// if latest.count == 1 {
// mapView.fit(annotations: showNodeHistory ? positions : latest, andShow: true)
// } else {
// mapView.fitAllAnnotations()
// }
// }
// } else {
// mapView.showsUserLocation = true
// }
// mapView.setUserTrackingMode(userTrackingMode, animated: true)
// }
// func makeCoordinator() -> MapCoordinator {
// return Coordinator(self)
// }
// final class MapCoordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
// var parent: MapViewSwiftUI
// var longPressRecognizer = UILongPressGestureRecognizer()
// init(_ parent: MapViewSwiftUI) {
// self.parent = parent
// super.init()
// self.longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressHandler))
// self.longPressRecognizer.minimumPressDuration = 0.5
// self.longPressRecognizer.cancelsTouchesInView = true
// self.longPressRecognizer.delegate = self
// self.parent.mapView.addGestureRecognizer(longPressRecognizer)
// }
// func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// switch annotation {
// case let positionAnnotation as PositionEntity:
// let reuseID = String(positionAnnotation.nodePosition?.num ?? 0) + "-" + String(positionAnnotation.time?.timeIntervalSince1970 ?? 0)
// let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "node") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseID )
// annotationView.tag = -1
// annotationView.canShowCallout = true
// if positionAnnotation.latest {
// annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)).darker()
// annotationView.displayPriority = .required
// annotationView.titleVisibility = .visible
// } else {
// annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)).lighter()
// annotationView.displayPriority = .defaultHigh
// annotationView.titleVisibility = .adaptive
// }
// annotationView.tag = -1
// annotationView.canShowCallout = true
// annotationView.titleVisibility = .adaptive
// let leftIcon = UIImageView(image: annotationView.glyphText?.image())
// leftIcon.backgroundColor = UIColor(.indigo)
// annotationView.leftCalloutAccessoryView = leftIcon
// let subtitle = UILabel()
// subtitle.text = "Long Name: \(positionAnnotation.nodePosition?.user?.longName ?? "Unknown") \n"
// subtitle.text? += "Latitude: \(String(format: "%.5f", positionAnnotation.coordinate.latitude)) \n"
// subtitle.text! += "Longitude: \(String(format: "%.5f", positionAnnotation.coordinate.longitude)) \n"
// let distanceFormatter = MKDistanceFormatter()
// subtitle.text! += "Altitude: \(distanceFormatter.string(fromDistance: Double(positionAnnotation.altitude))) \n"
// if positionAnnotation.nodePosition?.metadata != nil {
// if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.client ||
// DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.clientMute ||
// DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.routerClient {
// annotationView.glyphImage = UIImage(systemName: "flipphone")
// } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.repeater {
// annotationView.glyphImage = UIImage(systemName: "repeat")
// } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.router {
// annotationView.glyphImage = UIImage(systemName: "wifi.router.fill")
// } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.tracker {
// annotationView.glyphImage = UIImage(systemName: "location.viewfinder")
// } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.sensor {
// annotationView.glyphImage = UIImage(systemName: "sensor")
// }
// let pf = PositionFlags(rawValue: Int(positionAnnotation.nodePosition?.metadata?.positionFlags ?? 3))
// if pf.contains(.Satsinview) {
// subtitle.text! += "Sats in view: \(String(positionAnnotation.satsInView)) \n"
// }
// if pf.contains(.SeqNo) {
// subtitle.text! += "Sequence: \(String(positionAnnotation.seqNo)) \n"
// }
// if pf.contains(.Heading) {
// if parent.userTrackingMode != MKUserTrackingMode.followWithHeading {
// annotationView.glyphImage = UIImage(systemName: "location.north.fill")?.rotate(radians: Float(degreesToRadians(Double(positionAnnotation.heading))))
// subtitle.text! += "Heading: \(String(positionAnnotation.heading)) \n"
// } else {
// annotationView.glyphImage = UIImage(systemName: "flipphone")
// }
// }
// if pf.contains(.Speed) {
// let formatter = MeasurementFormatter()
// formatter.locale = Locale.current
// if positionAnnotation.speed <= 1 {
// annotationView.glyphImage = UIImage(systemName: "hexagon")
// }
// subtitle.text! += "Speed: \(formatter.string(from: Measurement(value: Double(positionAnnotation.speed), unit: UnitSpeed.kilometersPerHour))) \n"
// }
// } else {
// // node metadata is nil
// annotationView.glyphImage = UIImage(systemName: "flipphone")
// }
// if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
// let metersAway = positionAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
// subtitle.text! += "distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
// }
// subtitle.text! += positionAnnotation.time?.formatted() ?? "Unknown \n"
// subtitle.numberOfLines = 0
// annotationView.detailCalloutAccessoryView = subtitle
// let detailsIcon = UIButton(type: .detailDisclosure)
// detailsIcon.setImage(UIImage(systemName: "trash"), for: .normal)
// annotationView.rightCalloutAccessoryView = detailsIcon
// return annotationView
// case let waypointAnnotation as WaypointEntity:
// let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "waypoint") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: String(waypointAnnotation.id))
// annotationView.tag = Int(waypointAnnotation.id)
// annotationView.isEnabled = true
// annotationView.canShowCallout = true
// if waypointAnnotation.icon == 0 {
// annotationView.glyphText = "📍"
// } else {
// annotationView.glyphText = String(UnicodeScalar(Int(waypointAnnotation.icon)) ?? "📍")
// }
// annotationView.markerTintColor = UIColor(.accentColor)
// annotationView.displayPriority = .required
// annotationView.titleVisibility = .adaptive
// let leftIcon = UIImageView(image: annotationView.glyphText?.image())
// leftIcon.backgroundColor = UIColor(.accentColor)
// annotationView.leftCalloutAccessoryView = leftIcon
// let subtitle = UILabel()
// if waypointAnnotation.longDescription?.count ?? 0 > 0 {
// subtitle.text = (waypointAnnotation.longDescription ?? "") + "\n"
// } else {
// subtitle.text = ""
// }
// if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
// let metersAway = waypointAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
// let distanceFormatter = MKDistanceFormatter()
// subtitle.text! += "distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
// }
// if waypointAnnotation.created != nil {
// subtitle.text! += "Created: \(waypointAnnotation.created?.formatted() ?? "Unknown") \n"
// }
// if waypointAnnotation.lastUpdated != nil {
// subtitle.text! += "Updated: \(waypointAnnotation.lastUpdated?.formatted() ?? "Unknown") \n"
// }
// if waypointAnnotation.expire != nil {
// subtitle.text! += "Expires: \(waypointAnnotation.expire?.formatted() ?? "Unknown") \n"
// }
// subtitle.numberOfLines = 0
// annotationView.detailCalloutAccessoryView = subtitle
// let editIcon = UIButton(type: .detailDisclosure)
// editIcon.setImage(UIImage(systemName: "square.and.pencil"), for: .normal)
// annotationView.rightCalloutAccessoryView = editIcon
// return annotationView
// default: return nil
// }
// }
// func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// switch view.annotation {
// case _ as WaypointEntity:
// // Only Allow Edit for waypoint annotations with a id
// if view.tag > 0 {
// parent.onWaypointEdit(view.tag)
// }
// default: break
// }
// }
// @objc func longPressHandler(_ gesture: UILongPressGestureRecognizer) {
// if gesture.state != UIGestureRecognizer.State.ended {
// return
// } else if gesture.state != UIGestureRecognizer.State.began {
// // Screen Position - CGPoint
// let location = longPressRecognizer.location(in: self.parent.mapView)
// // Map Coordinate - CLLocationCoordinate2D
// let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
// let annotation = MKPointAnnotation()
// annotation.title = "Dropped Pin"
// annotation.coordinate = coordinate
// parent.mapView.addAnnotation(annotation)
// UINotificationFeedbackGenerator().notificationOccurred(.success)
// parent.onLongPress(coordinate)
// }
// }
// public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
// if let tileOverlay = overlay as? MKTileOverlay {
// return MKTileOverlayRenderer(tileOverlay: tileOverlay)
// } else {
// if let routePolyline = overlay as? MKPolyline {
// let titleString = routePolyline.title ?? "0"
// let renderer = MKPolylineRenderer(polyline: routePolyline)
// renderer.strokeColor = UIColor(hex: UInt32(titleString) ?? 0).lighter()
// renderer.lineWidth = 8
// return renderer
// }
// if let polygon = overlay as? MKPolygon {
// let renderer = MKPolygonRenderer(polygon: polygon)
// renderer.fillColor = UIColor.purple.withAlphaComponent(0.2)
// renderer.strokeColor = .purple.withAlphaComponent(0.7)
// return renderer
// }
// return MKOverlayRenderer(overlay: overlay)
// }
// }
// }
// /// is supposed to be located in the folder with the map name
// public struct DefaultTile: Hashable {
// let tileName: String
// let tileType: String
// public init(tileName: String, tileType: String) {
// self.tileName = tileName
// self.tileType = tileType
// }
// }
// public struct CustomMapOverlay: Equatable, Hashable {
// let mapName: String
// let tileType: String
// var canReplaceMapContent: Bool
// var minimumZoomLevel: Int?
// var maximumZoomLevel: Int?
// let defaultTile: DefaultTile?
// public init(
// mapName: String,
// tileType: String,
// canReplaceMapContent: Bool = true, // false for transparent tiles
// minimumZoomLevel: Int? = nil,
// maximumZoomLevel: Int? = nil,
// defaultTile: DefaultTile? = nil
// ) {
// self.mapName = mapName
// self.tileType = tileType
// self.canReplaceMapContent = canReplaceMapContent
// self.minimumZoomLevel = minimumZoomLevel
// self.maximumZoomLevel = maximumZoomLevel
// self.defaultTile = defaultTile
// }
// public init?(
// mapName: String?,
// tileType: String,
// canReplaceMapContent: Bool = true, // false for transparent tiles
// minimumZoomLevel: Int? = nil,
// maximumZoomLevel: Int? = nil,
// defaultTile: DefaultTile? = nil
// ) {
// if mapName == nil || mapName! == "" {
// return nil
// }
// self.mapName = mapName!
// self.tileType = tileType
// self.canReplaceMapContent = canReplaceMapContent
// self.minimumZoomLevel = minimumZoomLevel
// self.maximumZoomLevel = maximumZoomLevel
// self.defaultTile = defaultTile
// }
// }
//}

View file

@ -1,14 +0,0 @@
import SwiftUI
struct TileDownloadStatus: View {
@ObservedObject var tileManager = OfflineTileManager.shared
var body: some View {
if tileManager.status == .downloading {
Image(systemName: "arrow.down.circle.fill")
.foregroundColor(.gray)
} else {
EmptyView()
}
}
}

View file

@ -1,164 +0,0 @@
////
//// NodeMapControl.swift
//// Meshtastic
////
//// Created by Garth Vander Houwen on 9/9/23.
////
//import SwiftUI
//import CoreLocation
//import MapKit
//import WeatherKit
//import OSLog
//
//struct NodeMapMapkit: View {
//
// @Environment(\.managedObjectContext) var context
// @EnvironmentObject var bleManager: BLEManager
// /// Weather
// /// The current weather condition for the city.
// @State private var condition: WeatherCondition?
// @State private var temperature: Measurement<UnitTemperature>?
// @State private var humidity: Int?
// @State private var symbolName: String = "cloud.fill"
// @State private var attributionLink: URL?
// @State private var attributionLogo: URL?
//
// @Environment(\.colorScheme) var colorScheme: ColorScheme
// @AppStorage("meshMapType") private var meshMapType = 0
// @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false
// @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false
// @State private var selectedMapLayer: MapLayer = .standard
// @State var waypointCoordinate: WaypointCoordinate?
// @State var editingWaypoint: Int = 0
// @State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay(
// mapName: "offlinemap",
// tileType: "png",
// canReplaceMapContent: true
// )
// @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
// predicate: NSPredicate(
// format: "expire == nil || expire >= %@", Date() as NSDate
// ), animation: .none)
// private var waypoints: FetchedResults<WaypointEntity>
// @ObservedObject var node: NodeInfoEntity
//
// var body: some View {
//
// NavigationStack {
// GeometryReader { bounds in
// VStack {
// if node.hasPositions {
// ZStack {
// let positionArray = node.positions?.array as? [PositionEntity] ?? []
// let lastTenThousand = Array(positionArray.prefix(10000))
// // let todaysPositions = positionArray.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) }
// ZStack {
// MapViewSwiftUI(onLongPress: { coord in
// waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: coord, waypointId: 0)
// }, onWaypointEdit: { wpId in
// if wpId > 0 {
// waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: nil, waypointId: Int64(wpId))
// }
// },
// selectedMapLayer: selectedMapLayer,
// positions: lastTenThousand,
// waypoints: Array(waypoints),
// userTrackingMode: MKUserTrackingMode.none,
// showNodeHistory: meshMapShowNodeHistory,
// showRouteLines: meshMapShowRouteLines,
// customMapOverlay: self.customMapOverlay
// )
// VStack(alignment: .leading) {
// Spacer()
// HStack(alignment: .bottom, spacing: 1) {
// Picker("Map Type", selection: $selectedMapLayer) {
// ForEach(MapLayer.allCases, id: \.self) { layer in
// if layer == MapLayer.offline && UserDefaults.enableOfflineMaps {
// Text(layer.localized)
// } else if layer != MapLayer.offline {
// Text(layer.localized)
// }
// }
// }
// .onChange(of: (selectedMapLayer)) { newMapLayer in
// UserDefaults.mapLayer = newMapLayer
// }
// .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
// .pickerStyle(.menu)
// .padding(5)
// VStack {
// VStack {
// Label(temperature?.formatted(.measurement(width: .narrow)) ?? "??", systemImage: symbolName)
// .font(.caption)
//
// Label("\(humidity ?? 0)%", systemImage: "humidity")
// .font(.caption2)
//
// AsyncImage(url: attributionLogo) { image in
// image
// .resizable()
// .scaledToFit()
// } placeholder: {
// ProgressView()
// .controlSize(.mini)
// }
// .frame(height: 10)
//
// Link("Other data sources", destination: attributionLink ?? URL(string: "https://weather-data.apple.com/legal-attribution.html")!)
// .font(.caption2)
// }
// .padding(5)
//
// }
// .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
// .padding(5)
// .task {
// do {
// if node.hasPositions {
// let mostRecent = node.positions?.lastObject as? PositionEntity
// let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude))
// condition = weather.currentWeather.condition
// temperature = weather.currentWeather.temperature
// humidity = Int(weather.currentWeather.humidity * 100)
// symbolName = weather.currentWeather.symbolName
// let attribution = try await WeatherService.shared.attribution
// attributionLink = attribution.legalPageURL
// attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL
// }
// } catch {
// Logger.services.error("Could not gather weather information: \(error.localizedDescription)")
// condition = .clear
// symbolName = "cloud.fill"
// }
// }
// }
// }
// }
// .ignoresSafeArea(.all, edges: [.top, .leading, .trailing])
// .frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.65)
// }
// } else {
// HStack {
// }
// .padding([.top], 20)
// }
// }
// .edgesIgnoringSafeArea([.leading, .trailing])
// .sheet(item: $waypointCoordinate, content: { wpc in
// WaypointFormMapKit(coordinate: wpc)
// .presentationDetents([.medium, .large])
// .presentationDragIndicator(.automatic)
// })
// .navigationBarTitle(String(node.user?.longName ?? "unknown".localized), displayMode: .inline)
// .navigationBarItems(trailing:
// ZStack {
// ConnectedDevice(
// bluetoothOn: bleManager.isSwitchedOn,
// deviceConnected: bleManager.connectedPeripheral != nil,
// name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
// })
// }
// .padding(.bottom, 2)
// }
// }
//}

View file

@ -1,266 +0,0 @@
////
//// WaypointFormView.swift
//// Meshtastic
////
//// Copyright Garth Vander Houwen 1/10/23.
////
//
//import CoreLocation
//import MeshtasticProtobufs
//import OSLog
//import SwiftUI
//
//struct WaypointFormMapKit: View {
//
// @EnvironmentObject var bleManager: BLEManager
// @Environment(\.dismiss) private var dismiss
// @State var coordinate: WaypointCoordinate
// @FocusState private var iconIsFocused: Bool
// @State private var name: String = ""
// @State private var description: String = ""
// @State private var icon: String = "📍"
// @State private var latitude: Double = 0
// @State private var longitude: Double = 0
// @State private var expires: Bool = false
// @State private var expire: Date = Date.now.addingTimeInterval(60 * 480) // 1 minute * 480 = 8 Hours
// @State private var locked: Bool = false
// @State private var lockedTo: Int64 = 0
//
// var body: some View {
//
// Form {
// let distance = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude).distance(from: CLLocation(latitude: coordinate.coordinate?.latitude ?? 0, longitude: coordinate.coordinate?.longitude ?? 0))
// Section(header: Text((coordinate.waypointId > 0) ? "Editing Waypoint" : "Create Waypoint")) {
// HStack {
// Text("Location: \(String(format: "%.5f", latitude) + "," + String(format: "%.5f", longitude))")
// .textSelection(.enabled)
// .foregroundColor(Color.gray)
// .font(.caption2)
// if coordinate.coordinate?.latitude ?? 0 != 0 && coordinate.coordinate?.longitude ?? 0 != 0 {
// DistanceText(meters: distance)
// .foregroundColor(Color.gray)
// .font(.caption2)
// }
// }
// HStack {
// Text("Name")
// Spacer()
// TextField(
// "Name",
// text: $name,
// axis: .vertical
// )
// .foregroundColor(Color.gray)
// .onChange(of: name) {
// var totalBytes = name.utf8.count
// // Only mess with the value if it is too big
// while totalBytes > 30 {
// name = String(name.dropLast())
// totalBytes = name.utf8.count
// }
// if totalBytes > 30 {
// name = String(name.dropLast())
// }
// }
// }
// HStack {
// Text("Description")
// Spacer()
// TextField(
// "Description",
// text: $description,
// axis: .vertical
// )
// .foregroundColor(Color.gray)
// .onChange(of: description) {
// var totalBytes = description.utf8.count
// // Only mess with the value if it is too big
// while totalBytes > 100 {
// description = String(description.dropLast())
// totalBytes = description.utf8.count
// }
// }
// }
// HStack {
// Text("Icon")
// Spacer()
// EmojiOnlyTextField(text: $icon, placeholder: "Select an emoji")
// .font(.title)
// .focused($iconIsFocused)
// .onChange(of: icon) { _, value in
//
// // If you have anything other than emojis in your string make it empty
// if !value.onlyEmojis() {
// icon = ""
// }
// // If a second emoji is entered delete the first one
// if value.count >= 1 {
//
// if value.count > 1 {
// let index = value.index(value.startIndex, offsetBy: 1)
// icon = String(value[index])
// }
// iconIsFocused = false
// }
// }
//
// }
// Toggle(isOn: $expires) {
// Label("Expires", systemImage: "clock.badge.xmark")
// }
// .toggleStyle(SwitchToggleStyle(tint: .accentColor))
// if expires {
// DatePicker("Expire", selection: $expire, in: Date.now...)
// .datePickerStyle(.compact)
// .font(.callout)
// }
// Toggle(isOn: $locked) {
// Label("Locked", systemImage: "lock")
// }
// .toggleStyle(SwitchToggleStyle(tint: .accentColor))
// }
// }
// HStack {
// Button {
//
// var newWaypoint = Waypoint()
// // Loading a waypoint from edit
// if coordinate.waypointId > 0 {
// newWaypoint.id = UInt32(coordinate.waypointId)
// let waypoint = getWaypoint(id: Int64(coordinate.waypointId), context: bleManager.context)
// newWaypoint.latitudeI = waypoint.latitudeI
// newWaypoint.longitudeI = waypoint.longitudeI
// } else {
// // New waypoint
// newWaypoint.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
// newWaypoint.latitudeI = Int32(Double(coordinate.coordinate?.latitude ?? 0) * 1e7)
// newWaypoint.longitudeI = Int32(Double(coordinate.coordinate?.longitude ?? 0) * 1e7)
// }
// newWaypoint.name = name.count > 0 ? name : "Dropped Pin"
// newWaypoint.description_p = description
// // Unicode scalar value for the icon emoji string
// let unicodeScalers = icon.unicodeScalars
// // First element as an UInt32
// let unicode = unicodeScalers[unicodeScalers.startIndex].value
// newWaypoint.icon = unicode
// if locked {
// if lockedTo == 0 {
// newWaypoint.lockedTo = UInt32(bleManager.connectedPeripheral!.num)
// } else {
// newWaypoint.lockedTo = UInt32(lockedTo)
// }
// }
// if expires {
// newWaypoint.expire = UInt32(expire.timeIntervalSince1970)
// } else {
// newWaypoint.expire = 0
// }
// if bleManager.sendWaypoint(waypoint: newWaypoint) {
// dismiss()
// } else {
// dismiss()
// Logger.mesh.error("Send waypoint failed")
// }
// } label: {
// Label("Send", systemImage: "arrow.up")
// }
// .buttonStyle(.bordered)
// .buttonBorderShape(.capsule)
// .controlSize(.regular)
// .disabled(bleManager.connectedPeripheral == nil)
// .padding(.bottom)
//
// Button(role: .cancel) {
// dismiss()
// } label: {
// Label("cancel", systemImage: "x.circle")
// }
// .buttonStyle(.bordered)
// .buttonBorderShape(.capsule)
// .controlSize(.regular)
// .padding(.bottom)
//
// if coordinate.waypointId > 0 {
//
// Menu {
// Button("For me", action: {
// let waypoint = getWaypoint(id: Int64(coordinate.waypointId), context: bleManager.context)
// bleManager.context.delete(waypoint)
// do {
// try bleManager.context.save()
// } catch {
// bleManager.context.rollback()
// }
// dismiss() })
// Button("For everyone", action: {
// var newWaypoint = Waypoint()
//
// if coordinate.waypointId > 0 {
// newWaypoint.id = UInt32(coordinate.waypointId)
// }
// newWaypoint.name = name.count > 0 ? name : "Dropped Pin"
// newWaypoint.description_p = description
// newWaypoint.latitudeI = Int32(coordinate.coordinate?.latitude ?? 0 * 1e7)
// newWaypoint.longitudeI = Int32(coordinate.coordinate?.longitude ?? 0 * 1e7)
// // Unicode scalar value for the icon emoji string
// let unicodeScalers = icon.unicodeScalars
// // First element as an UInt32
// let unicode = unicodeScalers[unicodeScalers.startIndex].value
// newWaypoint.icon = unicode
// if locked {
// if lockedTo == 0 {
// newWaypoint.lockedTo = UInt32(bleManager.connectedPeripheral!.num)
// } else {
// newWaypoint.lockedTo = UInt32(lockedTo)
// }
// }
// newWaypoint.expire = 1
// if bleManager.sendWaypoint(waypoint: newWaypoint) {
// dismiss()
// } else {
// dismiss()
// Logger.mesh.error("Send waypoint failed")
// }
// })
// }
// label: {
// Label("delete", systemImage: "trash")
// .foregroundColor(.red)
// }
// .buttonStyle(.bordered)
// .buttonBorderShape(.capsule)
// .controlSize(.regular)
// .padding(.bottom)
// }
// }
// .onAppear {
// if coordinate.waypointId > 0 {
// let waypoint = getWaypoint(id: Int64(coordinate.waypointId), context: bleManager.context)
// name = waypoint.name ?? "Dropped Pin"
// description = waypoint.longDescription ?? ""
// icon = String(UnicodeScalar(Int(waypoint.icon)) ?? "📍")
// latitude = Double(waypoint.latitudeI) / 1e7
// longitude = Double(waypoint.longitudeI) / 1e7
// if waypoint.expire != nil {
// expires = true
// expire = waypoint.expire ?? Date()
// } else {
// expires = false
// }
// if waypoint.locked > 0 {
// locked = true
// lockedTo = waypoint.locked
// }
// } else {
// name = ""
// description = ""
// locked = false
// expires = false
// expire = Date.now.addingTimeInterval(60 * 480)
// icon = "📍"
// latitude = coordinate.coordinate?.latitude ?? 0
// longitude = coordinate.coordinate?.longitude ?? 0
// }
// }
// }
//}

View file

@ -115,12 +115,15 @@ struct ChannelMessageList: View {
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) ")
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): \(error.localizedDescription)")
Logger.data.error("Failed to read message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)")
}
}
}

View file

@ -37,14 +37,28 @@ struct MessageText: View {
HStack {
Spacer()
Image(systemName: "lock.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .green)
.font(.system(size: 20))
.offset(x: 8, y: 8)
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .green)
.font(.system(size: 20))
.offset(x: 8, y: 8)
}
}
}
let isStoreAndForward = message.portNum == Int32(PortNum.storeForwardApp.rawValue)
let isDetectionSensorMessage = message.portNum == Int32(PortNum.detectionSensorApp.rawValue)
if isStoreAndForward {
VStack(alignment: .trailing) {
Spacer()
HStack {
Spacer()
Image(systemName: "envelope.circle.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .gray)
.font(.system(size: 20))
.offset(x: 8, y: 8)
}
}
}
if tapBackDestination.overlaySensorMessage {
VStack {
isDetectionSensorMessage ? Image(systemName: "sensor.fill")
@ -59,6 +73,7 @@ struct MessageText: View {
} else {
EmptyView()
}
}
.contextMenu {
MessageContextMenuItems(
@ -79,7 +94,7 @@ struct MessageText: View {
do {
try context.save()
} catch {
Logger.data.error("Failed to delete message \(message.messageId): \(error.localizedDescription)")
Logger.data.error("Failed to delete message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)")
}
}
Button("Cancel", role: .cancel) {}

View file

@ -37,7 +37,7 @@ struct RetryButton: View {
do {
try context.save()
} catch {
Logger.data.error("Failed to delete message \(messageID): \(error.localizedDescription)")
Logger.data.error("Failed to delete message \(messageID, privacy: .public): \(error.localizedDescription, privacy: .public)")
}
if !bleManager.sendMessage(
message: payload,
@ -47,7 +47,7 @@ struct RetryButton: View {
replyID: replyID
) {
// Best effort, unlikely since we already checked BLE state
Logger.services.warning("Failed to resend message \(messageID)")
Logger.services.warning("Failed to resend message \(messageID, privacy: .public)")
} else {
switch destination {
case .user:

View file

@ -31,10 +31,10 @@ struct TapbackResponses: View {
tapback.read = true
do {
try context.save()
Logger.data.info("📖 Read tapback \(tapback.messageId) ")
Logger.data.info("📖 Read tapback \(tapback.messageId, privacy: .public) ")
onRead()
} catch {
Logger.data.error("Failed to read tapback \(tapback.messageId): \(error.localizedDescription)")
Logger.data.error("Failed to read tapback \(tapback.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)")
}
}
}

View file

@ -103,11 +103,11 @@ struct UserMessageList: View {
message.read = true
do {
try context.save()
Logger.data.info("📖 [App] Read message \(message.messageId) ")
Logger.data.info("📖 [App] Read message \(message.messageId, privacy: .public) ")
appState.unreadDirectMessages = user.unreadMessages
} catch {
Logger.data.error("Failed to read message \(message.messageId): \(error.localizedDescription)")
Logger.data.error("Failed to read message \(message.messageId, privacy: .public): \(error.localizedDescription, privacy: .public)")
}
}
}

View file

@ -135,7 +135,7 @@ struct DetectionSensorLog: View {
self.isExporting = false
Logger.services.info("Detection Sensor metrics log download succeeded.")
case .failure(let error):
Logger.services.error("Detection Sensor log download failed: \(error.localizedDescription).")
Logger.services.error("Detection Sensor log download failed: \(error.localizedDescription, privacy: .public).")
}
}
)

View file

@ -211,7 +211,7 @@ struct DeviceMetricsLog: View {
) {
Button("device.metrics.delete", role: .destructive) {
if clearTelemetry(destNum: node.num, metricsType: 0, context: context) {
Logger.data.notice("Cleared Device Metrics for \(node.num)")
Logger.data.notice("Cleared Device Metrics for \(node.num, privacy: .public)")
} else {
Logger.data.error("Clear Device Metrics Log Failed")
}
@ -257,7 +257,7 @@ struct DeviceMetricsLog: View {
self.isExporting = false
Logger.services.info("Device metrics log download succeeded.")
case .failure(let error):
Logger.services.error("Device metrics log download failed: \(error.localizedDescription)")
Logger.services.error("Device metrics log download failed: \(error.localizedDescription, privacy: .public)")
}
}
)

View file

@ -177,7 +177,7 @@ struct EnvironmentMetricsLog: View {
self.isExporting = false
Logger.services.info("Environment metrics log download succeeded.")
case .failure(let error):
Logger.services.error("Environment metrics log download failed: \(error.localizedDescription)")
Logger.services.error("Environment metrics log download failed: \(error.localizedDescription, privacy: .public)")
}
}
)

View file

@ -41,7 +41,7 @@ struct DeleteNodeButton: View {
id: node.num,
context: context
) else {
Logger.data.error("Unable to find node info to delete node \(node.num)")
Logger.data.error("Unable to find node info to delete node \(node.num, privacy: .public)")
return
}
let success = bleManager.removeNode(
@ -49,7 +49,7 @@ struct DeleteNodeButton: View {
connectedNodeNum: connectedNode.num
)
if !success {
Logger.data.error("Failed to delete node \(deleteNode.user?.longName ?? "unknown".localized)")
Logger.data.error("Failed to delete node \(deleteNode.user?.longName ?? "unknown".localized, privacy: .public)")
} else {
dismiss()
}

View file

@ -19,20 +19,20 @@ struct NavigateToButton: View {
Logger.services.error("NavigateToAction: Selected node does not exist")
return
}
Logger.services.info("Fetching NodeInfoEntity for userNum: \(userNum)")
Logger.services.info("Fetching NodeInfoEntity for userNum: \(userNum, privacy: .public)")
let fetchRequest: NSFetchRequest<NodeInfoEntity> = NSFetchRequest(entityName: "NodeInfoEntity")
fetchRequest.predicate = NSPredicate(format: "num == %lld", Int64(userNum))
do {
let fetchedNodes = try PersistenceController.shared.container.viewContext.fetch(fetchRequest)
guard let nodeInfo = fetchedNodes.first else {
Logger.services.error("NavigateToAction: Node with userNum \(userNum) not found in Core Data")
Logger.services.error("NavigateToAction: Node with userNum \(userNum, privacy: .public) not found in Core Data")
return
}
if let latitude = nodeInfo.latestPosition?.latitude,
let longitude = nodeInfo.latestPosition?.longitude {
if let url = URL(string: "maps://?saddr=&daddr=\(latitude),\(longitude)") {
@ -41,10 +41,10 @@ struct NavigateToButton: View {
Logger.services.error("Failed to create URL for navigation")
}
} else {
Logger.services.warning("NavigateToAction: Node \(userNum) has invalid or missing coordinates")
Logger.services.warning("NavigateToAction: Node \(userNum, privacy: .public) has invalid or missing coordinates")
}
} catch {
Logger.services.error("NavigateToAction: Failed to fetch node with userNum \(userNum): \(error.localizedDescription)")
Logger.services.error("NavigateToAction: Failed to fetch node with userNum \(userNum, privacy: .public): \(error.localizedDescription, privacy: .public)")
}
} label: {
Label {

View file

@ -115,7 +115,7 @@ struct MeshMap: View {
editingWaypoint!.longitudeI = Int32((newWaypointCoord?.longitude ?? 0) * 1e7)
editingWaypoint!.expire = Date.now.addingTimeInterval(60 * 480)
editingWaypoint!.id = 0
Logger.services.debug("Long press occured at Lat: \(coordinate.latitude) Long: \(coordinate.longitude)")
Logger.services.debug("Long press occured at Lat: \(coordinate.latitude, privacy: .public) Long: \(coordinate.longitude, privacy: .public)")
default: return
}
})

View file

@ -237,7 +237,7 @@ struct NodeList: View {
if deleteNode != nil {
let success = bleManager.removeNode(node: deleteNode!, connectedNodeNum: Int64(bleManager.connectedPeripheral?.num ?? -1))
if !success {
Logger.data.error("Failed to delete node \(deleteNode?.user?.longName ?? "unknown".localized)")
Logger.data.error("Failed to delete node \(deleteNode?.user?.longName ?? "unknown".localized, privacy: .public)")
}
}
}

View file

@ -176,7 +176,7 @@ struct PaxCounterLog: View {
) {
Button("paxcounter.delete", role: .destructive) {
if clearPax(destNum: node.num, context: context) {
Logger.services.info("Cleared Pax Counter for \(node.num)")
Logger.services.info("Cleared Pax Counter for \(node.num, privacy: .public)")
} else {
Logger.services.error("Clear Pax Counter Log Failed")
}
@ -216,7 +216,7 @@ struct PaxCounterLog: View {
self.isExporting = false
Logger.services.info("PAX Counter log download succeeded")
case .failure(let error):
Logger.services.error("PAX Counter log download failed: \(error.localizedDescription)")
Logger.services.error("PAX Counter log download failed: \(error.localizedDescription, privacy: .public)")
}
}
)

View file

@ -163,7 +163,7 @@ struct PositionLog: View {
Logger.services.info("Position log download succeeded.")
self.isExporting = false
case .failure(let error):
Logger.services.error("Position log download failed: \(error.localizedDescription)")
Logger.services.error("Position log download failed: \(error.localizedDescription, privacy: .public)")
}
}
)

View file

@ -243,7 +243,7 @@ struct PowerMetricsLog: View {
) {
Button("Delete Power metrics?", role: .destructive) {
if clearTelemetry(destNum: node.num, metricsType: 2, context: context) {
Logger.data.notice("Cleared Power Metrics for \(node.num)")
Logger.data.notice("Cleared Power Metrics for \(node.num, privacy: .public)")
} else {
Logger.data.error("Clear Power Metrics Log Failed")
}
@ -272,7 +272,7 @@ struct PowerMetricsLog: View {
ContentUnavailableView("No Power Metrics", systemImage: "slash.circle")
}
}
.navigationTitle("Power Metrics Log}")
.navigationTitle("Power Metrics Log")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
ZStack {
@ -289,7 +289,7 @@ struct PowerMetricsLog: View {
self.isExporting = false
Logger.services.info("Power metrics log download succeeded.")
case .failure(let error):
Logger.services.error("Power metrics log download failed: \(error.localizedDescription)")
Logger.services.error("Power metrics log download failed: \(error.localizedDescription, privacy: .public)")
}
}
)

View file

@ -64,7 +64,7 @@ struct TraceRouteLog: View {
do {
try context.save()
} catch let error as NSError {
Logger.data.error("\(error.localizedDescription)")
Logger.data.error("\(error.localizedDescription, privacy: .public)")
}
} label: {
Label("delete", systemImage: "trash")

View file

@ -178,7 +178,7 @@ struct AppLog: View {
self.isExporting = false
Logger.services.info("Application log download succeeded.")
case .failure(let error):
Logger.services.error("Application log download failed: \(error.localizedDescription)")
Logger.services.error("Application log download failed: \(error.localizedDescription, privacy: .public)")
}
}
)

View file

@ -186,11 +186,11 @@ struct Channels: View {
if channel.role != Channel.Role.disabled {
do {
try context.save()
Logger.data.info("💾 Saved Channel: \(channel.settings.name)")
Logger.data.info("💾 Saved Channel: \(channel.settings.name, privacy: .public)")
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Unresolved Core Data error in the channel editor. Error: \(nsError)")
Logger.data.error("Unresolved Core Data error in the channel editor. Error: \(nsError, privacy: .public)")
}
} else {
let objects = selectedChannel?.allPrivateMessages ?? []
@ -203,11 +203,11 @@ struct Channels: View {
context.delete(selectedChannel!)
do {
try context.save()
Logger.data.info("💾 Deleted Channel: \(channel.settings.name)")
Logger.data.info("💾 Deleted Channel: \(channel.settings.name, privacy: .public)")
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Unresolved Core Data error in the channel editor. Error: \(nsError)")
Logger.data.error("Unresolved Core Data error in the channel editor. Error: \(nsError, privacy: .public)")
}
}
let adminMessageId = bleManager.saveChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!)

View file

@ -249,7 +249,6 @@ struct LoRaConfig: View {
let expiration = node.sessionExpiration ?? Date()
if expiration < Date() || node.loRaConfig == nil {
Logger.mesh.info("⚙️ Empty or expired lora config requesting via PKI admin")
_ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
}
} else {

View file

@ -357,7 +357,7 @@ struct MQTTConfig: View {
defaultTopic = "msh/" + (region?.topic ?? "UNSET")
geocoder.reverseGeocodeLocation(LocationsHandler.shared.locationsArray.first!, completionHandler: {(placemarks, error) in
if let error {
Logger.services.error("Failed to reverse geocode location: \(error.localizedDescription)")
Logger.services.error("Failed to reverse geocode location: \(error.localizedDescription, privacy: .public)")
return
}

View file

@ -18,8 +18,8 @@ struct StoreForwardConfig: View {
@State var hasChanges: Bool = false
/// Enable the Store and Forward Module
@State var enabled = false
/// Is a S&F Router
@State var isRouter = false
/// Is a S&F Server
@State var isServer = false
/// Send a Heartbeat
@State var heartbeat: Bool = false
/// Number of Records
@ -35,43 +35,19 @@ struct StoreForwardConfig: View {
ConfigHeader(title: "Store & Forward", config: \.storeForwardConfig, node: node, onAppear: setStoreAndForwardValues)
Section(header: Text("options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "envelope.arrow.triangle.branch")
Text("Enables the store and forward module. Store and forward must be enabled on both client and router devices.")
Text("Enables the store and forward module.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.listRowSeparator(.visible)
if enabled {
HStack {
Picker(selection: $isRouter, label: Text("Role")) {
Text("Client")
.tag(false)
Text("Router")
.tag(true)
}
.pickerStyle(SegmentedPickerStyle())
.padding(.top, 5)
.padding(.bottom, 5)
}
VStack {
if isRouter {
Text("Store and forward router devices require a ESP32 device with PSRAM.")
.foregroundColor(.gray)
.font(.callout)
} else {
Text("Store and forward clients can request history from routers on the network.")
.foregroundColor(.gray)
.font(.callout)
}
}
}
}
if isRouter {
Section(header: Text("Router Options")) {
if enabled {
Section(header: Text("Settings")) {
Toggle(isOn: $heartbeat) {
Label("storeforward.heartbeat", systemImage: "waveform.path.ecg")
Text("Send a heartbeat to advertise the server's presence.")
}
Picker("Number of records", selection: $records) {
Text("unset").tag(0)
@ -81,7 +57,7 @@ struct StoreForwardConfig: View {
Text("100").tag(100)
}
.pickerStyle(DefaultPickerStyle())
Picker("History Return Max", selection: $historyReturnMax ) {
Picker("History Return Max", selection: $historyReturnMax) {
Text("unset").tag(0)
Text("25").tag(25)
Text("50").tag(50)
@ -89,7 +65,7 @@ struct StoreForwardConfig: View {
Text("100").tag(100)
}
.pickerStyle(DefaultPickerStyle())
Picker("History Return Window", selection: $historyReturnWindow ) {
Picker("History Return Window", selection: $historyReturnWindow) {
Text("unset").tag(0)
Text("One Minute").tag(60)
Text("Five Minutes").tag(300)
@ -101,6 +77,20 @@ struct StoreForwardConfig: View {
}
.pickerStyle(DefaultPickerStyle())
}
Section(header: Text("Server Option")) {
Toggle(isOn: $isServer) {
Label("Server", systemImage: "server.rack")
Text("Enable this device as a Store and Forward server. Requires an ESP32 device with PSRAM.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.listRowSeparator(.visible)
if isServer {
Text("Store and forward servers require an ESP32 device with PSRAM or Linux Native.")
.foregroundColor(.gray)
.font(.callout)
}
}
}
}
.scrollDismissesKeyboard(.interactively)
@ -110,18 +100,19 @@ struct StoreForwardConfig: View {
SaveConfigButton(node: node, hasChanges: $hasChanges) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if connectedNode != nil {
/// Let the user set isRouter for the connected node, for nodes on the mesh set isRouter based
/// Let the user set isServer for the connected node, for nodes on the mesh set isServer based
/// on receipt of a primary heartbeat
if connectedNode?.num ?? 0 == node?.num ?? -1 {
connectedNode?.storeForwardConfig?.isRouter = isRouter
connectedNode?.storeForwardConfig?.isRouter = isServer
do {
try context.save()
} catch {
Logger.mesh.error("Failed to save isRouter: \(error.localizedDescription)")
Logger.mesh.error("Failed to save isServer: \(error.localizedDescription, privacy: .public)")
}
}
var sfc = ModuleConfig.StoreForwardConfig()
sfc.isServer = isServer
sfc.enabled = self.enabled
sfc.heartbeat = self.heartbeat
sfc.records = UInt32(self.records)
@ -171,8 +162,8 @@ struct StoreForwardConfig: View {
.onChange(of: enabled) { oldEnabled, newEnabled in
if oldEnabled != newEnabled && newEnabled != node!.storeForwardConfig!.enabled { hasChanges = true }
}
.onChange(of: isRouter) { oldIsRouter, newIsRouter in
if oldIsRouter != newIsRouter && newIsRouter != node!.storeForwardConfig!.isRouter { hasChanges = true }
.onChange(of: isServer) { oldIsServer, newIsServer in
if oldIsServer != newIsServer && newIsServer != node!.storeForwardConfig!.isRouter { hasChanges = true }
}
.onChange(of: heartbeat) { oldHeartbeat, newHeartbeat in
if oldHeartbeat != newHeartbeat && newHeartbeat != node?.storeForwardConfig?.heartbeat ?? true { hasChanges = true }
@ -187,9 +178,10 @@ struct StoreForwardConfig: View {
if oldHistoryReturnWindow != newHistoryReturnWindow && newHistoryReturnWindow != node!.storeForwardConfig?.historyReturnWindow ?? -1 { hasChanges = true }
}
}
func setStoreAndForwardValues() {
self.enabled = (node?.storeForwardConfig?.enabled ?? false)
self.isRouter = (node?.storeForwardConfig?.isRouter ?? false)
self.isServer = (node?.storeForwardConfig?.isRouter ?? false)
self.heartbeat = (node?.storeForwardConfig?.heartbeat ?? true)
self.records = Int(node?.storeForwardConfig?.records ?? 50)
self.historyReturnMax = Int(node?.storeForwardConfig?.historyReturnMax ?? 100)

View file

@ -530,7 +530,7 @@ struct PositionConfig: View {
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving Position Config Entity \(nsError)")
Logger.data.error("Error Saving Position Config Entity \(nsError, privacy: .public)")
}
}
@ -550,7 +550,7 @@ struct PositionConfig: View {
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving Position Config Entity \(nsError)")
Logger.data.error("Error Saving Position Config Entity \(nsError, privacy: .public)")
}
}
}

View file

@ -22,7 +22,6 @@ struct SecurityConfig: View {
@State var hasChanges = false
@State var publicKey = ""
@State var hasValidPublicKey: Bool = false
@State var privateKey = ""
@State var hasValidPrivateKey: Bool = false
@State var adminKey: String = ""
@ -45,11 +44,14 @@ struct SecurityConfig: View {
Section(header: Text("Admin & Direct Message Keys")) {
VStack(alignment: .leading) {
Label("Public Key", systemImage: "key")
SecureInput("Public Key", text: $publicKey, isValid: $hasValidPublicKey)
.background(
RoundedRectangle(cornerRadius: 10.0)
.stroke(hasValidPublicKey ? Color.clear : Color.red, lineWidth: 2.0)
)
Text(publicKey)
.font(idiom == .phone ? .caption : .callout)
.allowsTightening(true)
.monospaced()
.keyboardType(.alphabet)
.foregroundStyle(.tertiary)
.disableAutocorrection(true)
.textSelection(.enabled)
Text("Sent out to other nodes on the mesh to allow them to compute a shared secret key.")
.foregroundStyle(.secondary)
.font(idiom == .phone ? .caption : .callout)
@ -144,15 +146,6 @@ struct SecurityConfig: View {
.onChange(of: adminChannelEnabled) { _, newAdminChannelEnabled in
if newAdminChannelEnabled != node?.securityConfig?.adminChannelEnabled { hasChanges = true }
}
.onChange(of: publicKey) {
let tempKey = Data(base64Encoded: publicKey) ?? Data()
if tempKey.count == 32 {
hasValidPublicKey = true
} else {
hasValidPublicKey = false
}
hasChanges = true
}
.onChange(of: privateKey) {
let tempKey = Data(base64Encoded: privateKey) ?? Data()
if tempKey.count == 32 {
@ -222,7 +215,7 @@ struct SecurityConfig: View {
SaveConfigButton(node: node, hasChanges: $hasChanges) {
if !hasValidPublicKey || !hasValidPrivateKey || !hasValidAdminKey {
if !hasValidPrivateKey || !hasValidAdminKey || !hasValidAdminKey2 || !hasValidAdminKey3 {
return
}

View file

@ -62,7 +62,7 @@ class Api: ObservableObject {
completion(deviceHardware)
}
} catch {
Logger.services.error("JSON decode failure: \(error.localizedDescription)")
Logger.services.error("JSON decode failure: \(error.localizedDescription, privacy: .public)")
}
return
}
@ -82,7 +82,7 @@ class Api: ObservableObject {
completion(firmwareReleases)
}
} catch {
Logger.services.error("JSON decode failure: \(error.localizedDescription)")
Logger.services.error("JSON decode failure: \(error.localizedDescription, privacy: .public)")
}
return
}

View file

@ -188,7 +188,7 @@ struct RouteRecorder: View {
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving RouteEntity from the Route Recorder \(nsError)")
Logger.data.error("Error Saving RouteEntity from the Route Recorder \(nsError, privacy: .public)")
}
} label: {
Label("start", systemImage: "play")
@ -246,7 +246,7 @@ struct RouteRecorder: View {
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving RouteEntity from the Route Recorder \(nsError)")
Logger.data.error("Error Saving RouteEntity from the Route Recorder \(nsError, privacy: .public)")
}
isShowingDetails = false
} label: {
@ -298,11 +298,10 @@ struct RouteRecorder: View {
do {
try context.save()
Logger.data.info("💾 Saved a new route location")
// logger.info("💾 Updated Canned Messages Messages For: \(fetchedNode[0].num)")
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving LocationEntity from the Route Recorder \(nsError)")
Logger.data.error("Error Saving LocationEntity from the Route Recorder \(nsError, privacy: .public)")
}
}
}

View file

@ -64,7 +64,7 @@ struct Routes: View {
var latIndex = -1
var longIndex = -1
for index in headers!.indices {
Logger.services.debug("\(index): \( headers![index])")
Logger.services.debug("\(index, privacy: .public): \( headers![index], privacy: .public)")
if headers![index].trimmingCharacters(in: .whitespaces) == "Latitude" {
latIndex = index
} else if headers![index].trimmingCharacters(in: .whitespaces) == "Longitude" {
@ -94,7 +94,7 @@ struct Routes: View {
do {
try context.save()
} catch let error as NSError {
Logger.services.error("\(error.localizedDescription)")
Logger.services.error("\(error.localizedDescription, privacy: .public)")
isShowingBadFileAlert = true
}
} else {
@ -103,11 +103,11 @@ struct Routes: View {
} catch {
// TODO: deal with errors
Logger.services.error("\(error.localizedDescription)")
Logger.services.error("\(error.localizedDescription, privacy: .public)")
}
} catch {
Logger.services.error("CSV Import Error: \(error.localizedDescription)")
Logger.services.error("CSV Import Error: \(error.localizedDescription, privacy: .public)")
}
}
List(routes, id: \.self, selection: $selectedRoute) { route in
@ -151,7 +151,7 @@ struct Routes: View {
do {
try context.save()
} catch let error as NSError {
Logger.data.error("\(error.localizedDescription)")
Logger.data.error("\(error.localizedDescription, privacy: .public)")
}
} label: {
Label("delete", systemImage: "trash")
@ -227,7 +227,7 @@ struct Routes: View {
} catch {
context.rollback()
let nsError = error as NSError
Logger.data.error("Error Saving RouteEntity from the Route Editor \(nsError)")
Logger.data.error("Error Saving RouteEntity from the Route Editor \(nsError, privacy: .public)")
}
}
.buttonStyle(.bordered)
@ -300,7 +300,7 @@ struct Routes: View {
self.isExporting = false
Logger.services.info("Route log download succeeded.")
case .failure(let error):
Logger.services.error("Route log download failed: \(error.localizedDescription).")
Logger.services.error("Route log download failed: \(error.localizedDescription, privacy: .public).")
}
}
)