Public Access
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:
@@ -4474,7 +4474,9 @@ public sealed class ShellFlowTests : IAsyncLifetime
|
||||
var vault = shell.Vault!;
|
||||
|
||||
await AddHostAsync(vault, "prod-db");
|
||||
vault.SelectedHost = Host(vault, "prod-db");
|
||||
|
||||
// The long press, which is the only thing that raises this bar. Selecting used to be enough.
|
||||
vault.OpenHostPaneCommand.Execute(Host(vault, "prod-db"));
|
||||
|
||||
vault.ShowsConnectBar.ShouldBeTrue();
|
||||
|
||||
@@ -4500,6 +4502,89 @@ public sealed class ShellFlowTests : IAsyncLifetime
|
||||
vault.ShowsConnectBar.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// ◆ <b>The gesture split, from the side that costs something to get wrong.</b> Choosing a host used to
|
||||
/// raise the bar — a password box, CONNECT, EDIT, MOVE and DELETE over the bottom of the list — which
|
||||
/// meant a tap on a machine's name put five controls in the way of the one thing it obviously means.
|
||||
/// A tap connects now, and this pins that it raises nothing on the way past.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task ATapOnAHost_ConnectsAndLeavesTheBarWhereItWas()
|
||||
{
|
||||
var vault = await ReadyToConnectAsync();
|
||||
|
||||
await AddKeyAsync(vault, "deploy");
|
||||
await BindKeyAsync(vault, vault.Hosts[0], vault.Keys[0].EntityId);
|
||||
|
||||
await using var renderer = await FakeRenderer.AttachAsync(workspace, Token);
|
||||
|
||||
vault.SelectedHost = null;
|
||||
|
||||
await vault.ConnectToRowCommand.ExecuteAsync(vault.Hosts[0]);
|
||||
|
||||
vault.Status.ShouldContain("Connected", Case.Insensitive);
|
||||
vault.SelectedHost.ShouldNotBeNull("the row a tap landed on is what was connected to");
|
||||
vault.ShowsConnectBar.ShouldBeFalse("and nothing was raised over the list to do it");
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The one tap that cannot finish, and the reason the bar still exists. A host that authenticates with a
|
||||
/// typed password has nowhere to be given one from a list, so the tap raises the bar with the box in it
|
||||
/// and says so. What it must never do is connect with no password, or leave somebody tapping a row that
|
||||
/// silently does nothing.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task ATapOnAHostThatWantsAPassword_RaisesTheBarInsteadOfConnecting()
|
||||
{
|
||||
var vault = await ReadyToConnectAsync();
|
||||
|
||||
vault.SelectedHost = null;
|
||||
|
||||
await vault.ConnectToRowCommand.ExecuteAsync(vault.Hosts[0]);
|
||||
|
||||
vault.ShowsConnectBar.ShouldBeTrue("there is nowhere else to type it");
|
||||
vault.SelectedHostAsksForAPassword.ShouldBeTrue();
|
||||
vault.Status.ShouldContain("password");
|
||||
ssh.Requests.ShouldBeEmpty("nothing was dialled with no password");
|
||||
|
||||
// The second tap, with the box filled in, is the one that goes through — otherwise the bar would be
|
||||
// answering the instruction it just gave with the same instruction again.
|
||||
await using var renderer = await FakeRenderer.AttachAsync(workspace, Token);
|
||||
|
||||
vault.ConnectPassword = "typed-in";
|
||||
|
||||
await vault.ConnectToRowCommand.ExecuteAsync(vault.Hosts[0]);
|
||||
|
||||
ssh.Requests.ShouldHaveSingleItem().Credential.ShouldBeOfType<SshPasswordCredential>();
|
||||
vault.ShowsConnectBar.ShouldBeFalse("and the bar goes with the tap that succeeded");
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The pencil in the phone's header, which is where EDIT went when the bar stopped being raised by
|
||||
/// choosing a machine. Both halves matter: it is offered for a chosen host, and it is taken away while an
|
||||
/// editor is up — a pencil that opens the form already on screen is a control with nothing to do.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task TheHeaderPencil_IsOfferedForAChosenHostAndNotOverAnEditor()
|
||||
{
|
||||
await UnlockedAsync();
|
||||
var vault = shell.Vault!;
|
||||
|
||||
await AddHostAsync(vault, "prod-db");
|
||||
|
||||
vault.SelectedHost = null;
|
||||
vault.CanEditSelectedHost.ShouldBeFalse("there is nothing for it to be about");
|
||||
|
||||
vault.SelectedHost = Host(vault, "prod-db");
|
||||
vault.CanEditSelectedHost.ShouldBeTrue("and a tap is enough — it does not need the bar");
|
||||
|
||||
vault.EditSelectedHostCommand.Execute(null);
|
||||
vault.CanEditSelectedHost.ShouldBeFalse("the form it opens is already on screen");
|
||||
|
||||
vault.CancelEditCommand.Execute(null);
|
||||
vault.CanEditSelectedHost.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AGroupsHeading_OpensThatGroupsEditorRatherThanTheSelectedOne()
|
||||
{
|
||||
@@ -4687,7 +4772,7 @@ public sealed class ShellFlowTests : IAsyncLifetime
|
||||
|
||||
await AddHostAsync(vault, "prod-db");
|
||||
|
||||
vault.SelectedHost = vault.Hosts.Single();
|
||||
vault.OpenHostPaneCommand.Execute(vault.Hosts.Single());
|
||||
|
||||
vault.ShowsConnectBar.ShouldBeTrue();
|
||||
vault.ShowsConnectControls.ShouldBeTrue();
|
||||
@@ -4712,7 +4797,7 @@ public sealed class ShellFlowTests : IAsyncLifetime
|
||||
|
||||
await AddHostAsync(vault, "prod-db");
|
||||
|
||||
vault.SelectedHost = vault.Hosts.Single();
|
||||
vault.OpenHostPaneCommand.Execute(vault.Hosts.Single());
|
||||
vault.DeleteHostCommand.Execute(null);
|
||||
|
||||
await vault.ConfirmDeleteCommand.ExecuteAsync(null);
|
||||
|
||||
Reference in New Issue
Block a user