v0.5: settings dialog, persistent Serilog logs, Windows toasts
Settings (⚙ button in top bar)
------------------------------
SettingsDialog opens via OpenSettings command in MainViewModel. Sections:
- Serveur: server URL field + "Tester" button that GETs /api/health
and reports status inline.
- Installation: installRoot path with Browse button
(Microsoft.Win32.OpenFolderDialog, .NET 8 native).
- License: shows status / owner / exp / machine ID (read-only,
copy-to-clipboard button), "Désactiver" wipes the cached license.
- Cache: shows download cache path + current size, opens or empties it.
- Logs & Application: launcher version + logs path with "Open" button.
Apply persists to LocalConfig via IConfigStore. After save, MainViewModel
RebuildList()s so changes to ServerBaseUrl or InstallRoot take effect
without restart.
Persistent Serilog logs
-----------------------
Logs now live in %LocalAppData%/PSLauncher/logs/app-YYYYMMDD.log, rolling
daily, 10 days kept. Output template includes source context and stack
traces. Generic Host wired up via .UseSerilog() so all
ILogger<T>-injected types share the sink. Unhandled AppDomain and
Dispatcher exceptions are routed to Serilog before propagation.
Windows toasts
--------------
IToastService + ToastService backed by Microsoft.Toolkit.Uwp.Notifications
(ToastContentBuilder.Show()). Required bumping the App TFM from
net8.0-windows to net8.0-windows10.0.17763.0 (with explicit
WindowsSdkPackageVersion=10.0.17763.41 to satisfy CommunityToolkit.Mvvm
8.3.2's MVVMTKCFG0003 check). Triggered on:
- successful install completion: "Proserve v{X} est prête à être lancée"
- install/download error: short error excerpt
Misc
----
- InverseBoolConverter for "disable button while busy" patterns.
- Added Markdig.Wpf import to ReleaseNotesViewerDialog (was implicit
before, now required explicitly).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PSLauncher.App.Services;
|
||||
using PSLauncher.App.ViewModels;
|
||||
using PSLauncher.App.Views;
|
||||
using PSLauncher.Core.Configuration;
|
||||
@@ -15,6 +17,7 @@ using PSLauncher.Core.Manifests;
|
||||
using PSLauncher.Core.Process;
|
||||
using PSLauncher.Core.Updates;
|
||||
using PSLauncher.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace PSLauncher.App;
|
||||
|
||||
@@ -22,18 +25,46 @@ public partial class App : Application
|
||||
{
|
||||
private IHost? _host;
|
||||
|
||||
public static string LogsDirectory { get; private set; } = string.Empty;
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
// Logs avant tout — pour ne rien perdre des erreurs au boot
|
||||
LogsDirectory = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"PSLauncher", "logs");
|
||||
Directory.CreateDirectory(LogsDirectory);
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Debug()
|
||||
.WriteTo.File(
|
||||
path: Path.Combine(LogsDirectory, "app-.log"),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
retainedFileCountLimit: 10,
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger();
|
||||
|
||||
Log.Information("PSLauncher starting (logs in {Path})", LogsDirectory);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
|
||||
Log.Fatal((Exception)args.ExceptionObject, "Unhandled exception (AppDomain)");
|
||||
DispatcherUnhandledException += (_, args) =>
|
||||
{
|
||||
Log.Error(args.Exception, "Unhandled UI exception");
|
||||
args.Handled = false;
|
||||
};
|
||||
|
||||
_host = Host.CreateDefaultBuilder()
|
||||
.ConfigureLogging(b => { b.ClearProviders(); b.AddDebug(); })
|
||||
.UseSerilog()
|
||||
.ConfigureServices((_, services) =>
|
||||
{
|
||||
services.AddSingleton<IConfigStore, ConfigStore>();
|
||||
services.AddSingleton<LocalConfig>(sp => sp.GetRequiredService<IConfigStore>().Load());
|
||||
|
||||
// HttpClient partagé (timeout long pour les gros téléchargements)
|
||||
services.AddSingleton(sp =>
|
||||
{
|
||||
var http = new HttpClient(new SocketsHttpHandler
|
||||
@@ -44,7 +75,7 @@ public partial class App : Application
|
||||
{
|
||||
Timeout = Timeout.InfiniteTimeSpan
|
||||
};
|
||||
http.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("PSLauncher", "0.2"));
|
||||
http.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("PSLauncher", "0.5"));
|
||||
return http;
|
||||
});
|
||||
|
||||
@@ -75,6 +106,9 @@ public partial class App : Application
|
||||
sp.GetRequiredService<LocalConfig>(),
|
||||
sp.GetRequiredService<ILogger<LicenseService>>()));
|
||||
|
||||
services.AddSingleton<IToastService, ToastService>();
|
||||
|
||||
services.AddTransient<SettingsViewModel>();
|
||||
services.AddSingleton<MainViewModel>();
|
||||
services.AddSingleton<MainWindow>();
|
||||
})
|
||||
@@ -88,6 +122,8 @@ public partial class App : Application
|
||||
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
Log.Information("PSLauncher shutting down");
|
||||
Log.CloseAndFlush();
|
||||
_host?.Dispose();
|
||||
base.OnExit(e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user