diff --git a/SharepointToolbox/App.xaml b/SharepointToolbox/App.xaml index 1cfc57a..b937526 100644 --- a/SharepointToolbox/App.xaml +++ b/SharepointToolbox/App.xaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SharepointToolbox"> - + diff --git a/SharepointToolbox/App.xaml.cs b/SharepointToolbox/App.xaml.cs index 3bfe562..0746383 100644 --- a/SharepointToolbox/App.xaml.cs +++ b/SharepointToolbox/App.xaml.cs @@ -1,13 +1,17 @@ +using System.IO; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Serilog; +using SharepointToolbox.Infrastructure.Auth; +using SharepointToolbox.Infrastructure.Logging; +using SharepointToolbox.Services; +using SharepointToolbox.ViewModels; +using SharepointToolbox.ViewModels.Tabs; using System.Windows; namespace SharepointToolbox; -/// -/// Interaction logic for App.xaml -/// public partial class App : Application { [STAThread] @@ -16,7 +20,7 @@ public partial class App : Application using IHost host = Host.CreateDefaultBuilder(args) .UseSerilog((ctx, cfg) => cfg .WriteTo.File( - System.IO.Path.Combine( + Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SharepointToolbox", "logs", "app-.log"), rollingInterval: RollingInterval.Day, @@ -27,16 +31,49 @@ public partial class App : Application host.Start(); App app = new(); app.InitializeComponent(); - app.MainWindow = host.Services.GetRequiredService(); + + var mainWindow = host.Services.GetRequiredService(); + + // Wire LogPanelSink now that we have the RichTextBox + Log.Logger = new LoggerConfiguration() + .WriteTo.File( + Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "SharepointToolbox", "logs", "app-.log"), + rollingInterval: RollingInterval.Day, + retainedFileCountLimit: 30) + .WriteTo.Sink(new LogPanelSink(mainWindow.GetLogPanel())) + .CreateLogger(); + + // Global exception handlers + app.DispatcherUnhandledException += (s, e) => + { + Log.Fatal(e.Exception, "Unhandled UI exception"); + MessageBox.Show( + $"A fatal error occurred:\n{e.Exception.Message}\n\nCheck log for details.", + "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Error); + e.Handled = true; + }; + TaskScheduler.UnobservedTaskException += (s, e) => + { + Log.Fatal(e.Exception, "Unobserved task exception"); + e.SetObserved(); + }; + + app.MainWindow = mainWindow; app.MainWindow.Visibility = Visibility.Visible; app.Run(); } private static void RegisterServices(HostBuilderContext ctx, IServiceCollection services) { - // Placeholder — services registered in subsequent plans + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); services.AddSingleton(); - // LogPanelSink registered in plan 01-06 after MainWindow is created - // (requires RichTextBox reference from MainWindow) } } diff --git a/SharepointToolbox/MainWindow.xaml b/SharepointToolbox/MainWindow.xaml index 2dbf194..934d97c 100644 --- a/SharepointToolbox/MainWindow.xaml +++ b/SharepointToolbox/MainWindow.xaml @@ -1,12 +1,71 @@ - - + Title="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[app.title]}" + MinWidth="900" MinHeight="600" Height="700" Width="1100"> + + + + +