Skip to content

Commit 36784e5

Browse files
authored
Merge pull request #42 from SMLunchen/fix_vector_show_node_on_map
- bugfix node name column not resizable
2 parents 011c413 + 4eab070 commit 36784e5

15 files changed

Lines changed: 405 additions & 83 deletions

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,36 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
77

88
---
99

10+
## [1.6.2.2] - 2026-08-14
11+
12+
### ✨ Hinzugefügt / 🔧 Geändert
13+
14+
#### 🗄️ NodeDB-Reset: Abfrage-Dialog mit Optionen
15+
- Der Button „NodeDB zurücksetzen" (Geräte-Einstellungen **und** Remote-Admin) öffnet jetzt einen Dialog mit zwei Häkchen statt einer simplen Ja/Nein-Abfrage:
16+
- **Favoriten ebenfalls löschen** — steuert das `nodedb_reset`-Bool: Standard behält Favoriten (`true`), angehakt löscht sie (`false`). Firmware-Abgleich ergab: der Bool-Wert ist genau `keepFavorites` in `resetNodes()`. Hinweis im Dialog: Router/Client-Base behalten Favoriten firmwareseitig immer.
17+
- **Interne Node-DB dieses Clients ebenfalls zurücksetzen** — leert zusätzlich die lokale Node-Liste (In-Memory) + Karten-Pins; Nachrichten-/Telemetrie-History bleibt.
18+
- **Korrektur der Beschreibungen:** Texte sagten „alle bekannten Nodes werden vergessen", obwohl Favoriten erhalten blieben — jetzt korrekt formuliert. Der Geräte-Reset löst wie zuvor einen **Reboot** aus (kein Shutdown).
19+
- Der hartcodierte Dialogtext wurde durch i18n-Strings (DE/EN) ersetzt.
20+
21+
### 🐛 Behoben
22+
23+
#### 🗺️ „Auf Karte zeigen" zentrierte die Vector-Map nicht
24+
- Bei aktiver **Vector-Map** (MapLibre/WebView2) öffnete „Auf Karte zeigen" zwar die Karte, sprang aber **nicht** auf die Node-Position — die Zentrierung lief nur über die Mapsui-**Rastermap**. Jetzt zentriert ein gemeinsamer Helfer (`CenterMapOnNode`) **beide** Karten (Raster via `CenterOnAndZoomTo`, Vector via `setCenter`). Betrifft alle drei Einstiege (Node-Kontextmenü, Nachrichten-Kontextmenü, Alert-Button). Öffnet der Klick die Vector-Map erstmalig, wird das Ziel gepuffert und nach dem Laden angewandt.
25+
26+
#### 🔓 PKI-DMs werden sofort entschlüsselt, wenn wir den Key haben (Flag-unabhängig)
27+
- Der sofortige client-seitige PKI-Entschlüsselungsversuch war an `packet.PkiEncrypted` gekoppelt. Die Firmware setzt dieses Flag aber auf `false`, sobald der **eigene** Node nicht entschlüsseln konnte (Router.cpp:814) — z.B. nach einem **NodeDB-Reset**, wenn dessen nodedb den Sender-Key nicht mehr hat. Dadurch wurden PKI-DMs gepuffert und nicht entschlüsselt, **obwohl der Client den Key besitzt**.
28+
- Jetzt versucht der Client PKI-Entschlüsselung für **jede DM an uns**, sobald Private- und Sender-Key vorliegen — unabhängig vom Flag (sicher, da AES-CCM tag-verifiziert ist). Telemetrie/DMs von Nodes, deren Key wir kennen (der Node aber nach Reset nicht), gehen so sofort auf statt im Puffer zu landen.
29+
30+
#### 📨 Request-Antworten werden nach Portnum verarbeitet (nicht mehr als „verschlüsselte DM")
31+
- Antworten auf `want_response`-Anfragen (UserInfo/Position/Telemetrie/PaxCounter) kommen teils verschlüsselt zurück: **Telemetrie/Pax** PKI-verschlüsselt (der Node hat unseren Key aus der Anfrage, uns fehlt seiner → Henne-Ei), **NodeInfo/Position** kanal-verschlüsselt (Firmware schließt diese Portnums von PKI aus, Router.cpp:1053). Konnte unser Node sie nicht entschlüsseln, landeten sie pauschal als „[Encrypted message]" im Chat und wurden beim Entschlüsseln nur akzeptiert, wenn es Text war.
32+
- Jetzt: ein gepuffertes Paket wird nach Entschlüsselung **nach Portnum geroutet** (`DispatchLateDecrypted``RouteDecodedData`) — NodeInfo/Position/Telemetrie/… gehen in die normale Verarbeitung (Node/Key/Telemetrie werden übernommen), nur echter Text (Portnum 1) erscheint als DM.
33+
- **Zusätzlich:** Kommt eine verschlüsselte Antwort kurz nach einer **expliziten** Info-Anfrage an denselben Node (≤30 s), wird sie als Request-Antwort erkannt, gepuffert und **nicht** mehr als Chat-Platzhalter angezeigt (und löst keine weitere NodeInfo-Anfrage aus). Hinweis: `pki=False` im Log ist irreführend — die Firmware setzt das Flag zurück, sobald der eigene Node nicht entschlüsseln konnte (Router.cpp:814).
34+
35+
#### 📏 Node-Namen-Spalte folgt jetzt der Spaltenbreite
36+
- Die Namensspalte in der Node-Tabelle war hart auf 155 px gedeckelt (`MaxWidth`) und saß in einem `StackPanel` — beim Verbreitern der Spalte wurde der Name trotzdem abgeschnitten. Das Cell-Template nutzt jetzt ein `Grid` (Auto + `*`) ohne `MaxWidth`, und die Zeilen strecken sich auf die Spaltenbreite (`HorizontalContentAlignment=Stretch`). Der Name folgt jetzt der Spaltenbreite (Ellipsis) und zeigt per Tooltip den vollen Namen.
37+
38+
---
39+
1040
## [1.6.2.1] - 2026-08-13
1141

