diff --git a/K8sFileBrowser/App.axaml b/K8sFileBrowser/App.axaml
index 2a7be7a..ee50a95 100644
--- a/K8sFileBrowser/App.axaml
+++ b/K8sFileBrowser/App.axaml
@@ -8,18 +8,18 @@
-
+
-
@@ -38,9 +38,10 @@
-
+
+
\ No newline at end of file
diff --git a/K8sFileBrowser/ApplicationHelper.cs b/K8sFileBrowser/ApplicationHelper.cs
index 1e02b71..67b28ab 100644
--- a/K8sFileBrowser/ApplicationHelper.cs
+++ b/K8sFileBrowser/ApplicationHelper.cs
@@ -14,9 +14,8 @@ public static class ApplicationHelper
{
public static async Task SaveFile(string? initialFolder, string? initialFile)
{
- Window? ret;
- if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop ||
- desktop.MainWindow is not { } wnd) return null;
+ if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop ||
+ desktop.MainWindow is not { } wnd) return null;
try
{
var filter = new List { new("All files") { Patterns = new List { "*" } } };
diff --git a/K8sFileBrowser/Assets/Icons.axaml b/K8sFileBrowser/Assets/Icons.axaml
new file mode 100644
index 0000000..6e4a286
--- /dev/null
+++ b/K8sFileBrowser/Assets/Icons.axaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/K8sFileBrowser/Models/FileInformation.cs b/K8sFileBrowser/Models/FileInformation.cs
index bf33e0f..ca9105a 100644
--- a/K8sFileBrowser/Models/FileInformation.cs
+++ b/K8sFileBrowser/Models/FileInformation.cs
@@ -9,8 +9,10 @@ public class FileInformation
public string Name { get; set; } = string.Empty;
public string Size { get; set; } = string.Empty;
public DateTimeOffset Date { get; set; } = DateTimeOffset.MinValue;
-
+
public bool IsFile => Type == FileType.File;
+ public bool IsDirectory => Type == FileType.Directory;
+ public bool IsUnknown => Type == FileType.Unknown;
}
public enum FileType
diff --git a/K8sFileBrowser/Services/KubernetesService.cs b/K8sFileBrowser/Services/KubernetesService.cs
index 7e80534..502ac2b 100644
--- a/K8sFileBrowser/Services/KubernetesService.cs
+++ b/K8sFileBrowser/Services/KubernetesService.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using k8s;
using k8s.KubeConfigModels;
using K8sFileBrowser.Models;
@@ -48,7 +49,7 @@ public class KubernetesService
{
var pods = _kubernetesClient.CoreV1.ListNamespacedPod(namespaceName);
var podList = pods != null
- ? pods.Items.Select(n =>
+ ? pods.Items.Select(n =>
new Pod
{
Name = n.Metadata.Name,
@@ -57,7 +58,7 @@ public class KubernetesService
: new List();
return podList;
}
-
+
public IList GetFiles(string namespaceName, string podName, string containerName, string path)
{
try
@@ -65,14 +66,17 @@ public class KubernetesService
var execResult = new KubernetesFileInformationResult(path);
var resultCode = _kubernetesClient
.NamespacedPodExecAsync(
- podName, namespaceName, containerName,
- new[] { "find", path, "-maxdepth", "1", "-exec", "stat", "-c", "%F|%n|%s|%Y", "{}", ";" },
+ podName, namespaceName, containerName,
+ new[] { "find", path, "-maxdepth", "1", "-exec", "stat", "-c", "%F|%n|%s|%Y", "{}", ";" },
true,
execResult.ParseFileInformationCallback, CancellationToken.None)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
- return execResult.FileInformations;
+ return execResult.FileInformations
+ .Where(f => f.Name != "." && f.Name != path)
+ .OrderBy(f => f.Type).ThenBy(f => f.Name)
+ .ToList();
}
catch (Exception e)
{
@@ -91,10 +95,10 @@ public class KubernetesService
}
- public void DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, FileInformation selectedFile, string? saveFileName)
+ public async Task DownloadFile(Namespace? selectedNamespace, Pod? selectedPod, FileInformation selectedFile, string? saveFileName)
{
Log.Information($"{selectedNamespace} - {selectedPod} - {selectedFile} - {saveFileName}");
-
- // TODO: this is done with Tar
+ await Task.Delay(10000);
+ // TODO: this is done with Tar
}
}
\ No newline at end of file
diff --git a/K8sFileBrowser/ViewModels/MainWindowViewModel.cs b/K8sFileBrowser/ViewModels/MainWindowViewModel.cs
index 6449cef..d0bbfc2 100644
--- a/K8sFileBrowser/ViewModels/MainWindowViewModel.cs
+++ b/K8sFileBrowser/ViewModels/MainWindowViewModel.cs
@@ -20,7 +20,7 @@ public class MainWindowViewModel : ViewModelBase
get => _selectedClusterContext;
set => this.RaiseAndSetIfChanged(ref _selectedClusterContext, value);
}
-
+
private readonly ObservableAsPropertyHelper> _namespaces;
public IEnumerable Namespaces => _namespaces.Value;
@@ -30,7 +30,7 @@ public class MainWindowViewModel : ViewModelBase
get => _selectedNamespace;
set => this.RaiseAndSetIfChanged(ref _selectedNamespace, value);
}
-
+
private readonly ObservableAsPropertyHelper> _pods;
public IEnumerable Pods => _pods.Value;
@@ -40,24 +40,31 @@ public class MainWindowViewModel : ViewModelBase
get => _selectedPod;
set => this.RaiseAndSetIfChanged(ref _selectedPod, value);
}
-
+
private readonly ObservableAsPropertyHelper> _fileInformation;
public IEnumerable FileInformation => _fileInformation.Value;
-
+
private FileInformation? _selectedFile;
public FileInformation? SelectedFile
{
get => _selectedFile;
set => this.RaiseAndSetIfChanged(ref _selectedFile, value);
}
-
+
private string? _selectedPath;
- public string? SelectedPath
+ public string? SelectedPath
{
get => _selectedPath;
set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
}
-
+
+ private bool _isDownloadActive;
+ public bool IsDownloadActive
+ {
+ get => _isDownloadActive;
+ set => this.RaiseAndSetIfChanged(ref _isDownloadActive, value);
+ }
+
public ReactiveCommand DownloadCommand { get; }
public ReactiveCommand ParentCommand { get; }
public ReactiveCommand OpenCommand { get; }
@@ -66,33 +73,39 @@ public class MainWindowViewModel : ViewModelBase
public MainWindowViewModel()
{
var kubernetesService = new KubernetesService();
-
+
var isFile = this
- .WhenAnyValue(x => x.SelectedFile)
- .Select(x => x is { Type: FileType.File });
-
+ .WhenAnyValue(x => x.SelectedFile, x => x.IsDownloadActive)
+ .Select(x => x is { Item1.Type: FileType.File, Item2: false });
+
var isDirectory = this
- .WhenAnyValue(x => x.SelectedFile)
- .Select(x => x is { Type: FileType.Directory });
-
+ .WhenAnyValue(x => x.SelectedFile, x => x.IsDownloadActive)
+ .Select(x => x is { Item1.Type: FileType.Directory, Item2: false });
+
var isNotRoot = this
- .WhenAnyValue(x => x.SelectedPath)
- .Select(x => x is not "/");
-
+ .WhenAnyValue(x => x.SelectedPath, x => x.IsDownloadActive)
+ .Select(x => x.Item1 is not "/" && !x.Item2);
+
OpenCommand = ReactiveCommand.Create(() =>
{
SelectedPath = SelectedFile != null ? SelectedFile!.Name : "/";
}, isDirectory, RxApp.MainThreadScheduler);
-
- DownloadCommand = ReactiveCommand.CreateFromTask(async () =>
+
+ DownloadCommand = ReactiveCommand.CreateFromTask(async () =>
{
+ await Observable.StartAsync(async () => {
var fileName = SelectedFile!.Name.Substring(SelectedFile!.Name.LastIndexOf('/') + 1, SelectedFile!.Name.Length - SelectedFile!.Name.LastIndexOf('/') - 1);
var saveFileName = await ApplicationHelper.SaveFile(".", fileName);
- kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedFile, saveFileName);
+ if (saveFileName != null)
+ {
+ IsDownloadActive = true;
+ await kubernetesService.DownloadFile(SelectedNamespace, SelectedPod, SelectedFile, saveFileName);
+ IsDownloadActive = false;
+ }
+ }, RxApp.TaskpoolScheduler);
+ }, isFile, RxApp.MainThreadScheduler);
- }, isFile, RxApp.TaskpoolScheduler);
-
- ParentCommand = ReactiveCommand.Create(() =>
+ ParentCommand = ReactiveCommand.Create(() =>
{
SelectedPath = SelectedPath![..SelectedPath!.LastIndexOf('/')];
if (SelectedPath!.Length == 0)
@@ -100,20 +113,20 @@ public class MainWindowViewModel : ViewModelBase
SelectedPath = "/";
}
}, isNotRoot, RxApp.MainThreadScheduler);
-
+
// read the cluster contexts
_namespaces = this
.WhenAnyValue(c => c.SelectedClusterContext)
.Throttle(TimeSpan.FromMilliseconds(10))
.Where(context => context != null)
- .Select(context =>
+ .Select(context =>
{
kubernetesService.SwitchClusterContext(context!);
return kubernetesService.GetNamespaces();
})
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.Namespaces);
-
+
// read the pods when the namespace changes
_pods = this
.WhenAnyValue(c => c.SelectedNamespace)
@@ -133,7 +146,7 @@ public class MainWindowViewModel : ViewModelBase
x.Item1))
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.FileInformation);
-
+
// reset the path when the pod or namespace changes
this.WhenAnyValue(c => c.SelectedPod, c => c.SelectedNamespace)
.Subscribe(x => SelectedPath = "/");
diff --git a/K8sFileBrowser/Views/MainWindow.axaml b/K8sFileBrowser/Views/MainWindow.axaml
index c7768e5..5af3685 100644
--- a/K8sFileBrowser/Views/MainWindow.axaml
+++ b/K8sFileBrowser/Views/MainWindow.axaml
@@ -3,18 +3,25 @@
xmlns:vm="using:K8sFileBrowser.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:models="clr-namespace:K8sFileBrowser.Models"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="K8sFileBrowser.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="K8sFileBrowser">
-
+
-
-
+
+
+
+
+ Download File...
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ SelectedItem="{Binding SelectedFile}"
+ >
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -99,5 +130,5 @@
-
+
\ No newline at end of file