using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using DodoSSH.Client.Shell.ViewModels;
namespace DodoSSH.Client.Android.Views;
/// Design 02 — the host list, and the connect bar that replaces the desktop's right column.
///
///
/// ◆ Two gestures on one list, and this file is the whole of the difference between them. A tap on a
/// host connects to it; a long press asks about it, which is what raises the bar. Why they were split is on
/// the screen itself; what is here is the mechanics, and there are two of them worth knowing.
///
///
/// The handlers are on the ListBox rather than on the row, and the row stays a plain Grid.
/// A button as the item template swallows the press before the list sees it — FilesScreen writes that
/// out at length — leaving nothing selected and every control that reads the selection doing nothing. Both
/// events fire after the list has moved its selection, which is what lets these read it.
///
///
internal sealed partial class HostsScreen : UserControl
{
///
/// Whether the press that is about to end was long enough to have meant something else.
///
///
/// Avalonia raises Tapped on release whatever the press lasted, so without this a long press
/// would open the bar and then connect — the two gestures firing one after the other on one touch, which
/// is the one outcome that would make both of them untrustworthy. Set when the hold starts and cleared
/// by the tap it suppresses, so it never survives the gesture that set it.
///
private bool held;
///
///
/// The long press is attached here rather than in the markup so that it sits beside the property that
/// makes it fire at all. is set rather than assumed:
/// it is the whole of the gesture, and a default that changed would take it away silently — every tap
/// would go on working and nothing would ever open the bar again.
///
///
/// ◆ FindControl rather than the field the name generator declares for x:Name,
/// which is the same rule PhoneShell and TerminalScreen already carry, and which this
/// file learned the hard way. That field is
/// assigned by the generated InitializeComponent, and no view on this head calls it — they load
/// their XAML directly, as above. So the field compiles, is null at run time, and the first thing
/// touching it throws. Here that was a in this constructor, which
/// is a crash before the first frame: the phone shell builds this control on the way up, so the
/// application died on launch rather than on the hosts screen.
///
///
public HostsScreen()
{
AvaloniaXamlLoader.Load(this);
var rows = this.FindControl("Rows")!;
InputElement.SetIsHoldingEnabled(rows, true);
rows.Holding += OnRowHeld;
}
///
/// Connects to the row that was tapped.
///
///
///
/// Guarded on the selection being a host rather than on what was under the finger. A tap on a group
/// heading moves the list's selection and the view model bounces it straight back to whatever was chosen
/// before — see VaultViewModel.SelectedSidebarRow — so reading the selection here answers "a host,
/// or nothing" without this file needing to know that rule. The cost of getting it wrong is connecting to
/// a machine the user was not pointing at.
///
///
/// Fire-and-forget, as the desktop grid's activation is: the command reports its own failures onto the
/// status line — an unknown host key, a refused password — and awaiting it here would be an event handler
/// returning a task nothing observes.
///
///
private void OnRowTapped(object? sender, TappedEventArgs e)
{
if (held)
{
held = false;
return;
}
if (DataContext is VaultViewModel { SelectedHost: { } row } vault)
{
_ = vault.ConnectToRowCommand.ExecuteAsync(row);
}
}
///
/// Raises the bar about the row that was held.
///
///
///
/// On rather than on completion, so the bar is up while the finger is
/// still down. A long press that showed nothing until release would be a gesture with no way to tell it
/// had been recognised, and the only feedback available on this list is the thing it does.
///
///
/// It goes through OpenHostPaneCommand, which is the desktop's own "ask about this host" — the two
/// heads raise different furniture from one flag rather than keeping a selection rule each.
///
///
private void OnRowHeld(object? sender, HoldingRoutedEventArgs e)
{
if (e.HoldingState != HoldingState.Started)
{
return;
}
held = true;
if (DataContext is VaultViewModel { SelectedHost: { } row } vault)
{
vault.OpenHostPaneCommand.Execute(row);
}
}
}