1242
### ✨ Hinzugefügt

MeshhessenClient/MainWindow.Map.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ await VectorMapView.CoreWebView2.ExecuteScriptAsync(
298298
// Restore cached line overlays (traceroutes, paths) e.g. after raster->vector switch
299299
foreach (var (key, json) in _vectorLineJson.ToList())
300300
ExecVectorScript($"setLines({JsonSerializer.Serialize(key)}, {json})");
301+
302+
// A "Show on map" click that opened the map applies its center now that JS is callable.
303+
if (_pendingVectorCenter is { } pc)
304+
{
305+
_pendingVectorCenter = null;
306+
ExecVectorScript($"setCenter({pc.Lon.ToString(ci)}, {pc.Lat.ToString(ci)}, {pc.Zoom})");
307+
}
301308
}
302309
catch (Exception ex)
303310
{
@@ -589,6 +596,49 @@ private async void ExecVectorScript(string script)
589596
catch (Exception ex) { Services.Logger.WriteLine($"[VectorMap] script error: {ex.Message}"); }
590597
}
591598

599+
/// <summary>
600+
/// Centers whichever map is currently active on a node position. Handles both the
601+
/// Mapsui raster map (<see cref="_map"/>) and the MapLibre vector map (WebView2) —
602+
/// the raster-only path used to leave the vector map un-centered on "Show on map".
603+
/// </summary>
604+
private void CenterMapOnNode(double lat, double lon)
605+
{
606+
var nodePos = SphericalMercator.FromLonLat(lon, lat);
607+
if (_map != null)
608+
{
609+
_map.Navigator.CenterOnAndZoomTo(new MPoint(nodePos.x, nodePos.y), 76.0);
610+
MapControl.Refresh();
611+
}
612+
if (UseVectorMap)
613+
{
614+
const int zoom = 12;
615+
if (_vectorMapReady)
616+
{
617+
var ci = System.Globalization.CultureInfo.InvariantCulture;
618+
ExecVectorScript($"setCenter({lon.ToString(ci)}, {lat.ToString(ci)}, {zoom})");
619+
}
620+
else
621+
{
622+
// Map tab opened for the first time by this very click — apply once it's ready.
623+
_pendingVectorCenter = (lon, lat, zoom);
624+
}
625+
}
626+
}
627+
628+
/// <summary>Removes every node pin from both maps (raster features + vector). Assumes
629+
/// <c>_allNodes</c> is already cleared so the vector push writes an empty set.</summary>
630+
private void ClearAllNodePinsFromMap()
631+
{
632+
_nodeFeatures.Clear();
633+
if (_nodeLayer != null)
634+
{
635+
_nodeLayer.Features = _nodeFeatures;
636+
_nodeLayer.DataHasChanged();
637+
}
638+
MapControl?.Refresh();
639+
PushNodePinsToVectorMap();
640+
}
641+
592642
private static string CssColor(Mapsui.Styles.Color c) =>
593643
$"rgba({c.R},{c.G},{c.B},{(c.A / 255.0).ToString("0.##", System.Globalization.CultureInfo.InvariantCulture)})";
594644

