diff --git a/Needlework.Net/Constants/BlobCacheKeys.cs b/Needlework.Net/Constants/BlobCacheKeys.cs
index c229115..533902b 100644
--- a/Needlework.Net/Constants/BlobCacheKeys.cs
+++ b/Needlework.Net/Constants/BlobCacheKeys.cs
@@ -3,5 +3,7 @@
public static class BlobCacheKeys
{
public static readonly string GithubLatestRelease = nameof(GithubLatestRelease);
+
+ public static readonly string AppSettings = nameof(AppSettings);
}
}
diff --git a/Needlework.Net/DataModels/AppSettings.cs b/Needlework.Net/DataModels/AppSettings.cs
new file mode 100644
index 0000000..4d7c782
--- /dev/null
+++ b/Needlework.Net/DataModels/AppSettings.cs
@@ -0,0 +1,13 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Needlework.Net.DataModels
+{
+ public partial class AppSettings : ObservableObject
+ {
+ [ObservableProperty]
+ private bool _isCheckForUpdates = true;
+
+ [ObservableProperty]
+ private bool _isCheckForSchema = true;
+ }
+}
diff --git a/Needlework.Net/Needlework.Net.csproj b/Needlework.Net/Needlework.Net.csproj
index c1c6e5b..6d06d67 100644
--- a/Needlework.Net/Needlework.Net.csproj
+++ b/Needlework.Net/Needlework.Net.csproj
@@ -30,6 +30,7 @@
+
diff --git a/Needlework.Net/Program.cs b/Needlework.Net/Program.cs
index 970acf9..5c3a514 100644
--- a/Needlework.Net/Program.cs
+++ b/Needlework.Net/Program.cs
@@ -14,6 +14,7 @@ using Needlework.Net.ViewModels.Pages.Console;
using Needlework.Net.ViewModels.Pages.Endpoints;
using Needlework.Net.ViewModels.Pages.Home;
using Needlework.Net.ViewModels.Pages.Schemas;
+using Needlework.Net.ViewModels.Pages.Settings;
using Needlework.Net.ViewModels.Pages.WebSocket;
using Needlework.Net.Views.MainWindow;
using Needlework.Net.Views.Pages.About;
@@ -21,6 +22,7 @@ using Needlework.Net.Views.Pages.Console;
using Needlework.Net.Views.Pages.Endpoints;
using Needlework.Net.Views.Pages.Home;
using Needlework.Net.Views.Pages.Schemas;
+using Needlework.Net.Views.Pages.Settings;
using Needlework.Net.Views.Pages.WebSocket;
using Projektanker.Icons.Avalonia;
using Projektanker.Icons.Avalonia.FontAwesome;
@@ -97,6 +99,8 @@ class Program
// WEBSOCKET
locator.Register(() => new WebSocketView());
locator.Register(() => new EventView());
+ // SETTINGS
+ locator.Register(() => new SettingsView());
builder.AddSingleton(locator);
}
@@ -132,6 +136,7 @@ class Program
builder.AddSingleton();
builder.AddSingleton();
builder.AddSingleton();
+ builder.AddSingleton();
}
private static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e)
diff --git a/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs b/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs
index b0f495e..d0cbdbb 100644
--- a/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs
+++ b/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs
@@ -1,8 +1,6 @@
using Avalonia;
using Avalonia.Media;
using Avalonia.Threading;
-using BlossomiShymae.Briar;
-using BlossomiShymae.Briar.Utils;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
@@ -11,7 +9,6 @@ using Needlework.Net.Constants;
using Needlework.Net.Extensions;
using Needlework.Net.Helpers;
using Needlework.Net.Messages;
-using Needlework.Net.Models;
using Needlework.Net.Services;
using Needlework.Net.ViewModels.Pages;
using Needlework.Net.Views.MainWindow;
@@ -20,8 +17,6 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
-using System.Net.Http.Json;
-using System.Reactive;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -33,25 +28,18 @@ public partial class MainWindowViewModel
{
private readonly DocumentService _documentService;
- private readonly GithubService _githubService;
-
private readonly NotificationService _notificationService;
private readonly DialogService _dialogService;
private readonly SchemaPaneService _schemaPaneService;
- private readonly IDisposable _checkForUpdatesDisposable;
-
- private readonly IDisposable _checkForSchemaVersionDisposable;
-
- public MainWindowViewModel(IEnumerable pages, DialogService dialogService, DocumentService documentService, NotificationService notificationService, GithubService githubService, SchemaPaneService schemaPaneService)
+ public MainWindowViewModel(IEnumerable pages, DialogService dialogService, DocumentService documentService, NotificationService notificationService, SchemaPaneService schemaPaneService)
{
_dialogService = dialogService;
_documentService = documentService;
_notificationService = notificationService;
_schemaPaneService = schemaPaneService;
- _githubService = githubService;
NavigationViewItems = pages
.OrderBy(p => p.Index)
@@ -89,42 +77,6 @@ public partial class MainWindowViewModel
}
});
- _checkForUpdatesDisposable = Observable.Timer(TimeSpan.Zero, Intervals.CheckForUpdates)
- .Select(time => Unit.Default)
- .Subscribe(async _ =>
- {
- try
- {
- await CheckForUpdatesAsync();
- }
- catch (Exception ex)
- {
- var message = "Failed to check for updates. Please check your internet connection or try again later.";
- this.Log()
- .Error(ex, message);
- _notificationService.Notify(AppInfo.Name, message, InfoBarSeverity.Error);
- _checkForUpdatesDisposable?.Dispose();
- }
- });
-
- _checkForSchemaVersionDisposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(5))
- .Select(time => Unit.Default)
- .Subscribe(async _ =>
- {
- try
- {
- await CheckForSchemaVersionAsync();
- }
- catch (Exception ex)
- {
- var message = "Failed to check for schema version. Please check your internet connection or try again later.";
- this.Log()
- .Error(ex, message);
- _notificationService.Notify(AppInfo.Name, message, InfoBarSeverity.Error);
- _checkForSchemaVersionDisposable?.Dispose();
- }
- });
-
WeakReferenceMessenger.Default.RegisterAll(this);
}
@@ -151,8 +103,6 @@ public partial class MainWindowViewModel
public List NavigationViewItems { get; private set; } = [];
- public bool IsSchemaVersionChecked { get; private set; }
-
public string AppName => AppInfo.Name;
public string Title => $"{AppInfo.Name} {AppInfo.Version}";
@@ -224,48 +174,6 @@ public partial class MainWindowViewModel
}
};
- private async Task CheckForUpdatesAsync()
- {
- var release = await _githubService.GetLatestReleaseAsync();
- if (release.IsLatest(AppInfo.Version))
- {
- this.Log()
- .Information("New version available: {TagName}", release.TagName);
- _notificationService.Notify(AppInfo.Name, $"New version available: {release.TagName}", InfoBarSeverity.Informational, null, "https://github.com/BlossomiShymae/Needlework.Net/releases/latest");
- _checkForUpdatesDisposable?.Dispose();
- }
- }
-
-
- private async Task CheckForSchemaVersionAsync()
- {
- if (!ProcessFinder.IsPortOpen()) return;
-
- var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync();
- var client = Connector.GetLcuHttpClientInstance();
- var currentSemVer = lcuSchemaDocument.Info.Version.Split('.');
- var systemBuild = await client.GetFromJsonAsync("/system/v1/builds") ?? throw new NullReferenceException();
- var latestSemVer = systemBuild.Version.Split('.');
-
- if (!IsSchemaVersionChecked)
- {
- this.Log()
- .Information("LCU Schema (current): {Version}", lcuSchemaDocument.Info.Version);
- this.Log()
- .Information("LCU Schema (latest): {Version}", systemBuild.Version);
- IsSchemaVersionChecked = true;
- }
-
- bool isVersionMatching = currentSemVer[0] == latestSemVer[0] && currentSemVer[1] == latestSemVer[1]; // Compare major and minor versions
- if (!isVersionMatching)
- {
- this.Log()
- .Warning("LCU Schema version mismatch: Current {CurrentVersion}, Latest {LatestVersion}", lcuSchemaDocument.Info.Version, systemBuild.Version);
- _notificationService.Notify(AppInfo.Name, $"LCU Schema is possibly outdated compared to latest system build. Consider submitting a pull request on dysolix/hasagi-types.\nCurrent: {string.Join(".", currentSemVer)}\nLatest: {string.Join(".", latestSemVer)}", InfoBarSeverity.Warning, null, "https://github.com/dysolix/hasagi-types#updating-the-types");
- _checkForSchemaVersionDisposable?.Dispose();
- }
- }
-
public async Task> PopulateAsync(string? searchText, CancellationToken cancellationToken)
{
if (searchText == null) return [];
diff --git a/Needlework.Net/ViewModels/Pages/Settings/SettingsViewModel.cs b/Needlework.Net/ViewModels/Pages/Settings/SettingsViewModel.cs
new file mode 100644
index 0000000..2a9dc34
--- /dev/null
+++ b/Needlework.Net/ViewModels/Pages/Settings/SettingsViewModel.cs
@@ -0,0 +1,197 @@
+using Akavache;
+using Avalonia.Threading;
+using BlossomiShymae.Briar;
+using BlossomiShymae.Briar.Utils;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using FluentAvalonia.UI.Controls;
+using Needlework.Net.Constants;
+using Needlework.Net.DataModels;
+using Needlework.Net.Extensions;
+using Needlework.Net.Models;
+using Needlework.Net.Services;
+using System;
+using System.Net.Http.Json;
+using System.Reactive;
+using System.Reactive.Linq;
+using System.Threading.Tasks;
+
+namespace Needlework.Net.ViewModels.Pages.Settings
+{
+ public partial class SettingsViewModel : PageBase, IEnableLogger
+ {
+ private readonly IBlobCache _blobCache;
+
+ private readonly IDisposable _checkForUpdatesDisposable;
+
+ private readonly IDisposable _checkForSchemaVersionDisposable;
+
+ private readonly GithubService _githubService;
+
+ private readonly DocumentService _documentService;
+
+ private readonly NotificationService _notificationService;
+
+ private readonly TaskCompletionSource _initializeTaskCompletionSource = new();
+
+ public SettingsViewModel(IBlobCache blobCache, GithubService githubService, DocumentService documentService, NotificationService notificationService) : base("Settings", "fa-solid fa-gear")
+ {
+ _blobCache = blobCache;
+ _githubService = githubService;
+ _documentService = documentService;
+ _notificationService = notificationService;
+
+ _checkForUpdatesDisposable = Observable.Timer(TimeSpan.Zero, Intervals.CheckForUpdates)
+ .Select(time => Unit.Default)
+ .Subscribe(async _ =>
+ {
+ try
+ {
+ await _initializeTaskCompletionSource.Task;
+ if (AppSettings!.IsCheckForUpdates)
+ {
+ await CheckForUpdatesAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ var message = "Failed to check for updates. Please check your internet connection or try again later.";
+ this.Log()
+ .Error(ex, message);
+ _notificationService.Notify(AppInfo.Name, message, InfoBarSeverity.Error);
+ _checkForUpdatesDisposable?.Dispose();
+ }
+ });
+
+ _checkForSchemaVersionDisposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(5))
+ .Select(time => Unit.Default)
+ .Subscribe(async _ =>
+ {
+ try
+ {
+ await _initializeTaskCompletionSource.Task;
+ if (AppSettings!.IsCheckForSchema)
+ {
+ await CheckForSchemaVersionAsync();
+ }
+ }
+ catch (Exception ex)
+ {
+ var message = "Failed to check for schema version. Please check your internet connection or try again later.";
+ this.Log()
+ .Error(ex, message);
+ _notificationService.Notify(AppInfo.Name, message, InfoBarSeverity.Error);
+ _checkForSchemaVersionDisposable?.Dispose();
+ }
+ });
+ }
+
+ [ObservableProperty]
+ private bool _isBusy = true;
+
+ [ObservableProperty]
+ private AppSettings? _appSettings;
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(UpdateCheckTitle), nameof(UpdateCheckIconValue), nameof(UpdateCheckLastChecked))]
+ private Guid _upToDateGuid = Guid.Empty;
+
+ public bool IsUpToDate { get; private set; }
+
+ public bool IsSchemaVersionChecked { get; private set; }
+
+ public string UpdateCheckTitle => IsUpToDate switch
+ {
+ true => "You're up to date",
+ false => "You're out of date"
+ };
+
+ public string UpdateCheckIconValue => IsUpToDate switch
+ {
+ true => "fa-heart-circle-check",
+ false => "fa-heart-circle-exclamation"
+ };
+
+ public string UpdateCheckLastChecked => $"Last checked: {DateTime.Now:dddd}, {DateTime.Now:T}";
+
+ partial void OnAppSettingsChanged(AppSettings? value)
+ {
+ if (AppSettings is AppSettings appSettings)
+ {
+ _blobCache.InsertObject(BlobCacheKeys.AppSettings, appSettings);
+ }
+ }
+
+ public override async Task InitializeAsync()
+ {
+ await Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ try
+ {
+ AppSettings = await _blobCache.GetObject(BlobCacheKeys.AppSettings);
+ }
+ catch (Exception ex)
+ {
+ this.Log()
+ .Warning(ex, "Failed to get application settings.");
+ AppSettings = new();
+ }
+ finally
+ {
+ AppSettings!.PropertyChanged += (s, e) => OnAppSettingsChanged((AppSettings?)s);
+ IsBusy = false;
+ _initializeTaskCompletionSource.SetResult(true);
+ }
+ });
+ }
+
+ [RelayCommand]
+ private async Task CheckForUpdatesAsync()
+ {
+ var release = await _githubService.GetLatestReleaseAsync();
+ if (release.IsLatest(AppInfo.Version))
+ {
+ this.Log()
+ .Information("New version available: {TagName}", release.TagName);
+ _notificationService.Notify(AppInfo.Name, $"New version available: {release.TagName}", InfoBarSeverity.Informational, null, "https://github.com/BlossomiShymae/Needlework.Net/releases/latest");
+ _checkForUpdatesDisposable?.Dispose();
+ IsUpToDate = false;
+ }
+ else
+ {
+ IsUpToDate = true;
+ }
+ UpToDateGuid = Guid.NewGuid();
+ }
+
+
+ private async Task CheckForSchemaVersionAsync()
+ {
+ if (!ProcessFinder.IsPortOpen()) return;
+
+ var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync();
+ var client = Connector.GetLcuHttpClientInstance();
+ var currentSemVer = lcuSchemaDocument.Info.Version.Split('.');
+ var systemBuild = await client.GetFromJsonAsync("/system/v1/builds") ?? throw new NullReferenceException();
+ var latestSemVer = systemBuild.Version.Split('.');
+
+ if (!IsSchemaVersionChecked)
+ {
+ this.Log()
+ .Information("LCU Schema (current): {Version}", lcuSchemaDocument.Info.Version);
+ this.Log()
+ .Information("LCU Schema (latest): {Version}", systemBuild.Version);
+ IsSchemaVersionChecked = true;
+ }
+
+ bool isVersionMatching = currentSemVer[0] == latestSemVer[0] && currentSemVer[1] == latestSemVer[1]; // Compare major and minor versions
+ if (!isVersionMatching)
+ {
+ this.Log()
+ .Warning("LCU Schema outdated: Current {CurrentVersion}, Latest {LatestVersion}", lcuSchemaDocument.Info.Version, systemBuild.Version);
+ _notificationService.Notify(AppInfo.Name, $"LCU Schema is outdated compared to latest system build. Consider submitting a pull request on dysolix/hasagi-types.\nCurrent: {string.Join(".", currentSemVer)}\nLatest: {string.Join(".", latestSemVer)}", InfoBarSeverity.Warning, null, "https://github.com/dysolix/hasagi-types#updating-the-types");
+ _checkForSchemaVersionDisposable?.Dispose();
+ }
+ }
+ }
+}
diff --git a/Needlework.Net/Views/Pages/Settings/SettingsView.axaml b/Needlework.Net/Views/Pages/Settings/SettingsView.axaml
new file mode 100644
index 0000000..427af8b
--- /dev/null
+++ b/Needlework.Net/Views/Pages/Settings/SettingsView.axaml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Needlework.Net/Views/Pages/Settings/SettingsView.axaml.cs b/Needlework.Net/Views/Pages/Settings/SettingsView.axaml.cs
new file mode 100644
index 0000000..dc91fb5
--- /dev/null
+++ b/Needlework.Net/Views/Pages/Settings/SettingsView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace Needlework.Net.Views.Pages.Settings;
+
+public partial class SettingsView : UserControl
+{
+ public SettingsView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file