Public Access
The bottom bar is Vaults · Connections · Settings, drawn as Material 3 pills with icons from the embedded Material font and a count of open sessions on Connections, which replaces the strip of shells above the bar. Vaults is the open vault: its header carries the name as a switch to the vaults screen, the sync light and a lock icon, and a Hosts · Keychain · Snippets segmented control sits under it. The keychain and snippets move there from Settings (IsVaultSurface lights the tab; IsMoreSurface no longer lists them), and back from either goes to the hosts. Settings becomes an account card and grouped sections: security, vaults and history, files, this app. Host rows are cards with a terminal tile and the live dot on its corner. The connect sheet names the host and its address, labels its box, focuses it as it opens, reveals the password on request, and puts CANCEL left and CONNECT right; every two-button row on the head now confirms on the right. While a hosts-screen sheet is up the header and bar are dimmed too and a tap on them lowers it, and the bar stands down while the software keyboard is up. The keychain gains a floating + and a sheet of the desktop's five adds (generate a key, paste a key, a password, a bucket, a tag) over the editors VaultViewModel already had, drawn as a full-screen page with labels above the boxes; a chosen item gets EDIT and COPY PUBLIC KEY beside MOVE and DELETE. Snippets get a floating + and cards that say RUNS or INSERTS. Files trade the seven-button block for a selection bar (save, and a ⋮ sheet with rename, move, permissions and a red delete), folder and file tiles, an upload button, and header actions for a terminal on the same host, refresh and close; a terminal's + sheet opens SFTP on that terminal's own host. The theme takes the audit's visual findings: Fluent's ClipToBounds is cleared on glowing buttons so the glow is no longer cut to a square (the desktop's Button.accent too), prose is Montserrat with mono kept for addresses and code, disabled secondary buttons are quieter than enabled ones, Fluent's dropdown, checkbox, switch, focus and selection brushes come from the palette, touch no longer leaves hover fills behind, and lists, nav entries and icon buttons carry names for TalkBack. Builds clean; checked on the emulator end to end: unlock, the three vault views, adding and deleting a password, connecting through the new sheet, files on the terminal's host with the selection bar and its sheet. App tests 727 of 733 pass with 5 skipped; the one failure, TheCountsAPhoneShows_ FollowEveryForwardsState, passes three runs of three alone and is untouched here. Layout tests pass.
303 lines
17 KiB
XML
303 lines
17 KiB
XML
<UserControl xmlns="https://github.com/avaloniaui"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
|
|
x:Class="DodoSSH.Client.Android.Views.MoreScreen"
|
|
x:DataType="vm:MainWindowViewModel"
|
|
Background="{StaticResource Canvas}">
|
|
|
|
<!--
|
|
SETTINGS: the hub behind the bottom bar's third entry.
|
|
|
|
It takes the shell as its data context rather than the vault, because nearly every row on it is a
|
|
navigation command and nothing here reads an item.
|
|
|
|
── the 2026-10-04 redesign ────────────────────────────────────────────────────────────────────────────
|
|
The hub used to hold the product: the keychain, snippets, SFTP and S3 were rows here beside Preferences
|
|
and LOCK, and the audit's M8 said so. The keychain and snippets now live under VAULTS, in the open vault's
|
|
own switch, and what is left is arranged the way a phone's settings are — who you are, then grouped
|
|
sections of rows on cards — rather than one flat list of places.
|
|
|
|
The account card says who is signed in and where, which nothing on this head said before: the server's
|
|
address is the fact that decides whose keychain this is, and it was only ever typed once, on the first
|
|
screen. It is not a button; there is no account page on the phone to open (signing out is on the lock
|
|
screen — see PreferencesScreen's NOT HERE).
|
|
|
|
Rows are cards of rows rather than cards each, because a destination is a place and not a thing, and a
|
|
group of them reads as one section only when they share a surface. Each keeps a sentence saying what is
|
|
behind it, because a hub whose entries are one word each is a menu you have to open to read.
|
|
|
|
Every icon is the embedded Material font's, as the bar's are — see NavButton, which says why the
|
|
Unicode glyphs went (audit M7).
|
|
-->
|
|
|
|
<UserControl.Styles>
|
|
<!-- A section's rows. The card's own padding is the rows' business, so a row's press fill reaches its
|
|
edges; the hairlines between rows are drawn by the rows' containers. -->
|
|
<Style Selector="Border.settingsgroup">
|
|
<Setter Property="Background" Value="{StaticResource Panel}" />
|
|
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
|
|
<Setter Property="BorderThickness" Value="1" />
|
|
<Setter Property="CornerRadius" Value="16" />
|
|
<Setter Property="ClipToBounds" Value="True" />
|
|
</Style>
|
|
<Style Selector="Button.settingsrow">
|
|
<Setter Property="MinHeight" Value="60" />
|
|
<Setter Property="Padding" Value="14,8" />
|
|
<Setter Property="CornerRadius" Value="0" />
|
|
</Style>
|
|
<Style Selector="Border.rowrule">
|
|
<Setter Property="Height" Value="1" />
|
|
<Setter Property="Margin" Value="62,0,0,0" />
|
|
<Setter Property="Background" Value="{StaticResource BorderSubtle}" />
|
|
</Style>
|
|
<!-- The tile an icon sits on: a square of the accent's wash, so a row's mark is a colour as well as a
|
|
shape. Amber for the one row that ends something rather than going somewhere. -->
|
|
<Style Selector="Border.rowtile">
|
|
<Setter Property="Width" Value="36" />
|
|
<Setter Property="Height" Value="36" />
|
|
<Setter Property="CornerRadius" Value="10" />
|
|
<Setter Property="Background" Value="{StaticResource AccentSoft}" />
|
|
<Setter Property="VerticalAlignment" Value="Center" />
|
|
</Style>
|
|
<Style Selector="Border.rowtile > TextBlock">
|
|
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
|
|
<Setter Property="FontSize" Value="19" />
|
|
</Style>
|
|
<Style Selector="TextBlock.rowtitle">
|
|
<Setter Property="Foreground" Value="{StaticResource Text}" />
|
|
<Setter Property="FontSize" Value="14.5" />
|
|
<Setter Property="FontWeight" Value="SemiBold" />
|
|
</Style>
|
|
<Style Selector="TextBlock.chevron">
|
|
<Setter Property="Foreground" Value="{StaticResource TextGhost}" />
|
|
<Setter Property="FontSize" Value="20" />
|
|
</Style>
|
|
</UserControl.Styles>
|
|
|
|
<Grid RowDefinitions="Auto,*">
|
|
|
|
<Border Grid.Row="0" Height="60" Padding="20,0" Background="{StaticResource DeepChrome}"
|
|
BorderBrush="{StaticResource Border}" BorderThickness="0,0,0,1">
|
|
<TextBlock Classes="heading" FontSize="22" FontWeight="Bold" Text="Settings" />
|
|
</Border>
|
|
|
|
<ScrollViewer Grid.Row="1">
|
|
<StackPanel Margin="16,14,16,24" Spacing="8">
|
|
|
|
<!-- ============ who, and where ============ -->
|
|
<Border Classes="settingsgroup" Padding="14">
|
|
<Grid ColumnDefinitions="Auto,14,*">
|
|
<Border Grid.Column="0" Width="48" Height="48" CornerRadius="24"
|
|
Background="{StaticResource AvatarGradient}">
|
|
<TextBlock Text="{Binding AvatarInitials}" FontSize="17" FontWeight="Bold"
|
|
Foreground="{StaticResource AccentInk}"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="3" VerticalAlignment="Center">
|
|
<TextBlock FontSize="16" FontWeight="Bold" Foreground="{StaticResource Text}"
|
|
Text="{Binding AccountName}" TextTrimming="CharacterEllipsis" />
|
|
<TextBlock FontSize="12.5" Foreground="{StaticResource TextDim}" Text="{Binding Email}"
|
|
TextTrimming="CharacterEllipsis"
|
|
IsVisible="{Binding Email, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
|
<TextBlock Classes="mono" FontSize="11.5" Foreground="{StaticResource TextFaint}"
|
|
Text="{Binding ServerUrl}" TextTrimming="CharacterEllipsis" />
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ============ security ============ -->
|
|
<TextBlock Classes="label" Text="SECURITY" Margin="6,14,0,2" />
|
|
<Border Classes="settingsgroup">
|
|
<StackPanel>
|
|
<!--
|
|
The fingerprint is a row to Preferences rather than a switch here. Turning it on registers a key
|
|
in the phone's secure hardware and turning it off withdraws one — two operations with a prompt
|
|
and a paragraph each, which live on that screen — so this row says which state the phone is in
|
|
and goes there. ON and OFF are the two flags the shell already computes; a phone with no way to
|
|
hold a key says neither and the screen behind explains.
|
|
-->
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="Unlock with a fingerprint"
|
|
Command="{Binding ShowScreenCommand}"
|
|
CommandParameter="{x:Static vm:ShellScreen.Preferences}">
|
|
<Grid ColumnDefinitions="Auto,12,*,8,Auto,4,Auto">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="Unlock with a fingerprint" />
|
|
<TextBlock Classes="detail" Text="The key stays in this phone's secure hardware." />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="4" Classes="label" FontSize="10.5" Text="ON"
|
|
Foreground="{StaticResource Live}" VerticalAlignment="Center"
|
|
IsVisible="{Binding CanForgetDevice}" />
|
|
<TextBlock Grid.Column="4" Classes="label" FontSize="10.5" Text="OFF"
|
|
VerticalAlignment="Center" IsVisible="{Binding CanRegisterDevice}" />
|
|
<TextBlock Grid.Column="6" Classes="icon chevron" Text="" />
|
|
</Grid>
|
|
</Button>
|
|
<Border Classes="rowrule" />
|
|
<!--
|
|
Locking, here as well as in the vault's header. The header is on the VAULTS tab and stands down
|
|
on this one, so without this row every screen under SETTINGS would be two taps from locking the
|
|
keychain — the wrong direction for the one action whose whole value is being quick.
|
|
-->
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="Lock the keychain now"
|
|
Command="{Binding LockCommand}">
|
|
<Grid ColumnDefinitions="Auto,12,*">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="Lock now" />
|
|
<TextBlock Classes="detail" Text="Shells you have open keep running." />
|
|
</StackPanel>
|
|
</Grid>
|
|
</Button>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ============ vaults and history ============ -->
|
|
<TextBlock Classes="label" Text="VAULTS AND HISTORY" Margin="6,14,0,2" />
|
|
<Border Classes="settingsgroup">
|
|
<StackPanel>
|
|
<!--
|
|
Vaults, which the v2 design had no row for. It is on the phone because a vault arrives without
|
|
being asked for, and somebody it arrives for is at least as likely to be holding a phone. The
|
|
vault name in the VAULTS tab's header leads here too.
|
|
-->
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="Vaults"
|
|
Command="{Binding ShowScreenCommand}"
|
|
CommandParameter="{x:Static vm:ShellScreen.Vaults}">
|
|
<Grid ColumnDefinitions="Auto,12,*,8,Auto">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="Vaults and sharing" />
|
|
<TextBlock Classes="detail" Text="Your vaults, who is in each one, and who holds its key." />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="4" Classes="icon chevron" Text="" />
|
|
</Grid>
|
|
</Button>
|
|
<Border Classes="rowrule" />
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="Logs"
|
|
Command="{Binding ShowScreenCommand}"
|
|
CommandParameter="{x:Static vm:ShellScreen.Logs}">
|
|
<Grid ColumnDefinitions="Auto,12,*,8,Auto">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="Activity log" />
|
|
<TextBlock Classes="detail" Text="What has been connected to, and what has changed." />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="4" Classes="icon chevron" Text="" />
|
|
</Grid>
|
|
</Button>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ============ files ============ -->
|
|
<!--
|
|
SFTP and S3 stay reachable from here, and the terminal now reaches a host's files directly — the
|
|
FILES entry in a session's menu opens them on that session's host. A bucket has no session, so this
|
|
is still the way to one. Both go through ShowFiles rather than ShowScreen: they are one screen over
|
|
one view model, and which kind of remote it offers is the thing being chosen.
|
|
-->
|
|
<TextBlock Classes="label" Text="FILES" Margin="6,14,0,2" />
|
|
<Border Classes="settingsgroup">
|
|
<StackPanel>
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="SFTP"
|
|
Command="{Binding ShowFilesCommand}"
|
|
CommandParameter="{x:Static vm:RemoteKind.Host}">
|
|
<Grid ColumnDefinitions="Auto,12,*,8,Auto">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="SFTP" />
|
|
<TextBlock Classes="detail" Text="Browse a host's files." />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="4" Classes="icon chevron" Text="" />
|
|
</Grid>
|
|
</Button>
|
|
<Border Classes="rowrule" />
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="S3"
|
|
Command="{Binding ShowFilesCommand}"
|
|
CommandParameter="{x:Static vm:RemoteKind.Bucket}">
|
|
<Grid ColumnDefinitions="Auto,12,*,8,Auto">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="S3" />
|
|
<TextBlock Classes="detail" Text="Objects in an S3-compatible bucket from the keychain." />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="4" Classes="icon chevron" Text="" />
|
|
</Grid>
|
|
</Button>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<!-- ============ this app ============ -->
|
|
<!-- Preferences with tune rather than a gear: the gear is the tab this row is under (audit M7). -->
|
|
<TextBlock Classes="label" Text="THIS APP" Margin="6,14,0,2" />
|
|
<Border Classes="settingsgroup">
|
|
<Button Classes="row settingsrow" AutomationProperties.Name="Preferences"
|
|
Command="{Binding ShowScreenCommand}"
|
|
CommandParameter="{x:Static vm:ShellScreen.Preferences}">
|
|
<Grid ColumnDefinitions="Auto,12,*,8,Auto,4,Auto">
|
|
<Border Grid.Column="0" Classes="rowtile">
|
|
<TextBlock Classes="icon" Text="" />
|
|
</Border>
|
|
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
|
<TextBlock Classes="rowtitle" Text="Preferences and updates" />
|
|
<TextBlock Classes="detail" Text="Fingerprint unlock, updates, and what this phone cannot do." />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="4" Classes="mono" FontSize="11" Foreground="{StaticResource TextFaint}"
|
|
Text="{Binding Updates.CurrentVersion}" VerticalAlignment="Center"
|
|
MaxWidth="110" TextTrimming="CharacterEllipsis" />
|
|
<TextBlock Grid.Column="6" Classes="icon chevron" Text="" />
|
|
</Grid>
|
|
</Button>
|
|
</Border>
|
|
|
|
<!--
|
|
◆ Port forwarding, stated rather than drawn.
|
|
|
|
The v2 design gives this hub an entry and a full screen behind it: local, remote and dynamic rules
|
|
with toggles and byte counters. Forwarding is built and runs here as on the desktop — a host's
|
|
forwards start with every terminal opened to it — but it is not a destination, so it has no row. A
|
|
forward belongs to a host and runs with a session, and on this head the session's forwards are a
|
|
sheet off the terminal's bar: the ⇄ button, drawn for a tab that has some, lists them with a switch
|
|
each. See MainWindowViewModel.IsForwardsSheetOpen.
|
|
|
|
What the phone lacks is the editor: its host form has no PORT FORWARDS section yet, so forwards are
|
|
added on the desktop and arrive here with the host. The byte counters are not built anywhere;
|
|
SSH.NET keeps no count to draw.
|
|
|
|
It is a paragraph here rather than a missing row for the reason the desktop keeps TEAMS in its rail:
|
|
a place somebody looks for a feature should say where it went. See docs/design-import-gaps.md.
|
|
-->
|
|
<Border Classes="card" Margin="0,14,0,0">
|
|
<StackPanel Spacing="7">
|
|
<StackPanel Orientation="Horizontal" Spacing="9">
|
|
<TextBlock Classes="icon" Text="" Foreground="{StaticResource TextFaint}" FontSize="18" />
|
|
<TextBlock FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource TextDim}"
|
|
Text="Port forwarding" VerticalAlignment="Center" />
|
|
<Border Classes="tag outline" VerticalAlignment="Center">
|
|
<TextBlock Text="IN THE TERMINAL" />
|
|
</Border>
|
|
</StackPanel>
|
|
<TextBlock Classes="body"
|
|
Text="Forwards saved on a host start with every terminal opened to it, here as on the desktop. The ⇄ button in the terminal's bar lists them, with a switch each and the reason one could not start. They are added and edited in the desktop app's host editor; this phone runs them but has no editor for them yet." />
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
</StackPanel>
|
|
</ScrollViewer>
|
|
|
|
</Grid>
|
|
|
|
</UserControl>
|