MeshhessenClient/MainWindow.NodeList.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,7 @@ private void NodeContextMenu_ShowOnMap_Click(object sender, RoutedEventArgs e)
6464
}
6565

6666
MainTabs.SelectedIndex = 3;
67-
var nodePos = SphericalMercator.FromLonLat(node.Longitude.Value, node.Latitude.Value);
68-
if (_map != null)
69-
{
70-
_map.Navigator.CenterOnAndZoomTo(new MPoint(nodePos.x, nodePos.y), 76.0);
71-
MapControl.Refresh();
72-
}
67+
CenterMapOnNode(node.Latitude.Value, node.Longitude.Value);
7368
}
7469

7570
private void NodeFilterTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)

MeshhessenClient/MainWindow.xaml

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,8 @@
12191219
<Setter Property="MinHeight" Value="42"/>
12201220
<Setter Property="BorderThickness" Value="0"/>
12211221
<Setter Property="Padding" Value="0,2"/>
1222+
<!-- Cells fill their column width so name trims to the column and follows resize -->
1223+
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
12221224
<Style.Triggers>
12231225
<!-- Ungerade Zeilen: dezentes Grau -->
12241226
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
@@ -1276,8 +1278,14 @@
12761278
</GridViewColumn.Header>
12771279
<GridViewColumn.CellTemplate>
12781280
<DataTemplate>
1279-
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
1280-
<Border Width="28" Height="28" CornerRadius="14"
1281+
<!-- Grid (Auto + *) so the name tracks the column width and trims to it;
1282+
a StackPanel measures children with infinite width and ignores resize. -->
1283+
<Grid VerticalAlignment="Center" ClipToBounds="True">
1284+
<Grid.ColumnDefinitions>
1285+
<ColumnDefinition Width="Auto"/>
1286+
<ColumnDefinition Width="*"/>
1287+
</Grid.ColumnDefinitions>
1288+
<Border Grid.Column="0" Width="28" Height="28" CornerRadius="14"
12811289
Background="{Binding AvatarColor, Converter={StaticResource HexToColorConverter}}"
12821290
Margin="0,0,7,0">
12831291
<TextBlock Text="{Binding AvatarLabel}"
@@ -1286,12 +1294,13 @@
12861294
FontSize="9" FontWeight="Bold"
12871295
Foreground="White"/>
12881296
</Border>
1289-
<emoji:TextBlock Text="{Binding Name}"
1297+
<emoji:TextBlock Grid.Column="1" Text="{Binding Name}"
12901298
VerticalAlignment="Center"
1299+
HorizontalAlignment="Left"
12911300
FontWeight="SemiBold"
12921301
TextTrimming="CharacterEllipsis"
1293-
MaxWidth="155"/>
1294-
</StackPanel>
1302+
ToolTip="{Binding Name}"/>
1303+
</Grid>
12951304
</DataTemplate>
12961305
</GridViewColumn.CellTemplate>
12971306
</GridViewColumn>

MeshhessenClient/MainWindow.xaml.cs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public partial class MainWindow : Window
8484
private Services.VectorTileCacheService? _vectorTileCache;
8585
private bool _vectorMapInitStarted = false; // CoreWebView2 init/navigation kicked off
8686
private bool _vectorMapReady = false; // map.html loaded, JS API callable
87+
private (double Lon, double Lat, int Zoom)? _pendingVectorCenter; // "Show on map" fired before the vector map was ready
8788
private MemoryLayer? _nodeLayer;
8889
private MemoryLayer? _myPosLayer;
8990
private readonly List<IFeature> _nodeFeatures = new();
@@ -193,6 +194,7 @@ public MainWindow()
193194
_protocolService.MessageReceived += OnMessageReceived;
194195
_protocolService.PkiMessageDecrypted += OnPkiMessageDecrypted;
195196
_protocolService.NodeInfoReceived += OnNodeInfoReceived;
197+
_protocolService.NodeDbCleared += OnNodeDbCleared;
196198
_protocolService.ChannelInfoReceived += OnChannelInfoReceived;
197199
_protocolService.LoRaConfigReceived += OnLoRaConfigReceived;
198200
_protocolService.DeviceInfoReceived += OnDeviceInfoReceived;
@@ -897,6 +899,7 @@ await Task.Run(() =>
897899
_protocolService.MessageReceived += OnMessageReceived;
898900
_protocolService.PkiMessageDecrypted += OnPkiMessageDecrypted;
899901
_protocolService.NodeInfoReceived += OnNodeInfoReceived;
902+
_protocolService.NodeDbCleared += OnNodeDbCleared;
900903
_protocolService.ChannelInfoReceived += OnChannelInfoReceived;
901904
_protocolService.LoRaConfigReceived += OnLoRaConfigReceived;
902905
_protocolService.DeviceInfoReceived += OnDeviceInfoReceived;
@@ -1609,6 +1612,7 @@ private async Task TryReconnectAsync()
16091612
_protocolService.MessageReceived += OnMessageReceived;
16101613
_protocolService.PkiMessageDecrypted += OnPkiMessageDecrypted;
16111614
_protocolService.NodeInfoReceived += OnNodeInfoReceived;
1615+
_protocolService.NodeDbCleared += OnNodeDbCleared;
16121616
_protocolService.ChannelInfoReceived += OnChannelInfoReceived;
16131617
_protocolService.LoRaConfigReceived += OnLoRaConfigReceived;
16141618
_protocolService.DeviceInfoReceived += OnDeviceInfoReceived;
@@ -1964,6 +1968,26 @@ private void OnNodeInfoReceived(object? sender, Models.NodeInfo node)
19641968
});
19651969
}
19661970

