3 Commits

Author SHA1 Message Date
72f3ac90cd Better UI and Reactive Attribute 2023-08-10 19:24:34 +02:00
4dce6e3d38 fix screnshot path 2023-08-08 05:14:36 +02:00
4e91f5ce70 added screenshot to README.md 2023-08-08 05:12:11 +02:00
8 changed files with 73 additions and 81 deletions

View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ReactiveUI />
</Weavers>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" />
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -9,7 +9,7 @@
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
<Version>0.0.8</Version>
<Version>0.0.9</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>TRACE</DefineConstants>
@@ -31,6 +31,7 @@
<PackageReference Include="Avalonia.Xaml.Interactions" Version="11.0.2" />
<PackageReference Include="Avalonia.Xaml.Interactivity" Version="11.0.2" />
<PackageReference Include="KubernetesClient" Version="11.0.44" />
<PackageReference Include="ReactiveUI.Fody" Version="19.4.1" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />

View File

@@ -19,6 +19,8 @@ public class FileInformation
public string Size { get; set; } = string.Empty;
public DateTimeOffset? Date { get; set; }
public string DateTimeOffsetString => Date?.ToString("yyyy-MM-dd HH:mm:ss") ?? string.Empty;
public bool IsFile => Type == FileType.File;
public bool IsDirectory => Type == FileType.Directory;
public bool IsUnknown => Type == FileType.Unknown;

View File

