Files
DodoSSH/src/DodoSSH.Client.App/Views/LogsScreen.axaml
T

256 lines
16 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.App.Views.LogsScreen"
x:DataType="vm:LogsViewModel">
<!--
What has been connected to, and what has been changed.
Two logs behind one screen, chosen by two buttons rather than by a selector's selection — the same idiom
the keychain screen's categories use, and for the same reason: a selection binding moves before a command
can refuse it.
Both are ordinary synced keychain items, encrypted like everything else. The server holds them and cannot
read a single field; what it does learn is that rows exist and when they were written, which ADR 0001
records as the metadata this design cannot hide.
The connections list shows anything still open at the top, marked "still open" rather than with a dash. A
dash would read as a missing recording, and the two are opposite facts — an entry is written once, when a
connection closes, so a live session is deliberately not in the vault yet.
── v5b — Logs.dc.html ──────────────────────────────────────────────────────────────────────────────────
A fidelity pass, not a new shape. What moved:
◆ THE HEADER. "Logs" 33 bold plus the two-segment CONNECTIONS/KEYCHAIN control, restyled onto
Border.navtrack/Button.navseg — the same h31 track the rail's own SSH/SFTP/S3 switcher already uses,
reused rather than redrawn, since it is the identical shape at the identical size. Two segments and not
three: LOGS is the screen's own title, not a tab, so the design's own two — CONNECTIONS and KEYCHAIN —
are the whole of what this screen switches between, exactly matching LogSection's own two values. No
deviation to record here. On the right: a mono status sentence — LogsViewModel.HeaderStatusLine, which
shows a refresh error when there is one and otherwise the fact this screen's own header comment already
states about whichever log is showing — and REFRESH, unchanged.
◆ THE BODY. A bordered radius-12 container on Pane bg wraps each table now, in place of the plain
background either one drew before. Tracked column headers are unchanged — HOST/ADDRESS/LASTED/KIND/
STARTED/FROM for connections, ITEM/TYPE/WHAT/FIELDS/WHEN for keychain activity, both already matching the
design exactly. Rows keep their real two-state host dot (green only for a session genuinely open right
now, per HasVisible on ConnectionLogRowViewModel.IsLive — never a third colour) and their existing
"failed"/"host key refused" amber badges. WHAT, on the activity table, is now a small coloured chip rather
than plain text — green for created, purple for a plain change, red for deleted — reading the same
ActivityOperation this screen already resolved to a word; a chip rather than a fabricated new fact.
◆ THE FOOTER. Logs.dc.html's own lock-glyph sentence — "Both logs are ordinary synced keychain items —
end-to-end encrypted. The server learns only that rows exist and when they were written." — verified
against this file's own header remark above and ADR 0001 before shipping it: both are true, so the
sentence is drawn as literal text rather than reworded.
-->
<UserControl.Styles>
<Style Selector="Border.logtable">
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Background" Value="{StaticResource Pane}" />
<Setter Property="ClipToBounds" Value="True" />
</Style>
<Style Selector="ListBox.logrows > ListBoxItem /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style Selector="ListBox.logrows > ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
WHAT, on the activity table: created/changed/deleted, coloured the way the item badges are. "changed"
is the base rule's own colour — it is the default ActivityLogRowViewModel.Operation falls through to —
and .created/.deleted are declared after it so they win; Avalonia has no specificity and settles two
matching rules by declaration order, the same trap this application's other screens record.
-->
<Style Selector="Border.opchip">
<Setter Property="Background" Value="{StaticResource AccentWash}" />
</Style>
<Style Selector="Border.opchip > TextBlock">
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
</Style>
<Style Selector="Border.opchip.created">
<Setter Property="Background" Value="{StaticResource LiveWash}" />
</Style>
<Style Selector="Border.opchip.created > TextBlock">
<Setter Property="Foreground" Value="{StaticResource Live}" />
</Style>
<Style Selector="Border.opchip.deleted">
<Setter Property="Background" Value="{StaticResource DangerWash}" />
</Style>
<Style Selector="Border.opchip.deleted > TextBlock">
<Setter Property="Foreground" Value="{StaticResource Danger}" />
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto,*" Margin="26">
<!-- ============ THE HEADER ============ -->
<Grid Grid.Row="0" Margin="0,0,0,20" ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<TextBlock Grid.Column="0" Text="Logs" FontSize="33" FontWeight="Bold" LetterSpacing="-0.5"
Foreground="{StaticResource Text}" VerticalAlignment="Center" />
<Border Grid.Column="1" Classes="navtrack" Margin="14,0,0,0" Width="200" VerticalAlignment="Center">
<Grid ColumnDefinitions="*,*">
<Button Grid.Column="0" Classes="navseg" Classes.active="{Binding ShowsConnections}"
Content="CONNECTIONS" Command="{Binding ShowSectionCommand}"
CommandParameter="{x:Static vm:LogSection.Connections}" />
<Button Grid.Column="1" Classes="navseg" Classes.active="{Binding ShowsActivity}"
Content="KEYCHAIN" Command="{Binding ShowSectionCommand}"
CommandParameter="{x:Static vm:LogSection.Activity}" />
</Grid>
</Border>
<TextBlock Grid.Column="3" Classes="mono" Text="{Binding HeaderStatusLine}" FontSize="10.5"
Foreground="{StaticResource TextFaint}" Margin="0,0,14,0"
TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<Button Grid.Column="4" Classes="ghost" Height="40"
Command="{Binding RefreshCommand}" IsEnabled="{Binding !IsBusy}">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock FontFamily="{StaticResource IconFont}" FontSize="15" Text="&#xE5D5;"
Foreground="{StaticResource TextFaint}" />
<TextBlock Text="Refresh" FontSize="13.5" FontWeight="SemiBold"
Foreground="{StaticResource TextDim}" />
</StackPanel>
</Button>
</Grid>
<!-- ============ Connections ============ -->
<Border Grid.Row="1" Classes="logtable" IsVisible="{Binding ShowsConnections}">
<Grid RowDefinitions="Auto,*,Auto">
<Grid Grid.Row="0" ColumnDefinitions="1.2*,1.6*,88,72,90,*" Margin="20,14,20,10"
IsVisible="{Binding HasConnections}">
<TextBlock Grid.Column="0" Classes="label" Text="HOST" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="1" Classes="label" Text="ADDRESS" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="2" Classes="label" Text="LASTED" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="3" Classes="label" Text="KIND" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="4" Classes="label" Text="STARTED" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="5" Classes="label" Text="FROM" FontSize="9.5" LetterSpacing="1" />
</Grid>
<ListBox Grid.Row="1" x:Name="ConnectionList" Classes="logrows" Focusable="True"
ItemsSource="{Binding Connections}" Margin="12,0,12,10">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ConnectionLogRowViewModel">
<Grid ColumnDefinitions="1.2*,1.6*,88,72,90,*" Height="40" Margin="8,0,10,0">
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="9" Margin="12,0,8,0"
VerticalAlignment="Center">
<Ellipse Classes="dot" Classes.live="{Binding IsLive}" VerticalAlignment="Center" />
<TextBlock Classes="mono" Text="{Binding HostLabel}" FontSize="12.5" FontWeight="Medium"
Foreground="{StaticResource Text}" TextTrimming="CharacterEllipsis" />
</StackPanel>
<TextBlock Grid.Column="1" Classes="mono" Text="{Binding Address}" FontSize="11"
Foreground="{StaticResource TextGhost}" Margin="0,0,8,0"
TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Classes="mono" Text="{Binding Duration}" FontSize="11"
Foreground="{StaticResource TextGhost}" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Classes="mono" Text="{Binding Kind}" FontSize="10.5"
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Classes="mono" Text="{Binding Started}" FontSize="10.5"
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
<StackPanel Grid.Column="5" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
<TextBlock Classes="mono" Text="{Binding DeviceName}" FontSize="10.5"
Foreground="{StaticResource TextFaint}"
TextTrimming="CharacterEllipsis" />
<!--
Only when there is something to say. A connection that opened and closed says nothing
here; one that was refused says so, and that is the row worth finding in a long list.
-->
<Border Classes="chip warn" Padding="7,0" Height="18" IsVisible="{Binding HasOutcome}">
<TextBlock Text="{Binding Outcome}" FontSize="9.5" />
</Border>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="1" Classes="hint" Text="{Binding EmptyMessage}" FontSize="12"
Margin="24" HorizontalAlignment="Center" VerticalAlignment="Center"
TextAlignment="Center" MaxWidth="420"
IsVisible="{Binding !HasConnections}" />
<!-- ◆ THE FOOTER. Verified against this file's own header remark and ADR 0001; see the file-level note. -->
<Border Grid.Row="2" BorderBrush="{StaticResource Border}" BorderThickness="0,1,0,0"
Padding="20,12">
<StackPanel Orientation="Horizontal" Spacing="10">
<TextBlock FontFamily="{StaticResource IconFont}" FontSize="13" Text="&#xE897;"
Foreground="{StaticResource TextFaint}" />
<TextBlock Classes="hint" FontSize="11" TextWrapping="Wrap"
Foreground="{StaticResource TextFaint}"
Text="Both logs are ordinary synced keychain items — end-to-end encrypted. The server learns only that rows exist and when they were written." />
</StackPanel>
</Border>
</Grid>
</Border>
<!-- ============ Keychain changes ============ -->
<Border Grid.Row="1" Classes="logtable" IsVisible="{Binding ShowsActivity}">
<Grid RowDefinitions="Auto,*,Auto">
<Grid Grid.Row="0" ColumnDefinitions="1.4*,0.8*,0.8*,1.6*,0.8*" Margin="20,14,20,10"
IsVisible="{Binding HasActivity}">
<TextBlock Grid.Column="0" Classes="label" Text="ITEM" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="1" Classes="label" Text="TYPE" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="2" Classes="label" Text="WHAT" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="3" Classes="label" Text="FIELDS" FontSize="9.5" LetterSpacing="1" />
<TextBlock Grid.Column="4" Classes="label" Text="WHEN" FontSize="9.5" LetterSpacing="1" />
</Grid>
<ListBox Grid.Row="1" x:Name="ActivityList" Classes="logrows" Focusable="True"
ItemsSource="{Binding Activity}" Margin="12,0,12,10">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ActivityLogRowViewModel">
<Grid ColumnDefinitions="1.4*,0.8*,0.8*,1.6*,0.8*" Height="40" Margin="8,0,10,0">
<TextBlock Grid.Column="0" Classes="mono" Text="{Binding ItemLabel}" FontSize="12.5"
FontWeight="Medium" Foreground="{StaticResource Text}" Margin="4,0,8,0"
TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Classes="mono" Text="{Binding ItemKind}" FontSize="10.5"
Foreground="{StaticResource TextGhost}" VerticalAlignment="Center" />
<Border Grid.Column="2" Classes="opchip" Classes.created="{Binding IsCreated}"
Classes.deleted="{Binding IsDeleted}" Height="19" CornerRadius="5" Padding="8,0"
HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock Text="{Binding Operation}" FontSize="10" FontWeight="SemiBold" />
</Border>
<TextBlock Grid.Column="3" Classes="mono" Text="{Binding ChangedFields}" FontSize="10.5"
Foreground="{StaticResource TextFaint}" Margin="0,0,8,0"
TextTrimming="CharacterEllipsis" VerticalAlignment="Center"
IsVisible="{Binding HasChangedFields}" />
<TextBlock Grid.Column="4" Classes="mono" Text="{Binding At}" FontSize="10.5"
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="1" Classes="hint" Text="{Binding EmptyMessage}" FontSize="12"
Margin="24" HorizontalAlignment="Center" VerticalAlignment="Center"
TextAlignment="Center" MaxWidth="420"
IsVisible="{Binding !HasActivity}" />
<Border Grid.Row="2" BorderBrush="{StaticResource Border}" BorderThickness="0,1,0,0"
Padding="20,12">
<StackPanel Orientation="Horizontal" Spacing="10">
<TextBlock FontFamily="{StaticResource IconFont}" FontSize="13" Text="&#xE897;"
Foreground="{StaticResource TextFaint}" />
<TextBlock Classes="hint" FontSize="11" TextWrapping="Wrap"
Foreground="{StaticResource TextFaint}"
Text="Both logs are ordinary synced keychain items — end-to-end encrypted. The server learns only that rows exist and when they were written." />
</StackPanel>
</Border>
</Grid>
</Border>
</Grid>
</UserControl>