1971+
/// <summary>Internal node DB was cleared (user-triggered) — drop the UI node list and map pins.</summary>
1972+
private void OnNodeDbCleared(object? sender, EventArgs e)
1973+
{
1974+
Dispatcher.BeginInvoke(() =>
1975+
{
1976+
try
1977+
{
1978+
_nodes.Clear();
1979+
_allNodes.Clear();
1980+
ApplyNodeSortAndFilterCore();
1981+
ClearAllNodePinsFromMap();
1982+
Services.Logger.WriteLine("[Nodes] Internal node DB cleared — UI list and map pins reset");
1983+
}
1984+
catch (Exception ex)
1985+
{
1986+
Services.Logger.WriteLine($"Error clearing node UI: {ex.Message}");
1987+
}
1988+
});
1989+
}
1990+
19671991
private void OnChannelInfoReceived(object? sender, ChannelInfo channel)
19681992
{
19691993
// Async Update - blockiert nicht den UI Thread
@@ -2605,12 +2629,7 @@ private void MessageContextMenu_ShowOnMap_Click(object sender, RoutedEventArgs e
26052629
}
26062630

26072631
MainTabs.SelectedIndex = 3;
2608-
var nodePos = SphericalMercator.FromLonLat(node.Longitude.Value, node.Latitude.Value);
2609-
if (_map != null)
2610-
{
2611-
_map.Navigator.CenterOnAndZoomTo(new MPoint(nodePos.x, nodePos.y), 76.0);
2612-
MapControl.Refresh();
2613-
}
2632+
CenterMapOnNode(node.Latitude.Value, node.Longitude.Value);
26142633
}
26152634

26162635
private void MessageContextMenu_SetColor_Click(object sender, RoutedEventArgs e)
@@ -3218,14 +3237,8 @@ private void ShowOnMap_Click(object sender, RoutedEventArgs e)
32183237
// Switch to Map tab
32193238
MainTabs.SelectedIndex = 3; // Map is tab index 3 (0=Messages, 1=Nodes, 2=Channels, 3=Map, 4=Settings, ...)
32203239

3221-
// Center map on node position with closer zoom
3222-
var nodePos = SphericalMercator.FromLonLat(node.Longitude.Value, node.Latitude.Value);
3223-
if (_map != null)
3224-
{
3225-
// Zoom level 12 (resolution ~76)
3226-
_map.Navigator.CenterOnAndZoomTo(new MPoint(nodePos.x, nodePos.y), 76.0);
3227-
MapControl.Refresh();
3228-
}
3240+
// Center whichever map is active (raster + vector) on the node position
3241+
CenterMapOnNode(node.Latitude.Value, node.Longitude.Value);
32293242

32303243
// Close notification
32313244
AlertNotificationBar.Visibility = Visibility.Collapsed;

MeshhessenClient/MeshhessenClient.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
<PublishDir>..\public\</PublishDir>
1919

2020
<AssemblyName>MeshhessenClient</AssemblyName>
21-
<Version>1.6.2.1</Version>
22-
<AssemblyVersion>1.6.2.1</AssemblyVersion>
23-
<FileVersion>1.6.2.1</FileVersion>
21+
<Version>1.6.2.2</Version>
22+
<AssemblyVersion>1.6.2.2</AssemblyVersion>
23+
<FileVersion>1.6.2.2</FileVersion>
2424
<Company>Meshtastic Community</Company>
2525
<Product>Meshhessen Client</Product>
2626
<Description>Offline-fähiger Windows Client für Meshtastic Geräte</Description>