@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using K8sFileBrowser.Models;
using K8sFileBrowser.Services;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace K8sFileBrowser.ViewModels;
@@ -16,81 +17,39 @@ public class MainWindowViewModel : ViewModelBase
private ObservableAsPropertyHelper<IEnumerable<ClusterContext>> _clusterContexts = null!;
public IEnumerable<ClusterContext> ClusterContexts => _clusterContexts.Value;
private ClusterContext? _selectedClusterContext;
[Reactive]
public ClusterContext? SelectedClusterContext { get; set; }
public ClusterContext? SelectedClusterContext
{
get => _selectedClusterContext;
set => this.RaiseAndSetIfChanged(ref _selectedClusterContext, value);
}
[Reactive]
public IEnumerable<Namespace> Namespaces { get; set; }
private IEnumerable<Namespace> _namespaces = null!;
public IEnumerable<Namespace> Namespaces
{
get => _namespaces;
set => this.RaiseAndSetIfChanged(ref _namespaces, value);
}
private Namespace? _selectedNamespace;
public Namespace? SelectedNamespace
{
get => _selectedNamespace;
set => this.RaiseAndSetIfChanged(ref _selectedNamespace, value);
}
[Reactive]
public Namespace? SelectedNamespace { get; set; }
private ObservableAsPropertyHelper<IEnumerable<Pod>> _pods = null!;
public IEnumerable<Pod> Pods => _pods.Value;
private Pod? _selectedPod;
[Reactive]
public Pod? SelectedPod { get; set; }
public Pod? SelectedPod
{
get => _selectedPod;
set => this.RaiseAndSetIfChanged(ref _selectedPod, value);
}
private IEnumerable<Container>? _containers;
public IEnumerable<Container>? Containers
{
get => _containers;
set => this.RaiseAndSetIfChanged(ref _containers, value);
}
[Reactive]
public IEnumerable<Container>? Containers { get; set; }
private Container? _selectedContainer;
public Container? SelectedContainer
{
get => _selectedContainer;
set => this.RaiseAndSetIfChanged(ref _selectedContainer, value);
}
[Reactive]
public Container? SelectedContainer { get; set; }
private ObservableAsPropertyHelper<IEnumerable<FileInformation>> _fileInformation = null!;
public IEnumerable<FileInformation> FileInformation => _fileInformation.Value;
private FileInformation? _selectedFile;
[Reactive]
public FileInformation? SelectedFile { get; set; }
public FileInformation? SelectedFile
{
get => _selectedFile;
set => this.RaiseAndSetIfChanged(ref _selectedFile, value);
}
[Reactive]
public string? SelectedPath { get; set; }
private string? _selectedPath;
[Reactive]
public Message Message { get; set; }
public string? SelectedPath
{
get => _selectedPath;
set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
}
private Message _message = null!;
public Message Message
{
get => _message;
set => this.RaiseAndSetIfChanged(ref _message, value);
}
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; } = null!;
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; } = null!;
@@ -143,7 +102,7 @@ public class MainWindowViewModel : ViewModelBase
.ObserveOn(RxApp.TaskpoolScheduler)
.Subscribe(_ => SelectedPath = "/");
}
private void RegisterReadContainers()
{
// read the file information when the path changes
@@ -155,7 +114,7 @@ public class MainWindowViewModel : ViewModelBase
: x.Item1.Containers.Select(c => new Container {Name = c}))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe( x => Containers = x);
this.WhenAnyValue(x => x.Containers)
.Throttle(new TimeSpan(10))
.ObserveOn(RxApp.MainThreadScheduler)
@@ -318,14 +277,14 @@ public class MainWindowViewModel : ViewModelBase
}
catch (Exception e)
{
RxApp.MainThreadScheduler.Schedule(Action);
RxApp.MainThreadScheduler.Schedule(Action);
return new List<Namespace>();
async void Action() => await ShowErrorMessage(e.Message);
}
}
private IList<FileInformation> GetFileInformation(IKubernetesService kubernetesService,
private IList<FileInformation> GetFileInformation(IKubernetesService kubernetesService,
string path, Pod pod, Namespace nameSpace, Container container)
{
var kubernetesFileInformation = kubernetesService.GetFiles(
@@ -333,7 +292,7 @@ public class MainWindowViewModel : ViewModelBase
// when the path is root, we don't want to show the parent directory
if (SelectedPath is not { Length: > 1 }) return kubernetesFileInformation;
// add the parent directory
var parent = SelectedPath[..SelectedPath.LastIndexOf('/')];
if (string.IsNullOrEmpty(parent))
@@ -358,7 +317,7 @@ public class MainWindowViewModel : ViewModelBase
IsError = false
};
}
private async Task ShowErrorMessage(string message)
{
Message = new Message
@@ -370,7 +329,7 @@ public class MainWindowViewModel : ViewModelBase
await Task.Delay(7000);
HideWorkingMessage();
}
private void HideWorkingMessage()
{
Message = new Message

View File

@@ -90,7 +90,9 @@
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="4" Margin="10" HorizontalAlignment="Right">
<Button Command="{Binding DownloadLogCommand}" VerticalAlignment="Center" ToolTip.Tip="Download Container Log" Margin="0 0 48 0 ">
<PathIcon Data="{StaticResource document_one_page_regular}"></PathIcon>
</Button>
<Button Command="{Binding ParentCommand}" VerticalAlignment="Center" ToolTip.Tip="Go To Parent Directory">
<PathIcon Data="{StaticResource arrow_curve_up_left_regular}"></PathIcon>
</Button>
@@ -100,9 +102,6 @@
<Button Command="{Binding DownloadCommand}" VerticalAlignment="Center" ToolTip.Tip="Download File">
<PathIcon Data="{StaticResource arrow_download_regular}"></PathIcon>
</Button>
<Button Command="{Binding DownloadLogCommand}" VerticalAlignment="Center" ToolTip.Tip="Download Pod Log">
<PathIcon Data="{StaticResource document_one_page_regular}"></PathIcon>
</Button>
</StackPanel>
</Grid>
<DataGrid Grid.Row="1"
@@ -121,11 +120,11 @@
<Setter Property="Padding" Value="10"></Setter>
</Style>
<Style Selector="DataGridCell">
<Setter Property="FontSize" Value="12"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
</Style>
</DataGrid.Styles>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Type">
<DataGridTemplateColumn Header="Type" CanUserSort="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="models:FileInformation">
<Border ToolTip.Tip="{Binding Type}" Background="Transparent">
@@ -144,7 +143,7 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Name" Width="*">
<DataGridTemplateColumn Header="Name" Width="*" CanUserSort="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="models:FileInformation">
<Border Background="Transparent">
@@ -158,11 +157,11 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Size">
<DataGridTemplateColumn Header="Size" CanUserSort="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="models:FileInformation">
<Border Background="Transparent">
<TextBlock Text="{Binding Size}" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Size}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 8 0"/>
<Interaction.Behaviors>
<EventTriggerBehavior EventName="DoubleTapped">
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window }}" />
@@ -172,14 +171,14 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Date">
<DataGridTemplateColumn Header="Date" CanUserSort="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="models:FileInformation">
<Border Background="Transparent">
<TextBlock Text="{Binding Date}" VerticalAlignment="Center"/>
<TextBlock Text="{Binding DateTimeOffsetString}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 8 0"/>
<Interaction.Behaviors>
<EventTriggerBehavior EventName="DoubleTapped">
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window }}" />
<InvokeCommandAction Command="{Binding ((vm:MainWindowViewModel)DataContext).OpenCommand, RelativeSource={RelativeSource AncestorType=Window}}" />
</EventTriggerBehavior>
</Interaction.Behaviors>
</Border>

View File

@@ -2,3 +2,5 @@
A UI tool for downloading files from a Pod.
The application is also the first Avalonia UI and C# UI app I have written.
![screenshot of K8sFileBrowser](https://github.com/frosch95/K8sFileBrowser/blob/master/screenshot.png?raw=true)

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB