Public Access
Start port forwards on the phone, listed in a sheet off its terminal
The Android head skipped a host's forwards because it had nowhere to show what was listening. Its terminal bar now draws a ⇄ for a tab that has forwards, carrying the listening count in green and turning the danger colour when one failed, and it raises a sheet over the same rows the desktop sidebar lists: notation, route, state, failure reason and a tap to switch each off or on. MainWindowViewModel.StartForwards no longer returns early on Android. The sheet is shell state like the connect sheet: it collapses the renderer while up, is lowered by leaving the terminal, and the back gesture closes it first. Forwards ride the shell's keep-alive rather than holding the foreground service themselves, since a forward cannot outlive its shell. They only change the notification, which now counts ports forwarded beside shells connected, fed by a new ForwardsChanged event and ListeningForwardCount. The phone still has no editor for forwards; the More screen card, README and design-import-gaps now say they run here and are edited on the desktop.
This commit is contained in:
1 parent
f05deb4fbc
commit
c3dfa42108
13 files changed
+415
-56
No files matched your search
@@ -1220,6 +1220,18 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// </remarks>
|
||||
internal event EventHandler? TerminalSessionOpened;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when any tab's forward starts, stops or fails, so a head can re-count what is listening.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The phone's keep-alive notification is the one listener: it says how many forwards are listening
|
||||
/// alongside how many shells are connected. On the UI thread, since every state change is.
|
||||
/// </remarks>
|
||||
internal event EventHandler? ForwardsChanged;
|
||||
|
||||
/// <summary>How many forwards are listening, across every tab.</summary>
|
||||
internal int ListeningForwardCount => Tabs.Sum(tab => tab.ListeningForwardCount);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when something this shell did belongs in the terminal the user is already looking at, so the
|
||||
/// view can put the keyboard back there.
|
||||
@@ -1466,6 +1478,7 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
internal bool IsTerminalShowing =>
|
||||
IsTerminalSurface
|
||||
&& !IsConnectSheetOpen
|
||||
&& !IsForwardsSheetOpen
|
||||
&& !IsHostKeyDecisionShowing
|
||||
&& SelectedTab is { HasSession: true };
|
||||
|
||||
@@ -2211,6 +2224,45 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
[RelayCommand]
|
||||
private void CloseConnectSheet() => IsConnectSheetOpen = false;
|
||||
|
||||
// ---- The phone's forwards sheet ----
|
||||
|
||||
/// <summary>
|
||||
/// Whether the phone's list of the selected tab's forwards is open over the terminal.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The phone's answer to the desktop sidebar's FORWARDS section, over the same rows —
|
||||
/// <see cref="TerminalTabViewModel.Forwards"/> — with the same state word, reason and switch. A sheet
|
||||
/// rather than something always drawn because a phone gives a shell the whole screen, so it is raised
|
||||
/// from a button in the terminal's bar, which draws only for a tab that has forwards and turns the danger
|
||||
/// colour when one of them failed.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Shell state for the reason <see cref="IsConnectSheetOpen"/> is: it collapses the renderer, which is a
|
||||
/// native view nothing can be drawn over. Lowered with the connect sheet whenever the terminal surface is
|
||||
/// left — see <see cref="OnSurfaceChanged"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[ObservableProperty]
|
||||
private bool isForwardsSheetOpen;
|
||||
|
||||
/// <summary>Raises the forwards sheet over the terminal.</summary>
|
||||
/// <remarks>Gated as <see cref="OpenConnectSheet"/> is, and on there being a tab to list.</remarks>
|
||||
[RelayCommand]
|
||||
private void OpenForwardsSheet()
|
||||
{
|
||||
if (!IsTerminalSurface || SelectedTab is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsForwardsSheetOpen = true;
|
||||
}
|
||||
|
||||
/// <summary>Lowers the forwards sheet, leaving the terminal where it was.</summary>
|
||||
[RelayCommand]
|
||||
private void CloseForwardsSheet() => IsForwardsSheetOpen = false;
|
||||
|
||||
// ---- Open terminals ----
|
||||
|
||||
/// <summary>
|
||||
@@ -3875,19 +3927,13 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// for the tab's current id would start its listener on a connection it was never part of.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// ◆ <b>Not on the phone.</b> This view model is shared, and the forwarding would work there unchanged —
|
||||
/// but that head has no surface listing a tab's forwards, and listeners opened on a phone that never shows
|
||||
/// them are exactly the invisible state this codebase refuses elsewhere. Its More screen says forwarding
|
||||
/// is desktop-only for now. A runtime check for the reason <c>RendererNeverStarted</c> gives.
|
||||
/// ◆ <b>On the phone too.</b> This used to return early on Android, because that head had nowhere to show
|
||||
/// what was listening. Its terminal bar now carries a FORWARDS button over the same rows the desktop's
|
||||
/// sidebar lists — see <see cref="IsForwardsSheetOpen"/> — so both heads start the same forwards.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private void StartForwards(TerminalTabViewModel tab, TerminalSessionEventArgs e)
|
||||
{
|
||||
if (OperatingSystem.IsAndroid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sessionId = e.SessionId;
|
||||
|
||||
tab.ReplaceForwards(e.Forwards.Select(rule => new PortForwardViewModel(
|
||||
@@ -3897,8 +3943,21 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
|
||||
foreach (var forward in tab.Forwards)
|
||||
{
|
||||
// Never unsubscribed, and it need not be: the row holds the handler, not the other way round, so a
|
||||
// row replaced by a reconnect is collected with it — after its last change, to Stopped, is heard.
|
||||
forward.PropertyChanged += OnForwardChanged;
|
||||
_ = forward.StartAsync();
|
||||
}
|
||||
|
||||
ForwardsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnForwardChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is nameof(PortForwardViewModel.State))
|
||||
{
|
||||
ForwardsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -5187,6 +5246,7 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
if (value is not ShellSurface.Terminal)
|
||||
{
|
||||
IsConnectSheetOpen = false;
|
||||
IsForwardsSheetOpen = false;
|
||||
}
|
||||
|
||||
RaiseSurfaceState();
|
||||
@@ -5371,6 +5431,9 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// <inheritdoc cref="OnIsSearchingChanged" />
|
||||
partial void OnIsConnectSheetOpenChanged(bool value) => RaiseTerminalState();
|
||||
|
||||
/// <inheritdoc cref="OnIsSearchingChanged" />
|
||||
partial void OnIsForwardsSheetOpenChanged(bool value) => RaiseTerminalState();
|
||||
|
||||
/// <remarks>
|
||||
/// The unlock card and the confirmation swap, so arming one has to hide the other — see
|
||||
/// <see cref="IsAskingForThePassphrase"/>.
|
||||
|
||||
Reference in new issue
Block a user