-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathApp.axaml.cs
More file actions
234 lines (181 loc) · 10.6 KB
/
App.axaml.cs
File metadata and controls
234 lines (181 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace CRT
{
// ###########################################################################################
// Central configuration — all tunable application values are defined here.
// Referenced by: OnlineServices, DataManager, UpdateService, Main, Logger.
// ###########################################################################################
public static class AppConfig
{
// ===== Debug ===============================================================================
// Only referenced inside #if DEBUG blocks — ignored entirely in Release builds.
// Enables online sync in DEBUG builds (normally skipped for faster development iteration).
// Used by: DataManager.InitializeAsync
public static readonly bool DebugSimulateSync = true;
// Simulates an available app update in DEBUG builds for UI testing.
// Used by: UpdateService.CheckForUpdateAsync, UpdateService.PendingVersion
public static readonly bool DebugSimulateUpdate = true;
// Fake version string shown in the update banner during debug update simulations.
// Used by: UpdateService.PendingVersion
public const string DebugSimulatedVersion = "99.0.0";
// ===== App Identity ========================================================================
// Short application code name used for User-Agent headers and API control payloads.
// Used by: OnlineServices
public const string AppShortName = "CRT";
// Name of the local AppData subfolder used for data and log storage.
// Used by: DataManager.ResolveDataRoot, Logger.Initialize
public const string AppFolderName = "Classic-Repair-Toolbox";
// Name of the log file written inside the AppFolderName directory.
// Used by: Logger.Initialize
public const string LogFileName = "Classic-Repair-Toolbox.log";
// Name of the JSON file storing user preferences. Stored alongside the log file.
// Used by: UserSettings.Load
public const string SettingsFileName = "Classic-Repair-Toolbox.settings.json";
// Name of the JSON file storing custom drawn polyline traces. Stored alongside the log file.
// Used by: TraceStorage.LoadFromFile
public const string TracesFileName = "Classic-Repair-Toolbox.traces.json";
// Prefix and suffix for the versioned main Excel file containing hardware definitions.
// Used by: DataManager.InitializeAsync, DataManager.LoadMainExcel
public const string MainExcelFileNamePrefix = "Classic-Repair-Toolbox.v";
public const string MainExcelFileSuffix = ".xlsx";
// Name of the main Excel file containing all hardware and board definitions.
// Used by: DataManager.InitializeAsync, DataManager.LoadMainExcel
public const string MainExcelFileName = "Classic-Repair-Toolbox.xlsx";
// ===== Online Services =====================================================================
// URL to the JSON manifest listing all data files and their SHA-256 checksums.
// Used by: OnlineServices.FetchManifestAsync
public const string ChecksumsUrl = "https://classic-repair-toolbox.dk/app-data/dataChecksums.json";
// URL for the phone-home version check endpoint.
// Used by: OnlineServices.CheckInVersionAsync
public const string CheckVersionUrl = "https://classic-repair-toolbox.dk/app-checkin/";
// Timeout for lightweight API calls (manifest fetch, version check).
// Used by: OnlineServices.FetchManifestAsync, OnlineServices.CheckInVersionAsync
public static readonly TimeSpan ApiTimeout = TimeSpan.FromSeconds(5);
// Timeout per individual file download — files can be large on slow connections.
// Used by: OnlineServices.SyncFilesAsync
public static readonly TimeSpan DownloadTimeout = TimeSpan.FromSeconds(30);
// ===== GitHub Updates ======================================================================
// GitHub repository owner used to check for application updates via Velopack.
// Used by: UpdateService.CheckForUpdateAsync
public const string GitHubOwner = "HovKlan-DH";
// GitHub repository name used to check for application updates via Velopack.
// Used by: UpdateService.CheckForUpdateAsync
public const string GitHubRepo = "Classic-Repair-Toolbox";
// ===== Schematics Viewer ==================================================================
// Zoom multiplier applied per mouse wheel step.
// Used by: Main.OnSchematicsZoom
public const double SchematicsZoomFactor = 1.5;
// Minimum allowed zoom level (1.0 = 100%).
// Used by: Main.OnSchematicsZoom
public const double SchematicsMinZoom = 0.9;
// Maximum allowed zoom level.
// Used by: Main.OnSchematicsZoom
public const double SchematicsMaxZoom = 20.0;
// Maximum pixel width used when pre-scaling schematic thumbnail images.
// Used by: Main.OnBoardSelectionChanged, Main.CreateScaledThumbnail, Main.CreateScaledThumbnailWithHighlights
public const int ThumbnailMaxWidth = 800;
// Logical pixel size of the splash screen window, matching Splash.axaml Width/Height.
// Used by: App.OnFrameworkInitializationCompleted to center the splash on the saved screen.
public const int SplashWidth = 600;
public const int SplashHeight = 350;
// ###########################################################################################
// Builds a display-safe semantic version string.
// ###########################################################################################
public static readonly string AppVersionString = GetAppVersion();
private static string GetAppVersion()
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
if (version == null)
{
return "0.0.0";
}
// Include the 4th digit (Revision) if it's explicitly set above 0,
// otherwise falling back to standard Major.Minor.Build format.
return version.Revision > 0
? $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"
: $"{version.Major}.{version.Minor}.{version.Build}";
}
}
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
// ###########################################################################################
// Registers global exception handlers to capture unexpected crashes into the log.
// ###########################################################################################
private void SetupGlobalExceptionLogging()
{
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
if (e.ExceptionObject is Exception ex)
{
Logger.Critical($"Unhandled AppDomain Exception: {ex}");
}
};
TaskScheduler.UnobservedTaskException += (s, e) =>
{
Logger.Critical($"Unobserved Task Exception: {e.Exception}");
e.SetObserved(); // Prevents the application from crashing
};
}
// ###########################################################################################
// Shows the splash screen, initializes data (syncing with online source), then opens the main window.
// ###########################################################################################
public override async void OnFrameworkInitializationCompleted()
{
Logger.Initialize();
this.SetupGlobalExceptionLogging();
Logger.Info($"Classic Repair Toolbox version [{AppConfig.AppVersionString}] launched");
UserSettings.Load();
// Apply selected theme early
if (UserSettings.ThemeVariant == "Dark")
this.RequestedThemeVariant = Avalonia.Styling.ThemeVariant.Dark;
else if (UserSettings.ThemeVariant == "Light")
this.RequestedThemeVariant = Avalonia.Styling.ThemeVariant.Light;
else
this.RequestedThemeVariant = Avalonia.Styling.ThemeVariant.Default;
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var splash = new Splash();
// Place the splash on the same screen the main window will open on
if (UserSettings.HasWindowPlacement)
{
double scaling = UserSettings.WindowScreenScaling;
var centerX = UserSettings.WindowScreenX + UserSettings.WindowScreenWidth / 2;
var centerY = UserSettings.WindowScreenY + UserSettings.WindowScreenHeight / 2;
splash.WindowStartupLocation = WindowStartupLocation.Manual;
splash.Position = new PixelPoint(
centerX - (int)(AppConfig.SplashWidth * scaling / 2),
centerY - (int)(AppConfig.SplashHeight * scaling / 2));
}
desktop.MainWindow = splash;
// Create a TaskCompletionSource to bridge the event into an awaitable task
var splashOpened = new TaskCompletionSource();
splash.Opened += (s, e) => splashOpened.TrySetResult();
splash.Show();
// Wait until Avalonia explicitly fires the "opened" event, guaranteeing the UI is visibly drawn
await splashOpened.Task;
// Either use local data or sync it from online source
// await DataManager.InitializeAsync(desktop.Args ?? []);
await DataManager.InitializeAsync(desktop.Args ?? Array.Empty<string>()); // supporting .NET6
var main = new Main();
desktop.MainWindow = main;
main.Show();
splash.Close();
Logger.Info("Application UI opened");
// UI has finished loading, so we can do a check-in
_ = OnlineServices.CheckInVersionAsync();
}
base.OnFrameworkInitializationCompleted();
}
}
}