MeshhessenClient/NodeConfigWindow.xaml.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -918,18 +918,21 @@ private static string WindowsToPosixTz(TimeZoneInfo tz)
918918

919919
private async void NodeDbReset_Click(object sender, RoutedEventArgs e)
920920
{
921-
var result = MessageBox.Show(
922-
"NodeDB zurücksetzen?\n\nDas löscht alle bekannten Nodes auf dem Gerät.\nDie eigene Konfiguration bleibt erhalten.\n\nDas Gerät wird danach neu starten.",
923-
"NodeDB zurücksetzen",
924-
MessageBoxButton.YesNo,
925-
MessageBoxImage.Warning);
926-
if (result != MessageBoxResult.Yes) return;
921+
var dlg = new NodeDbResetDialog { Owner = this };
922+
if (dlg.ShowDialog() != true) return;
927923

928924
try
929925
{
930-
await _protocolService.ResetNodeDbAsync();
931-
UpdateStatus("NodeDB-Reset gesendet. Gerät startet neu...");
932-
Logger.WriteLine("NodeDB reset sent");
926+
// nodedb_reset value == keepFavorites (firmware: resetNodes(keepFavorites)).
927+
// The checkbox asks to WIPE favorites, so keepFavorites = !WipeFavorites.
928+
await _protocolService.ResetNodeDbAsync(keepFavorites: !dlg.WipeFavorites);
929+
930+
// Optionally also drop our own in-memory node cache.
931+
if (dlg.ResetInternalDb)
932+
_protocolService.ClearKnownNodes();
933+
934+
UpdateStatus(Loc("StrNcNodeDbResetSent"));
935+
Logger.WriteLine($"NodeDB reset sent (wipeFavorites={dlg.WipeFavorites}, resetInternal={dlg.ResetInternalDb})");
933936
}
934937
catch (Exception ex)
935938
{
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<Window x:Class="MeshhessenClient.NodeDbResetDialog"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:ui="http://schemas.modernwpf.com/2019"
5+
ui:WindowHelper.UseModernWindowStyle="True"
6+
Title="{DynamicResource StrNodeDbResetTitle}"
7+
Width="500"
8+
SizeToContent="Height"
9+
MinWidth="440"
10+
ResizeMode="NoResize"
11+
WindowStartupLocation="CenterOwner">
12+
<StackPanel Margin="22">
13+
<TextBlock Text="{DynamicResource StrNodeDbResetHeader}"
14+
FontWeight="SemiBold" FontSize="15" TextWrapping="Wrap" Margin="0,0,0,10"/>
15+
<TextBlock Text="{DynamicResource StrNodeDbResetBody}"
16+
TextWrapping="Wrap" Margin="0,0,0,18"/>
17+
18+
<!-- Option 1: wipe favorites too -->
19+
<CheckBox x:Name="WipeFavoritesCheck"
20+
Content="{DynamicResource StrNodeDbResetWipeFavorites}" Margin="0,0,0,4"/>
21+
<TextBlock Text="{DynamicResource StrNodeDbResetFavoritesHint}"
22+
FontSize="11" Opacity="0.75"
23+
TextWrapping="Wrap" Margin="28,0,0,14"/>
24+
25+
<!-- Option 2: also reset our internal node DB -->
26+
<CheckBox x:Name="ResetInternalCheck"
27+
Content="{DynamicResource StrNodeDbResetInternal}" Margin="0,0,0,4"/>
28+
<TextBlock x:Name="InternalHint" Text="{DynamicResource StrNodeDbResetInternalHint}"
29+
FontSize="11" Opacity="0.75"
30+
TextWrapping="Wrap" Margin="28,0,0,18"/>
31+
32+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
33+
<Button x:Name="CancelButton" Content="{DynamicResource StrNodeDbResetCancelBtn}"
34+
Width="110" Margin="0,0,8,0" Click="Cancel_Click" IsCancel="True"/>
35+
<Button x:Name="OkButton" Content="{DynamicResource StrNodeDbResetConfirmBtn}"
36+
Width="150" Click="Ok_Click" IsDefault="True"/>
37+
</StackPanel>
38+
</StackPanel>
39+
</Window>

0 commit comments

Comments
 (0)