Let a tap on the phone's host list mean connect

Choosing a machine raised the connect bar over the bottom of the list: a
password box, CONNECT, EDIT, MOVE and DELETE. Five controls in the way of the
one thing a tap on a machine's name obviously means.

So the gestures split. A tap connects. A long press raises the bar, with all
five. The pencil in the phone's header — its only persistent chrome — edits
whichever host is chosen, which is the one of the five common enough to be worth
a control that is always in the same place.

The flag doing it is the desktop's own IsHostPaneOpen rather than a second one.
That head made exactly this move when a selection stopped opening its drawer, and
the question both are asking is "has somebody asked about this host" — answering
it twice is how two heads come to disagree about what a selection means.

One tap cannot finish: a host that authenticates with a typed password has
nowhere on a list to be given one. That tap raises the bar with the box in it and
says so, and a second tap with the box filled in connects. The branch is in the
view model rather than in the head, because "can this machine be reached without
asking for anything" is the same question the bar's own password box answers, and
a copy of it in a view would be a second reading of a binding chain that has one.

Two mechanics worth knowing. Avalonia raises Tapped on release whatever the press
lasted, so a long press would open the bar and then connect — one touch firing
both gestures — which is why HostsScreen tracks the hold and swallows the tap it
precedes. And Holding only fires once IsHoldingEnabled is set, so that and the
handler are attached together rather than one in markup and one in code.

ConnectToRecent now opens the pane rather than selecting the row. On the phone it
has to: a selection alone raises nothing now, so going back to a recent machine
would land on a screen with nothing to press.
This commit is contained in:
2026-08-05 08:43:33 +02:00
parent 69bc9e270b
commit 746711da9d
8 changed files with 412 additions and 26 deletions
@@ -1133,9 +1133,17 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
/// <remarks>
/// <para>
/// <b>Two destinations, because a recent row is one of two different things.</b> One that names a
/// keychain host goes to that host, selected, on the hosts screen — which is where its connect bar is,
/// with whatever authentication the keychain resolves for it and a password box only if it needs one.
/// Connecting from here instead would be a third connect path that had to answer all of that again.
/// keychain host goes to that host on the hosts screen, with the panel about it opened — the desktop's
/// drawer, the phone's connect bar — carrying whatever authentication the keychain resolves for it and a
/// password box only if it needs one. Connecting from here instead would be a third connect path that
/// had to answer all of that again.
/// </para>
/// <para>
/// ◆ <b>It opens that panel rather than merely selecting the row, and on the phone it has to.</b>
/// Choosing a host there no longer raises the bar — a tap on the list connects instead, see
/// <c>VaultViewModel.ShowsConnectBar</c> — so arriving with the host selected and nothing else would be
/// arriving at a screen with nothing to press. Asking to go back to a machine is exactly the deliberate
/// act that flag exists to distinguish from browsing.
/// </para>
/// <para>
/// One that names no item was typed into the manual box, and the log stored exactly what was dialled —
@@ -1163,7 +1171,7 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
&& vault.Hosts.FirstOrDefault(host => host.EntityId == hostId) is { } known
&& vault.IsVaultShown(known.VaultId))
{
vault.SelectedHost = known;
vault.OpenHostPaneCommand.Execute(known);
ShowScreen(ShellScreen.Hosts);
return;
}