diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj
index 1e26cb7..76e0183 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/FileTime.App.FrequencyNavigation.Abstractions.csproj
@@ -11,4 +11,8 @@
+
+
+
+
diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs
index a20f81a..7fd7a3f 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation.Abstractions/ViewModels/IFrequencyNavigationViewModel.cs
@@ -1,3 +1,4 @@
+using Avalonia.Input;
using FileTime.App.Core.ViewModels;
namespace FileTime.App.FrequencyNavigation.ViewModels;
@@ -9,4 +10,5 @@ public interface IFrequencyNavigationViewModel : IModalViewModel
string SearchText { get; set; }
string SelectedItem { get; set; }
void Close();
+ void HandleKeyDown(KeyEventArgs keyEventArgs);
}
\ No newline at end of file
diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation/Services/FrequencyNavigationService.cs b/src/AppCommon/FileTime.App.FrequencyNavigation/Services/FrequencyNavigationService.cs
index 5538bba..7fad564 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation/Services/FrequencyNavigationService.cs
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation/Services/FrequencyNavigationService.cs
@@ -89,7 +89,7 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
if (TryAgeContainerScores() || DateTime.Now - _lastSave > TimeSpan.FromMinutes(5))
{
}
-
+ //TODO: move to if above
await SaveStateAsync();
}
catch (Exception e)
@@ -113,7 +113,7 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
var itemsToRemove = new List();
foreach (var container in _containerScores)
{
- var newScore = (int) Math.Floor(container.Value.Score * 0.9);
+ var newScore = (int)Math.Floor(container.Value.Score * 0.9);
if (newScore > 0)
{
container.Value.Score = newScore;
@@ -136,14 +136,18 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
return new List();
_saveLock.Wait();
- var matchingContainers = _containerScores
- .Where(c => c.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
- .OrderBy(c => GetWeightedScore(c.Value.Score, c.Value.LastAccessed))
- .Select(c => c.Key)
- .ToList();
-
- _saveLock.Release();
- return matchingContainers;
+ try
+ {
+ return _containerScores
+ .Where(c => c.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
+ .OrderBy(c => GetWeightedScore(c.Value.Score, c.Value.LastAccessed))
+ .Select(c => c.Key)
+ .ToList();
+ }
+ finally
+ {
+ _saveLock.Release();
+ }
}
private int GetWeightedScore(int score, DateTime lastAccess)
@@ -159,36 +163,48 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
};
}
- public async Task InitAsync()
- {
- await LoadStateAsync();
- }
+ public async Task InitAsync() => await LoadStateAsync();
private async Task LoadStateAsync()
{
if (!File.Exists(_dbPath))
return;
- await _saveLock.WaitAsync();
- await using var dbStream = File.OpenRead(_dbPath);
- var containerScores = await JsonSerializer.DeserializeAsync>(dbStream);
- if (containerScores is null) return;
+ try
+ {
+ await _saveLock.WaitAsync();
+ _logger.LogTrace("Loading frequency navigation state from file '{DbPath}'", _dbPath);
+ await using var dbStream = File.OpenRead(_dbPath);
+ var containerScores = await JsonSerializer.DeserializeAsync>(dbStream);
+ if (containerScores is null) return;
- _containerScores = containerScores;
- _saveLock.Release();
+ _containerScores = containerScores;
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error loading frequency navigation state");
+ }
+ finally
+ {
+ _saveLock.Release();
+ }
}
- public async Task ExitAsync()
- {
- await SaveStateAsync();
- }
+ public async Task ExitAsync() => await SaveStateAsync();
private async Task SaveStateAsync()
{
await _saveLock.WaitAsync();
- _lastSave = DateTime.Now;
- await using var dbStream = File.OpenWrite(_dbPath);
- await JsonSerializer.SerializeAsync(dbStream, _containerScores);
- _saveLock.Release();
+ try
+ {
+ _lastSave = DateTime.Now;
+ await using var dbStream = File.Create(_dbPath);
+ await JsonSerializer.SerializeAsync(dbStream, _containerScores);
+ dbStream.Flush();
+ }
+ finally
+ {
+ _saveLock.Release();
+ }
}
}
\ No newline at end of file
diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs b/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs
index fb242ba..0403f4f 100644
--- a/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs
+++ b/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs
@@ -1,15 +1,22 @@
+using Avalonia.Input;
+using FileTime.App.Core.Services;
+using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels;
using FileTime.App.FrequencyNavigation.Services;
+using FileTime.Core.Models;
+using FileTime.Core.Timeline;
using MvvmGen;
namespace FileTime.App.FrequencyNavigation.ViewModels;
[ViewModel]
[Inject(typeof(IFrequencyNavigationService), "_frequencyNavigationService")]
+[Inject(typeof(IUserCommandHandlerService), "_userCommandHandlerService")]
+[Inject(typeof(ITimelessContentProvider), "_timelessContentProvider")]
public partial class FrequencyNavigationViewModel : IFrequencyNavigationViewModel
{
private string _searchText;
-
+
[Property] private IObservable _showWindow;
[Property] private List _filteredMatches;
[Property] private string _selectedItem;
@@ -20,7 +27,7 @@ public partial class FrequencyNavigationViewModel : IFrequencyNavigationViewMode
set
{
if (_searchText == value) return;
-
+
_searchText = value;
OnPropertyChanged();
@@ -29,18 +36,48 @@ public partial class FrequencyNavigationViewModel : IFrequencyNavigationViewMode
}
public void Close()
+ => _frequencyNavigationService.CloseNavigationWindow();
+
+ public async void HandleKeyDown(KeyEventArgs keyEventArgs)
{
- _frequencyNavigationService.CloseNavigationWindow();
+ if (keyEventArgs.Key == Key.Down)
+ {
+ var nextItem = FilteredMatches.SkipWhile(i => i != SelectedItem).Skip(1).FirstOrDefault();
+
+ if (nextItem is not null)
+ {
+ SelectedItem = nextItem;
+ }
+ }
+ else if (keyEventArgs.Key == Key.Up)
+ {
+ var previousItem = FilteredMatches.TakeWhile(i => i != SelectedItem).LastOrDefault();
+
+ if (previousItem is not null)
+ {
+ SelectedItem = previousItem;
+ }
+ }
+ else if (keyEventArgs.Key == Key.Enter)
+ {
+ var targetContainer = await _timelessContentProvider.GetItemByFullNameAsync(new FullName(SelectedItem), PointInTime.Present);
+ var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, targetContainer));
+ await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
+ Close();
+ }
}
partial void OnInitialize()
- {
- _showWindow = _frequencyNavigationService.ShowWindow;
- }
+ => _showWindow = _frequencyNavigationService.ShowWindow;
private void UpdateFilteredMatches()
{
FilteredMatches = new List(_frequencyNavigationService.GetMatchingContainers(_searchText));
+ if (FilteredMatches.Contains(SelectedItem)) return;
+
+ SelectedItem = FilteredMatches.Count > 0
+ ? FilteredMatches[0]
+ : null;
}
string IModalViewModel.Name => "FrequencyNavigation";
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json b/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json
index bfd4587..cca6606 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/appsettings.Development.json
@@ -1,7 +1,7 @@
{
"Serilog": {
"MinimumLevel": {
- "Default": "Debug",
+ "Default": "Verbose",
"Override": {
"Microsoft": "Information",
"System": "Warning"
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs
index debc025..e89e6e0 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs
@@ -18,6 +18,8 @@ public class ToastMessageSink : ILogEventSink
if (logEvent.Level >= LogEventLevel.Error)
{
var message = logEvent.RenderMessage();
+ if (logEvent.Exception is not null)
+ message += $" {logEvent.Exception.Message}";
dialogService.ShowToastMessage(message);
}
}
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Resources/Styles.axaml b/src/GuiApp/Avalonia/FileTime.GuiApp/Resources/Styles.axaml
index 8708c7a..54a4d7f 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp/Resources/Styles.axaml
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Resources/Styles.axaml
@@ -40,6 +40,21 @@
+
+
+
+
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml
index d6270cb..5021322 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml
@@ -9,21 +9,28 @@
d:DesignWidth="800"
x:CompileBindings="True"
x:DataType="viewModels:IFrequencyNavigationViewModel"
+ Background="{DynamicResource ContainerBackgroundColor}"
mc:Ignorable="d">
+
+
+
-
-
+ Classes="CommandPalette"
+ Items="{Binding FilteredMatches}"
+ SelectedItem="{Binding SelectedItem}">
+
-
-
+
+
\ No newline at end of file
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml.cs
index 72701a0..7745e68 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml.cs
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/FrequencyNavigation.axaml.cs
@@ -26,5 +26,9 @@ public partial class FrequencyNavigation : UserControl
{
viewModel.Close();
}
+ else
+ {
+ viewModel.HandleKeyDown(e);
+ }
}
}
\ No newline at end of file
diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml
index 35360d9..d156d64 100644
--- a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml
+++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml
@@ -719,12 +719,13 @@
-
+
+
+