diff --git a/Emerald/App.xaml.cs b/Emerald/App.xaml.cs index 16414ea8..d643e6a3 100644 --- a/Emerald/App.xaml.cs +++ b/Emerald/App.xaml.cs @@ -5,42 +5,55 @@ using Uno.UI.HotDesign; using Microsoft.Extensions.DependencyInjection; using Emerald.CoreX.Store.Modrinth; +using System.Diagnostics; +using System; +using Emerald.Helpers; namespace Emerald; public partial class App : Application { + private Helpers.Settings.SettingsSystem SS; /// /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// public App() { - this.InitializeComponent(); + this.InitializeComponent(); + + // Register exception handlers + + this.UnhandledException += App_UnhandledException; + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; } + public Window? MainWindow { get; private set; } protected IHost? Host { get; private set; } - public void ConfigureServices(IServiceCollection services) + private void ConfigureServices(IServiceCollection services) { - + //Stores services.AddTransient(provider => new ModStore(typeof(ModStore).Log())); services.AddTransient(provider => new PluginStore(typeof(PluginStore).Log())); services.AddTransient(provider => new ResourcePackStore(typeof(ResourcePackStore).Log())); services.AddTransient(provider => new ResourcePackStore(typeof(ShaderStore).Log())); services.AddTransient(provider => new ModpackStore(typeof(ModpackStore).Log())); - + + //Settings + services.AddSingleton(); + + } - protected override void OnLaunched(LaunchActivatedEventArgs args) + protected override async void OnLaunched(LaunchActivatedEventArgs args) { - var logPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Emerald", "logs", "app_.log"); - var builder = this.CreateBuilder(args) .Configure(host => host #if DEBUG @@ -54,7 +67,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) .WriteTo.File(logPath, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7, - outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{Method}) {Message}{NewLine}{Exception}")) + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}) {Message}{NewLine}{Exception}")) .ConfigureServices((context, services) => { @@ -71,8 +84,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) Host = builder.Build(); //Help me. - ServiceLocator.SetLocatorProvider(() => new Emerald.Helpers.Services.ServiceProviderLocator(Host!.Services)); + ServiceLocator.SetLocatorProvider(() => new Emerald.Services.ServiceProviderLocator(Host!.Services)); + SS = ServiceLocator.Current.GetInstance(); this.Log().LogInformation("New Instance was created. Logs are being saved at: {logPath}",logPath); // Do not repeat app initialization when the Window already has content, @@ -82,6 +96,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); + //load settings, + SS.LoadData(); + // Place the frame in the current Window MainWindow.Content = rootFrame; } @@ -95,10 +112,102 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) } // Ensure the current window is active MainWindow.Activate(); + + MainWindow.Closed += MainWindow_Closed; + } + + private void MainWindow_Closed(object sender, WindowEventArgs args) + { + //save settings, + SS.SaveData(); } /// /// Gets the current instance in use /// public new static App Current => (App)Application.Current; + + #region UnhandledExceptions + private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) + { + var logPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Emerald"); + e.Handled = true; + LogUnhandledException(e.Exception, "UI UnhandledException"); + ShowPlatformErrorDialog($"An unexpected error occurred. The application needs to be closed.\n see logs at {logPath} for more details"); + } + + private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) + { + var logPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Emerald"); + LogUnhandledException((Exception)e.ExceptionObject, "AppDomain UnhandledException"); + ShowPlatformErrorDialog($"A critical error occurred. The application needs to be closed.\n see logs at {logPath} for more details"); + } + + private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) + { + var logPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Emerald"); + e.SetObserved(); + LogUnhandledException(e.Exception, "Task UnobservedException"); + ShowPlatformErrorDialog($"A unobserved error occurred. The application needs to be closed.\n see logs at {logPath} for more details"); + } + + private void LogUnhandledException(Exception exception, string source) + { + try + { + this.Log().LogCritical(exception, + "Unhandled exception ({Source}). Platform: {Platform}", + source, + DirectResoucres.Platform + ); + + // Save to crash file (platform-specific path) + var crashPath = Path.Combine( +#if WINDOWS + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), +#else + Environment.GetFolderPath(Environment.SpecialFolder.Personal), +#endif + "Emerald", + "crashes", + $"crash_{DateTime.Now:yyyyMMdd_HHmmss}.txt" + ); + + Directory.CreateDirectory(Path.GetDirectoryName(crashPath)); + File.WriteAllText(crashPath, $""" + Exception Details: + Time: {DateTime.Now} + Platform: {DirectResoucres.Platform} + Type: {exception.GetType().FullName} + Message: {exception.Message} + Stack Trace: {exception.StackTrace} + """); + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to log unhandled exception: {ex.Message}"); + } + } + + private async void ShowPlatformErrorDialog(string message) + { + try + { + await MessageBox.Show("AppCrash".Localize(), message, Helpers.Enums.MessageBoxButtons.Ok); + Application.Current.Exit(); + + } + catch (Exception ex) + { + Debug.WriteLine($"Error: {message}"); + } + Process.GetCurrentProcess().Kill(); + } + #endregion } diff --git a/Emerald/DirectResources.cs b/Emerald/DirectResources.cs index f72ac03c..240d3162 100644 --- a/Emerald/DirectResources.cs +++ b/Emerald/DirectResources.cs @@ -18,6 +18,21 @@ public static int MinRAM public static string SettingsAPIVersion => "1.3"; + public static string Platform + { + get + { +#if WINDOWS + return "Windows"; +#elif MACCATALYST + return "OSX"; +#elif DESKTOP + return "Skia"; +#else + return "Unknown"; +#endif + } + } public static string BuildType { get diff --git a/Emerald/Emerald.csproj b/Emerald/Emerald.csproj index 9372b37a..59731572 100644 --- a/Emerald/Emerald.csproj +++ b/Emerald/Emerald.csproj @@ -54,6 +54,7 @@ + @@ -98,4 +99,12 @@ + + + + + + + + diff --git a/Emerald/Helpers/Enums/MessageBox.cs b/Emerald/Helpers/Enums/MessageBox.cs new file mode 100644 index 00000000..5a5b58a6 --- /dev/null +++ b/Emerald/Helpers/Enums/MessageBox.cs @@ -0,0 +1,22 @@ +namespace Emerald.Helpers.Enums; + +public enum MessageBoxResults +{ + Ok, + Cancel, + Yes, + No, + CustomResult1, + CustomResult2, + OpenFailed +} + +public enum MessageBoxButtons +{ + Ok, + OkCancel, + YesNo, + YesNoCancel, + Custom, + CustomWithCancel +} diff --git a/Emerald/Helpers/Extensions.cs b/Emerald/Helpers/Extensions.cs index 736b98e6..fdf846c5 100644 --- a/Emerald/Helpers/Extensions.cs +++ b/Emerald/Helpers/Extensions.cs @@ -1,3 +1,4 @@ +using CommonServiceLocator; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.Windows.ApplicationModel.Resources; @@ -78,7 +79,7 @@ public static ContentDialog ToContentDialog(this UIElement content, string title Content = content, Padding = new(12) } : content, - RequestedTheme = (ElementTheme)Settings.SettingsSystem.Settings.App.Appearance.Theme + RequestedTheme = (ElementTheme)ServiceLocator.Current.GetInstance< Helpers.Settings.SettingsSystem>().Settings.App.Appearance.Theme }; App.Current.Log().LogInformation("Created ContentDialog with title: {title}", title); return dialog; diff --git a/Emerald/Helpers/MessageBox.cs b/Emerald/Helpers/MessageBox.cs new file mode 100644 index 00000000..7b1010c6 --- /dev/null +++ b/Emerald/Helpers/MessageBox.cs @@ -0,0 +1,193 @@ +using CommonServiceLocator; +using Emerald.Helpers; +using Emerald.Helpers.Enums; +using Microsoft.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; +using System; +using System.Threading.Tasks; + +namespace Emerald.Helpers; + +/// +/// A as a MessageBox...
+/// Copied From the Emerald.UWP +///
+public partial class MessageBox : ContentDialog +{ + public MessageBoxResults Result { get; set; } = MessageBoxResults.Cancel; + + public MessageBox(string title, string caption, MessageBoxButtons buttons, string cusbtn1 = null, string cusbtn2 = null) + { + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; + Title = title; + Content = new TextBlock { Text = caption }; + + if (buttons == MessageBoxButtons.Ok) + { + PrimaryButtonText = ""; + SecondaryButtonText = "OK".Localize(); + DefaultButton = ContentDialogButton.None; + } + else if (buttons == MessageBoxButtons.OkCancel) + { + PrimaryButtonText = "OK".Localize(); + SecondaryButtonText = "Cancel".Localize(); + DefaultButton = ContentDialogButton.Primary; + } + else if (buttons == MessageBoxButtons.YesNoCancel) + { + PrimaryButtonText = "Yes".Localize(); + SecondaryButtonText = "No".Localize(); + CloseButtonText = "Cancel".Localize(); + DefaultButton = ContentDialogButton.Primary; + } + else if (buttons == MessageBoxButtons.YesNo) + { + PrimaryButtonText = "Yes".Localize(); + SecondaryButtonText = "No".Localize(); + DefaultButton = ContentDialogButton.Primary; + } + else if (buttons == MessageBoxButtons.Custom) + { + if (!string.IsNullOrEmpty(cusbtn1)) + { + PrimaryButtonText = cusbtn1; + } + if (!string.IsNullOrEmpty(cusbtn2)) + { + SecondaryButtonText = cusbtn2; + } + if (string.IsNullOrEmpty(cusbtn2) && string.IsNullOrEmpty(cusbtn1)) + { + PrimaryButtonText = "Yes".Localize(); + SecondaryButtonText = "No".Localize(); + DefaultButton = ContentDialogButton.Primary; + } + } + else if (buttons == MessageBoxButtons.CustomWithCancel) + { + if (!string.IsNullOrEmpty(cusbtn1)) + { + PrimaryButtonText = cusbtn1; + } + if (!string.IsNullOrEmpty(cusbtn2)) + { + SecondaryButtonText = cusbtn2; + } + if (string.IsNullOrEmpty(cusbtn2) && string.IsNullOrEmpty(cusbtn1)) + { + DefaultButton = ContentDialogButton.Primary; + PrimaryButtonText = "Yes".Localize(); + SecondaryButtonText = "No".Localize(); + } + + CloseButtonText = "Cancel".Localize(); + } + + PrimaryButtonClick += ContentDialog_PrimaryButtonClick; + SecondaryButtonClick += ContentDialog_SecondaryButtonClick; + } + + private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + if (sender.PrimaryButtonText == "OK".Localize()) + { + Result = MessageBoxResults.Ok; + } + else if (sender.PrimaryButtonText == "Yes".Localize()) + { + Result = MessageBoxResults.Yes; + } + else + { + Result = MessageBoxResults.CustomResult1; + } + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + if (sender.SecondaryButtonText == "OK".Localize()) + { + Result = MessageBoxResults.Ok; + } + else if (sender.SecondaryButtonText == "Cancel".Localize()) + { + Result = MessageBoxResults.Cancel; + } + else if (sender.SecondaryButtonText == "No".Localize()) + { + Result = MessageBoxResults.No; + } + else + { + Result = MessageBoxResults.CustomResult2; + } + } + + public static async Task Show(string title, string caption, MessageBoxButtons buttons, string customResult1 = null, string customResult2 = null, bool waitUntilOpens = true) + { + var theme = ServiceLocator.IsLocationProviderSet ? + (ElementTheme)ServiceLocator.Current.GetInstance().Settings.App.Appearance.Theme : + ElementTheme.Default; + var d = new MessageBox(title, caption, buttons, customResult1, customResult2) + { + XamlRoot = App.Current.MainWindow.Content.XamlRoot, + RequestedTheme = theme + }; + + if (waitUntilOpens) + { + bool notOpen = true; + while (notOpen) + { + try + { + await d.ShowAsync(); + notOpen = false; + } + catch (NullReferenceException) // XamlRoot can be null + { + notOpen = false; + return MessageBoxResults.OpenFailed; + } + } + return d.Result; + } + + try + { + await d.ShowAsync(); + } + catch + { + return MessageBoxResults.OpenFailed; + } + + return d.Result; + } + + public static async Task Show(string text) + { + var theme = ServiceLocator.IsLocationProviderSet ? + (ElementTheme)ServiceLocator.Current.GetInstance().Settings.App.Appearance.Theme : + ElementTheme.Default; + var d = new MessageBox("Information".Localize(), text, MessageBoxButtons.Ok) + { + XamlRoot = App.Current.MainWindow.Content.XamlRoot, + RequestedTheme = theme + }; + + try + { + await d.ShowAsync(); + } + catch + { + return MessageBoxResults.OpenFailed; + } + + return d.Result; + } +} diff --git a/Emerald/Helpers/Settings/JSON.cs b/Emerald/Helpers/Settings/JSON.cs index c1859a55..7370ba61 100644 --- a/Emerald/Helpers/Settings/JSON.cs +++ b/Emerald/Helpers/Settings/JSON.cs @@ -13,7 +13,7 @@ namespace Emerald.Helpers.Settings.JSON; public class JSON : Models.Model { public string Serialize() - => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }); + => JsonSerializer.Serialize(this); } public class SettingsBackup : JSON @@ -58,7 +58,7 @@ public class Settings : JSON } }; - // public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion; + public string APIVersion { get; set; } = DirectResoucres.SettingsAPIVersion; public DateTime LastSaved { get; set; } = DateTime.Now; public Minecraft Minecraft { get; set; } = new(); diff --git a/Emerald/Helpers/Settings/SettingsSystem.cs b/Emerald/Helpers/Settings/SettingsSystem.cs index 5a25b55a..4d4ac25a 100644 --- a/Emerald/Helpers/Settings/SettingsSystem.cs +++ b/Emerald/Helpers/Settings/SettingsSystem.cs @@ -1,4 +1,5 @@ using Emerald.Helpers.Settings.JSON; +using Microsoft.Extensions.Logging; using System.Text.Json; using System; using System.Collections.Generic; @@ -7,105 +8,142 @@ using Windows.Storage; namespace Emerald.Helpers.Settings; - -public static class SettingsSystem +public class SettingsSystem { - public static JSON.Settings Settings { get; private set; } = JSON.Settings.CreateNew(); - public static Account[] Accounts { get; set; } - - public static event EventHandler? APINoMatch; -// public static T GetSerializedFromSettings(string key, T def) -// { -// string json; -// try -// { -// json = ApplicationData.Current.RoamingSettings.Values[key] as string; -// return JsonSerializer.Deserialize(json); -// } -// catch -// { -// json = JsonSerializer.Serialize(def); -// ApplicationData.Current.RoamingSettings.Values[key] = json; -// return def; -// } -// } -// public static void LoadData() -// { -// Settings = GetSerializedFromSettings("Settings", JSON.Settings.CreateNew()); -// Accounts = GetSerializedFromSettings("Accounts", Array.Empty()); - -// if (Settings.APIVersion != DirectResoucres.SettingsAPIVersion) -// { -// APINoMatch?.Invoke(null, ApplicationData.Current.RoamingSettings.Values["Settings"] as string); -// ApplicationData.Current.RoamingSettings.Values["Settings"] = JSON.Settings.CreateNew().Serialize(); -// Settings = JsonSerializer.Deserialize(ApplicationData.Current.RoamingSettings.Values["Settings"] as string); -// } -// } - -// public static async Task CreateBackup(string system) -// { -// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); -// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json); - -// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList(); -// bl.Add(new SettingsBackup() { Time = DateTime.Now, Backup = system }); -// l.AllBackups = bl.ToArray(); -// json = l.Serialize(); - -// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); -// } - -// public static async Task DeleteBackup(int Index) -// { -// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); -// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json); - -// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList(); -// bl.RemoveAt(Index); -// l.AllBackups = bl.ToArray(); -// json = l.Serialize(); - -// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); -// } - -// public static async Task DeleteBackup(DateTime time) -// { -// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); -// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json); - -// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList(); -// bl.Remove(x => x.Time == time); -// l.AllBackups = bl.ToArray(); -// json = l.Serialize(); - -// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); -// } -// public static async Task RenameBackup(DateTime time, string name) -// { -// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); -// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json); - -// var bl = l.AllBackups == null ? new List() : l.AllBackups.ToList(); -// bl.FirstOrDefault(x => x.Time == time).Name = name; -// l.AllBackups = bl.ToArray(); -// json = l.Serialize(); - -// await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); -// } - -// public static async Task> GetBackups() -// { -// string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); -// var l = json.IsNullEmptyOrWhiteSpace() ? new Backups() : JsonSerializer.Deserialize(json); - -// return l.AllBackups == null ? new List() : l.AllBackups.ToList(); -// } - -// public static void SaveData() -// { -// Settings.LastSaved = DateTime.Now; -// ApplicationData.Current.RoamingSettings.Values["Settings"] = Settings.Serialize(); -// ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts); -// } -//} + private readonly ILogger _logger; + + public JSON.Settings Settings { get; private set; } + public Account[] Accounts { get; set; } + + public event EventHandler? APINoMatch; + + public SettingsSystem(ILogger logger) + { + _logger = logger; + } + + public T GetSerializedFromSettings(string key, T def) + { + try + { + string json = ApplicationData.Current.LocalSettings.Values[key] as string; + if (!string.IsNullOrWhiteSpace(json)) + { + return JsonSerializer.Deserialize(json); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error deserializing key '{Key}' from settings", key); + } + + // Save default value if deserialization fails or key is missing + try + { + string json = JsonSerializer.Serialize(def); + ApplicationData.Current.LocalSettings.Values[key] = json; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error serializing default value for key '{Key}'", key); + } + + return def; + } + + public void LoadData() + { + try + { + _logger.LogInformation("Loading settings and accounts."); + Settings = GetSerializedFromSettings("Settings", JSON.Settings.CreateNew()); + Accounts = GetSerializedFromSettings("Accounts", Array.Empty()); + + if (Settings.APIVersion != DirectResoucres.SettingsAPIVersion) + { + _logger.LogWarning("API version mismatch. Triggering APINoMatch event."); + APINoMatch?.Invoke(this, ApplicationData.Current.LocalSettings.Values["Settings"] as string); + ApplicationData.Current.LocalSettings.Values["Settings"] = JSON.Settings.CreateNew().Serialize(); + Settings = JsonSerializer.Deserialize(ApplicationData.Current.LocalSettings.Values["Settings"] as string); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error loading settings data."); + } + } + + public async Task CreateBackup(string system) + { + try + { + string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); + var backups = string.IsNullOrWhiteSpace(json) ? new Backups() : JsonSerializer.Deserialize(json); + + backups.AllBackups ??= Array.Empty(); + backups.AllBackups = backups.AllBackups.Append(new SettingsBackup { Time = DateTime.Now, Backup = system }).ToArray(); + + json = backups.Serialize(); + await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); + + _logger.LogInformation("Backup created successfully."); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error creating backup."); + } + } + + public async Task DeleteBackup(int index) + { + try + { + string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); + var backups = string.IsNullOrWhiteSpace(json) ? new Backups() : JsonSerializer.Deserialize(json); + + backups.AllBackups?.ToList().RemoveAt(index); + json = backups.Serialize(); + + await FileIO.WriteTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists), json); + + _logger.LogInformation("Backup at index {Index} deleted successfully.", index); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error deleting backup at index {Index}.", index); + } + } + + public async Task> GetBackups() + { + try + { + string json = await FileIO.ReadTextAsync(await ApplicationData.Current.LocalFolder.CreateFileAsync("backups.json", CreationCollisionOption.OpenIfExists)); + var backups = string.IsNullOrWhiteSpace(json) ? new Backups() : JsonSerializer.Deserialize(json); + + _logger.LogInformation("Backups retrieved successfully."); + return backups.AllBackups?.ToList() ?? []; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error retrieving backups."); + return []; + } + } + + public void SaveData() + { + try + { + Settings.LastSaved = DateTime.Now; + ApplicationData.Current.LocalSettings.Values["Settings"] = JsonSerializer.Serialize(Settings); + ApplicationData.Current.LocalSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts); + + _logger.LogInformation("Settings and accounts saved successfully."); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error saving data."); + } + } } diff --git a/Emerald/MainPage.xaml b/Emerald/MainPage.xaml index c8a3de96..ad12e865 100644 --- a/Emerald/MainPage.xaml +++ b/Emerald/MainPage.xaml @@ -5,7 +5,6 @@ xmlns:utu="using:Uno.Toolkit.UI" xmlns:models="using:Emerald.Models" xmlns:uc="using:Emerald.UserControls" - xmlns:SS="using:Emerald.Helpers.Settings" xmlns:conv="using:Emerald.Helpers.Converters" xmlns:helpers="using:Emerald.Helpers"> @@ -90,20 +89,20 @@ VerticalAlignment="Top" Content="{x:Bind InfoBadge}" /> (); this.InitializeComponent(); this.Loaded += MainPage_Loaded; NavView.ItemInvoked += MainNavigationView_ItemInvoked; diff --git a/Emerald/Models/SquareNavigationViewItem.cs b/Emerald/Models/SquareNavigationViewItem.cs index 3f9f2ac5..bc2ea821 100644 --- a/Emerald/Models/SquareNavigationViewItem.cs +++ b/Emerald/Models/SquareNavigationViewItem.cs @@ -1,12 +1,15 @@ using System.ComponentModel; +using CommonServiceLocator; using CommunityToolkit.Mvvm.ComponentModel; namespace Emerald.Models; public partial class SquareNavigationViewItem : Model { + public Helpers.Settings.SettingsSystem SS { get; private set; } public SquareNavigationViewItem() { + SS = ServiceLocator.Current.GetInstance(); PropertyChanged += (_, e) => { if (e.PropertyName == nameof(IsSelected) || e.PropertyName == nameof(ShowFontIcons)) diff --git a/Emerald/Package.appxmanifest b/Emerald/Package.appxmanifest index 29558f1d..e9c7863f 100644 --- a/Emerald/Package.appxmanifest +++ b/Emerald/Package.appxmanifest @@ -7,13 +7,13 @@ IgnorableNamespaces="uap rescap"> Emerald - Emerald + Riverside Valley @@ -33,6 +33,7 @@ DisplayName="Emerald" Description="Emerald"> + diff --git a/Emerald/Helpers/Services/ServiceProviderLocator.cs b/Emerald/Services/ServiceProviderLocator.cs similarity index 97% rename from Emerald/Helpers/Services/ServiceProviderLocator.cs rename to Emerald/Services/ServiceProviderLocator.cs index c0207531..d722e87c 100644 --- a/Emerald/Helpers/Services/ServiceProviderLocator.cs +++ b/Emerald/Services/ServiceProviderLocator.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using CommonServiceLocator; -namespace Emerald.Helpers.Services; +namespace Emerald.Services; //Bruh I can't get uno services to work. help me. diff --git a/Emerald/Strings/en/Resources.resw b/Emerald/Strings/en/Resources.resw index abd56c45..c3f94326 100644 --- a/Emerald/Strings/en/Resources.resw +++ b/Emerald/Strings/en/Resources.resw @@ -760,4 +760,7 @@ Tint opacity + + Emerald Got Crashed! + \ No newline at end of file diff --git a/Emerald/UserControls/ArgumentsListView.xaml.cs b/Emerald/UserControls/ArgumentsListView.xaml.cs index 6ac337f1..a0c6fb8c 100644 --- a/Emerald/UserControls/ArgumentsListView.xaml.cs +++ b/Emerald/UserControls/ArgumentsListView.xaml.cs @@ -13,8 +13,8 @@ using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; using System.Collections.ObjectModel; -using SS = Emerald.Helpers.Settings.SettingsSystem; using Emerald.Models; +using CommonServiceLocator; namespace Emerald.UserControls; @@ -23,8 +23,10 @@ namespace Emerald.UserControls; public sealed partial class ArgumentsListView : UserControl { private int count = 0; + private readonly Helpers.Settings.SettingsSystem SS; public ArgumentsListView() { + SS = ServiceLocator.Current.GetInstance(); InitializeComponent(); view.ItemsSource = Source; UpdateSource(); diff --git a/Emerald/Views/Settings/AppearancePage.xaml b/Emerald/Views/Settings/AppearancePage.xaml index e366b7d7..ccb5f53b 100644 --- a/Emerald/Views/Settings/AppearancePage.xaml +++ b/Emerald/Views/Settings/AppearancePage.xaml @@ -1,7 +1,7 @@  + mc:Ignorable="d" xmlns:Main="using:Emerald" xmlns:helpers="using:Emerald.Helpers" + DataContext="{x:Bind SS.Settings}" xmlns:cn="using:CommunityToolkit.WinUI.Controls" xmlns:uc="using:Emerald.UserControls" xmlns:windowsui="using:Windows.UI"> (); if (SS.Settings.App.Appearance.MicaTintColor == (int)Helpers.Settings.Enums.MicaTintColor.CustomColor) { diff --git a/Emerald/Views/Settings/GeneralPage.xaml b/Emerald/Views/Settings/GeneralPage.xaml index c998ca54..a5ea6f81 100644 --- a/Emerald/Views/Settings/GeneralPage.xaml +++ b/Emerald/Views/Settings/GeneralPage.xaml @@ -1,7 +1,7 @@  + mc:Ignorable="d" xmlns:Main="using:Emerald" xmlns:helpers="using:Emerald.Helpers" + DataContext="{x:Bind SS.Settings}" xmlns:cn="using:CommunityToolkit.WinUI.Controls" xmlns:uc="using:Emerald.UserControls"> diff --git a/Emerald/Views/Settings/GeneralPage.xaml.cs b/Emerald/Views/Settings/GeneralPage.xaml.cs index a8569390..985067c6 100644 --- a/Emerald/Views/Settings/GeneralPage.xaml.cs +++ b/Emerald/Views/Settings/GeneralPage.xaml.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using CmlLib.Core; +using CommonServiceLocator; using CommunityToolkit.WinUI.Controls; using Emerald.Helpers; using Microsoft.UI.Xaml; @@ -26,8 +27,10 @@ namespace Emerald.Views.Settings; /// public sealed partial class GeneralPage : Page { + private readonly Helpers.Settings.SettingsSystem SS; public GeneralPage() { + SS = ServiceLocator.Current.GetInstance(); this.InitializeComponent(); } private async void btnChangeMPath_Click(object sender, RoutedEventArgs e) diff --git a/Emerald/Views/Settings/SettingsPage.xaml.cs b/Emerald/Views/Settings/SettingsPage.xaml.cs index 30caab1a..a430d373 100644 --- a/Emerald/Views/Settings/SettingsPage.xaml.cs +++ b/Emerald/Views/Settings/SettingsPage.xaml.cs @@ -26,7 +26,7 @@ private void Navigate(NavigationViewItem itm) NavigateOnce(typeof(AppearancePage)); break; case "About": - NavigateOnce(typeof(SettingsPage)); + throw new NotImplementedException(); break; default: NavigateOnce(typeof(GeneralPage)); diff --git a/Emerald/Views/Store/ModrinthStorePage.xaml b/Emerald/Views/Store/ModrinthStorePage.xaml new file mode 100644 index 00000000..84654cc6 --- /dev/null +++ b/Emerald/Views/Store/ModrinthStorePage.xaml @@ -0,0 +1,130 @@ + + + + diff --git a/Emerald/Views/Store/ModrinthStorePage.xaml.cs b/Emerald/Views/Store/ModrinthStorePage.xaml.cs new file mode 100644 index 00000000..64aa4367 --- /dev/null +++ b/Emerald/Views/Store/ModrinthStorePage.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 + +namespace Emerald.Views.Store; +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class ModrinthStorePage : Page +{ + public ModrinthStorePage() + { + this.InitializeComponent(); + } +}