52b2994d3b
Il salvataggio come bozza non e' mai scattato dal tasto indietro: la UI Blazor non riceveva l'evento, e uscire da un form con modifiche in corso buttava via il lavoro esattamente come prima che la bozza esistesse. Il back non arrivava a OnBackPressed per due motivi diversi: - da AndroidX Activity 1.6 il gesto passa dall'OnBackPressedDispatcher e il metodo dell'Activity non viene piu' invocato; - con la navigazione a tre tasti il back arriva come evento di tastiera e la WebView lo intercetta per conto suo, tornando indietro nella propria cronologia, prima che Activity o dispatcher lo vedano. Ora la chiave si ferma in DispatchKeyEvent, che passa prima della gerarchia di view, e resta un callback sul dispatcher per la navigazione a gesti, dove evento di tastiera non ce n'e'. Entrambi restano accesi solo finche' la UI ha davvero qualcosa da dire: altrimenti il back si comporta come sempre. I gestori diventano una pila, cosi' sopra il form puo' aprirsi un bottom sheet e il back chiude prima quello senza portarsi via la registrazione sottostante. ActionSheet si registra da solo mentre e' aperto. Verificato sull'EDA52: back sul form con modifiche apre "Lavoro non salvato" e salva la bozza; back sullo sheet chiude solo lo sheet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
59 lines
2.1 KiB
C#
59 lines
2.1 KiB
C#
namespace SteUp.Shared.Core.Messages.System;
|
|
|
|
/// <summary>
|
|
/// Ponte fra il tasto indietro del dispositivo (host nativo) e la UI Blazor.
|
|
/// Il form scheda si registra mentre è aperto così il back non manda l'app in
|
|
/// background buttando via il lavoro non salvato: passa invece dallo stesso
|
|
/// percorso del pulsante "Annulla", che propone il salvataggio come bozza.
|
|
///
|
|
/// I gestori sono impilati: sopra il form può aprirsi un bottom sheet, e il
|
|
/// tasto indietro deve chiudere prima quello. Chiudendolo si torna al gestore
|
|
/// sottostante, che resta valido — senza la pila il sheet, uscendo di scena,
|
|
/// porterebbe via con sé anche la registrazione del form.
|
|
/// </summary>
|
|
public class BackButtonService
|
|
{
|
|
private readonly List<Func<Task>> _handlers = [];
|
|
|
|
/// <summary>
|
|
/// Segnala all'host se in questo momento c'è qualcuno da avvisare. L'host
|
|
/// se ne serve per accendere e spegnere la propria intercettazione, così
|
|
/// quando la UI non ha nulla da dire il tasto indietro torna a comportarsi
|
|
/// esattamente come sempre.
|
|
/// </summary>
|
|
public event Action<bool>? HandlersChanged;
|
|
|
|
public bool HasHandlers => _handlers.Count > 0;
|
|
|
|
/// Registra il gestore corrente in cima alla pila. Ritorna la deregistrazione.
|
|
public IDisposable Register(Func<Task> handler)
|
|
{
|
|
_handlers.Add(handler);
|
|
HandlersChanged?.Invoke(true);
|
|
|
|
return new Registration(this, handler);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invocato dall'host nativo. Ritorna true se la UI Blazor ha preso in carico
|
|
/// il tasto indietro: in quel caso l'host non deve fare altro.
|
|
/// </summary>
|
|
public bool TryHandleBackPressed()
|
|
{
|
|
if (_handlers.Count == 0) return false;
|
|
|
|
// Fire-and-forget: il gestore rientra nel dispatcher Blazor da solo.
|
|
_ = _handlers[^1]();
|
|
return true;
|
|
}
|
|
|
|
private sealed class Registration(BackButtonService owner, Func<Task> handler) : IDisposable
|
|
{
|
|
public void Dispose()
|
|
{
|
|
if (!owner._handlers.Remove(handler)) return;
|
|
owner.HandlersChanged?.Invoke(owner.HasHandlers);
|
|
}
|
|
}
|
|
}
|