-
+
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.CicloVidaYMapa.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.CicloVidaYMapa.cs
index 913c010..54f5261 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.CicloVidaYMapa.cs
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.CicloVidaYMapa.cs
@@ -50,6 +50,7 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
try { await JS.InvokeVoidAsync("initAltInspector", _dotRef); } catch (Exception ex) { Console.WriteLine("initAltInspector: " + ex); }
try { await JS.InvokeVoidAsync("rt.setDotNetHelper", _dotRef); } catch (Exception ex) { Console.WriteLine("rt.setDotNetHelper: " + ex); }
try { await JS.InvokeVoidAsync("rt.blockLeafletDoubleClick"); } catch (Exception ex) { Console.WriteLine("rt.blockLeafletDoubleClick: " + ex); }
+ try { await JS.InvokeVoidAsync("rt.initMobileMapControls"); } catch (Exception ex) { Console.WriteLine("rt.initMobileMapControls: " + ex); }
try { await JS.InvokeVoidAsync("rt.initDraggablePopups"); } catch (Exception ex) { Console.WriteLine("rt.initDraggablePopups: " + ex); }
await CargarConfiguracionLocalAsync();
await AsegurarHistorialLocalCargadoAsync();
@@ -253,6 +254,7 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
private double _inspectorLat, _inspectorLon;
private double? _inspectorElev;
private double? _inspectorElevIdee;
+ private string? _inspectorCallejero;
///
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs
index 865f754..986aa72 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs
@@ -351,6 +351,7 @@ public partial class PlanificadorRutas : ComponentBase
private TimeSpan? _tiempoUltimoCalculoRuta = null;
private bool _mostrarElevacionesRuta = false;
private bool _pintandoElevacionesRuta = false;
+ private bool _panelRutasMovilContraido = false;
private double _umbralPendienteSubidaRuta = 3.0;
private double _umbralPendienteBajadaRuta = 3.0;
private readonly Dictionary> _paradasItinerarioVisiblesPorId = new(StringComparer.OrdinalIgnoreCase);
@@ -1111,9 +1112,7 @@ public partial class PlanificadorRutas : ComponentBase
if (string.IsNullOrWhiteSpace(distancia))
return nombre;
- return cercano.DistanciaMetros <= 20
- ? $"{nombre} ({distancia})"
- : $"mas cercano: {nombre} ({distancia})";
+ return $"{nombre} ({distancia})";
}
private static string LimpiarTextoCallejeroResumen(string texto)
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs
index bac82fe..e4f4398 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs
@@ -1,5 +1,6 @@
using System.Globalization;
using System.Text.Json;
+using System.Text.Json.Serialization;
using Microsoft.JSInterop;
using RutasDBUS.Modelos.Historial;
@@ -8,8 +9,14 @@ namespace RutasDBUS.Components.Pages;
public partial class PlanificadorRutas
{
private const string ClaveHistorialLocal = "rutasdbus.planificador.routeHistory.v1";
+ private const string ClaveRespaldoHistorialLocal = "rutasdbus.planificador.routeHistory.recovery.v1";
private const int MaxItemsHistorialLocal = 100;
private const int IntervaloRefrescoHistorialCompartidoSegundos = 3;
+ private static readonly JsonSerializerOptions OpcionesLecturaHistorialLocal = new()
+ {
+ PropertyNameCaseInsensitive = true,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString
+ };
private readonly List _historialLocal = new();
private readonly List _historialCompartido = new();
@@ -26,25 +33,38 @@ public partial class PlanificadorRutas
private ConfiguracionLocalPlanificador? _configuracionTemporalPreviaHistorial;
private Task? _tareaCargaHistorialLocal;
private bool _historialLocalCargado = false;
+ private bool _historialLocalCargando = false;
+ private int _versionAperturaHistorial;
private async Task ToggleHistorialPanel()
{
if (_mostrarHistorialRutas)
{
+ _versionAperturaHistorial++;
_mostrarHistorialRutas = false;
+ _historialLocalCargando = false;
StopRefrescoHistorialCompartido();
await RestaurarConfiguracionTemporalHistorialAsync();
return;
}
- if (!await AsegurarHistorialLocalCargadoAsync())
- {
- MostrarMensajeHistorial("No he podido cargar el historial personal. Intentalo de nuevo.", esError: true);
- return;
- }
-
+ var versionApertura = ++_versionAperturaHistorial;
_mostrarHistorialRutas = true;
+ _historialLocalCargando = !_historialLocalCargado;
+ StateHasChanged();
+
+ var historialLocalCargado = await AsegurarHistorialLocalCargadoAsync();
+ if (!_mostrarHistorialRutas || versionApertura != _versionAperturaHistorial)
+ return;
+
+ _historialLocalCargando = false;
+ if (!historialLocalCargado)
+ MostrarMensajeHistorial("No he podido cargar el historial personal. Intentalo de nuevo.", esError: true);
+
await CargarHistorialCompartidoAsync();
+ if (!_mostrarHistorialRutas || versionApertura != _versionAperturaHistorial)
+ return;
+
StartRefrescoHistorialCompartido();
}
@@ -55,44 +75,127 @@ public partial class PlanificadorRutas
_tareaCargaHistorialLocal ??= CargarHistorialLocalDesdeNavegadorAsync();
var tareaCarga = _tareaCargaHistorialLocal;
- var cargado = await tareaCarga;
-
- if (!cargado && ReferenceEquals(_tareaCargaHistorialLocal, tareaCarga))
- _tareaCargaHistorialLocal = null;
-
- return cargado;
+ try
+ {
+ return await tareaCarga;
+ }
+ catch
+ {
+ return false;
+ }
+ finally
+ {
+ if (!_historialLocalCargado && ReferenceEquals(_tareaCargaHistorialLocal, tareaCarga))
+ _tareaCargaHistorialLocal = null;
+ }
}
private async Task CargarHistorialLocalDesdeNavegadorAsync()
{
+ string? json;
try
{
- var json = await JS.InvokeAsync("rt.loadLocalValue", ClaveHistorialLocal);
- if (!string.IsNullOrWhiteSpace(json))
- {
- var items = JsonSerializer.Deserialize>(json);
- if (items is null)
- return false;
-
- _historialLocal.Clear();
- _historialLocal.AddRange(items
- .Where(x => !string.IsNullOrWhiteSpace(x.Id))
- .OrderByDescending(x => x.FechaActualizacionUtc)
- .Take(MaxItemsHistorialLocal));
- }
-
- _historialLocalSeleccionadoId = _historialLocal.FirstOrDefault()?.Id ?? string.Empty;
- _historialLocalCargado = true;
- return true;
+ json = await JS.InvokeAsync("rt.loadLocalValue", ClaveHistorialLocal);
}
catch (JSException)
{
return false;
}
- catch (JsonException)
+ catch (Exception)
{
return false;
}
+
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ FinalizarCargaHistorialLocal(Array.Empty());
+ return true;
+ }
+
+ try
+ {
+ var items = DeserializarHistorialLocal(json, out var registrosOmitidos);
+ FinalizarCargaHistorialLocal(items);
+
+ if (registrosOmitidos > 0)
+ {
+ await GuardarHistorialLocalRecuperadoAsync(json);
+ MostrarMensajeHistorial(
+ $"Se han recuperado {_historialLocal.Count} registros del historial; {registrosOmitidos} estaban dañados.",
+ esError: true);
+ }
+
+ return true;
+ }
+ catch (Exception ex) when (ex is JsonException or NotSupportedException or InvalidOperationException)
+ {
+ FinalizarCargaHistorialLocal(Array.Empty());
+ await GuardarHistorialLocalRecuperadoAsync(json);
+ MostrarMensajeHistorial(
+ "El historial personal guardado estaba dañado. Se ha conservado una copia y se ha iniciado uno nuevo.",
+ esError: true);
+ return true;
+ }
+ }
+
+ private static IReadOnlyList DeserializarHistorialLocal(
+ string json,
+ out int registrosOmitidos)
+ {
+ using var documento = JsonDocument.Parse(json);
+ if (documento.RootElement.ValueKind != JsonValueKind.Array)
+ throw new JsonException("El historial local no contiene una lista.");
+
+ registrosOmitidos = 0;
+ var recuperados = new List();
+ foreach (var elemento in documento.RootElement.EnumerateArray())
+ {
+ try
+ {
+ var item = elemento.Deserialize(OpcionesLecturaHistorialLocal);
+ if (item is null)
+ {
+ registrosOmitidos++;
+ continue;
+ }
+
+ if (string.IsNullOrWhiteSpace(item.Id))
+ item.Id = Guid.NewGuid().ToString("N");
+
+ item.LineasPermitidas ??= new List();
+ recuperados.Add(item);
+ }
+ catch (Exception ex) when (ex is JsonException or NotSupportedException or InvalidOperationException)
+ {
+ registrosOmitidos++;
+ }
+ }
+
+ return recuperados
+ .OrderByDescending(x => x.FechaActualizacionUtc)
+ .Take(MaxItemsHistorialLocal)
+ .ToList();
+ }
+
+ private void FinalizarCargaHistorialLocal(IEnumerable items)
+ {
+ _historialLocal.Clear();
+ _historialLocal.AddRange(items);
+ _historialLocalSeleccionadoId = _historialLocal.FirstOrDefault()?.Id ?? string.Empty;
+ _historialLocalCargado = true;
+ }
+
+ private async Task GuardarHistorialLocalRecuperadoAsync(string jsonOriginal)
+ {
+ try
+ {
+ await JS.InvokeVoidAsync("rt.saveLocalValue", ClaveRespaldoHistorialLocal, jsonOriginal);
+ var jsonRecuperado = JsonSerializer.Serialize(_historialLocal);
+ await JS.InvokeVoidAsync("rt.saveLocalValue", ClaveHistorialLocal, jsonRecuperado);
+ }
+ catch (JSException)
+ {
+ }
}
private async Task PersistirHistorialLocalAsync()
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs
index ec2854d..2db5773 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs
@@ -34,6 +34,7 @@ public partial class PlanificadorRutas : ComponentBase
private void ToggleBuscadorMapa()
{
_mostrarBuscadorMapa = !_mostrarBuscadorMapa;
+ _panelRutasMovilContraido = _mostrarBuscadorMapa;
}
///
@@ -1181,7 +1182,7 @@ public partial class PlanificadorRutas : ComponentBase
private bool _elevPopupMinimizado = false;
- private const string _versionApp = "(v-20260715a)";
+ private const string _versionApp = "(v-20260716a)";
private double? _paradaElevG;
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs
index 4acc6ef..03618fd 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Planificacion.cs
@@ -1269,6 +1269,7 @@ public partial class PlanificadorRutas : ComponentBase
{
_inspectorLat = lat;
_inspectorLon = lon;
+ _inspectorCallejero = null;
try
{
@@ -1316,6 +1317,19 @@ public partial class PlanificadorRutas : ComponentBase
_inspectorElevIdee = null;
}
+ try
+ {
+ var lugar = await GeocodificadorLocal.BuscarMasCercanoAsync(
+ lat,
+ lon,
+ CancellationToken.None);
+ _inspectorCallejero = FormatearLugarCallejeroResumen(lugar);
+ }
+ catch
+ {
+ _inspectorCallejero = null;
+ }
+
_popupInspectorVisible = true;
StateHasChanged();
}
@@ -1411,6 +1425,34 @@ public partial class PlanificadorRutas : ComponentBase
}
});
}
+
+ ///
+ /// Selecciona origen o destino mediante pulsacion larga en dispositivos tactiles.
+ ///
+ [JSInvokable]
+ public async Task OnMobileMapLongPress(double lat, double lon)
+ {
+ await InvokeAsync(async () =>
+ {
+ if (_inspectorActivo)
+ return;
+
+ _ctsDobleClick?.Cancel();
+ _ctsDobleClick?.Dispose();
+ _ctsDobleClick = null;
+ _primerClickPendiente = null;
+ _paradaPrimerClickPendiente = null;
+
+ var paradaHit = await DetectarParadaHitAsync(lat, lon);
+ if (paradaHit is null && !_mostrarParadas)
+ paradaHit = await DetectarParadaDestacadaHitAsync(lat, lon);
+
+ if (paradaHit is not null)
+ await EjecutarSeleccionOrigenDestinoAsync(paradaHit.Latitud, paradaHit.Longitud);
+ else
+ await EjecutarSeleccionOrigenDestinoAsync(lat, lon);
+ });
+ }
///
/// Gestiona seleccion origen destino.
@@ -1515,6 +1557,7 @@ public partial class PlanificadorRutas : ComponentBase
return;
var cronometroCalculo = System.Diagnostics.Stopwatch.StartNew();
+ _panelRutasMovilContraido = _mostrarBuscadorMapa;
await CerrarTodasLasPreviewsAsync();
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs
index 3926c32..194ac0d 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs
@@ -984,6 +984,7 @@ public partial class PlanificadorRutas : ComponentBase
_duracionTotalSegundos = 0;
_opcionesRuta.Clear();
_indiceAlternativaSeleccionada = 0;
+ _panelRutasMovilContraido = false;
if (_mapa != null)
{
diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.razor b/RutasDBUS/Components/Pages/PlanificadorRutas.razor
index 6a6c4a2..a3480f7 100644
--- a/RutasDBUS/Components/Pages/PlanificadorRutas.razor
+++ b/RutasDBUS/Components/Pages/PlanificadorRutas.razor
@@ -26,11 +26,13 @@
- Planificador de rutas en Dbus
+ Planificador de rutas en Dbus
+ Rutas Dbus@_versionApp
- Haz doble clic en el mapa para marcar origen y destino
+ Haz doble clic en el mapa para marcar origen y destino
+ Mantén pulsado el mapa para marcar origen y destino