-
Notifications
You must be signed in to change notification settings - Fork 2
/
AppViewModel.cs
341 lines (263 loc) · 9.56 KB
/
AppViewModel.cs
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NLog;
using NLog.Config;
using NLog.Targets;
namespace Ray1Editor;
public class AppViewModel : BaseViewModel
{
#region Constructor
public AppViewModel()
{
Path_AppDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Ray1Editor");
Path_AppUserDataFile = Path.Combine(Path_AppDataDir, $"Settings.json");
Path_LogFile = Path.Combine(Path_AppDataDir, $"Log.txt");
Path_ArchivedLogFile = Path.Combine(Path_AppDataDir, $"Log_archived.txt");
Path_SerializerLogFile = Path.Combine(Path_AppDataDir, $"SerializerLog.txt");
LegacyPath_UpdaterFile = Path.Combine(Path_AppDataDir, $"Updater.exe");
CloseAppCommand = new RelayCommand(() => App.Current.Shutdown());
}
#endregion
#region Paths
public string Path_AppDataDir { get; }
public string Path_AppUserDataFile { get; }
public string Path_LogFile { get; }
public string Path_ArchivedLogFile { get; }
public string Path_SerializerLogFile { get; }
public string LegacyPath_UpdaterFile { get; } // Need to keep this for deleting the updater if updated from older version
#endregion
#region URLs
public string Url_Ray1EditorHome => "https://raym.app/ray1editor";
public string Url_Ray1EditorGitHub => "https://github.com/RayCarrot/Ray1Editor";
public string Url_Ray1Map => "https://raym.app/maps_r1";
#endregion
#region Logger
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
#endregion
#region Commands
public ICommand CloseAppCommand { get; }
#endregion
#region Public Properties
/// <summary>
/// The current app version
/// </summary>
public Version CurrentAppVersion => new Version(0, 2, 2, 0);
/// <summary>
/// Indicates if the current version is a BETA version
/// </summary>
public bool IsBeta => true;
/// <summary>
/// The loaded app user data
/// </summary>
public AppUserData UserData { get; set; }
/// <summary>
/// The currently app view
/// </summary>
public AppView CurrentAppView { get; protected set; }
/// <summary>
/// THe view model for the current app view
/// </summary>
public AppViewBaseViewModel CurrentAppViewViewModel { get; protected set; }
/// <summary>
/// The current application title
/// </summary>
public string Title { get; protected set; }
/// <summary>
/// Indicates if an update was performed prior to launching this instance
/// </summary>
public bool HasUpdated { get; protected set; }
#endregion
#region Public Methods
public void SetTitle(string state)
{
Title = "Ray1Editor";
if (IsBeta)
Title += $" (BETA)";
if (state != null)
Title += $" - {state}";
else
Title += $" {CurrentAppVersion}";
Logger.Trace("Title set to {0}", Title);
}
/// <summary>
/// Changes the current view to the specified one
/// </summary>
/// <param name="view">The view to change to</param>
/// <param name="vm">The view model for the new view</param>
public void ChangeView(AppView view, AppViewBaseViewModel vm)
{
Logger.Info("Changing view from {0} to {1}", CurrentAppView, view);
// Dispose the current view model
CurrentAppViewViewModel?.Dispose();
// Save the app user data
SaveAppUserData();
// Set the new view
CurrentAppViewViewModel = vm;
CurrentAppView = view;
// Initialize the view model
CurrentAppViewViewModel.Initialize();
}
public void Initialize(string[] args)
{
// Create the data directory
Directory.CreateDirectory(Path_AppDataDir);
InitializeLogging(args);
Logger.Info("Initializing application with app version {0}", CurrentAppVersion);
// Default the title
SetTitle(null);
// Load the app user data
LoadAppUserData();
if (UserData.App_Version < CurrentAppVersion)
PostUpdate(UserData.App_Version);
// Update the version
UserData.App_Version = CurrentAppVersion;
// Register encodings
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// Change the view to the load map view
ChangeView(AppView.LoadMap, new LoadMapViewModel());
Logger.Info("Initialized application");
}
public void InitializeLogging(string[] args)
{
// Create a new logging configuration
var logConfig = new LoggingConfiguration();
#if DEBUG
// On debug we default it to log trace
LogLevel logLevel = LogLevel.Trace;
#else
// If not on debug we default to log info
LogLevel logLevel = LogLevel.Info;
#endif
// Allow the log level to be specified from a launch argument
if (args.Contains("-loglevel"))
{
string argLogLevel = args[args.FindIndex(x => x == "-loglevel") + 1];
logLevel = LogLevel.FromString(argLogLevel);
}
const string logLayout = "${time:invariant=true}|${level:uppercase=true}|${logger}|${message}${onexception:${newline}${exception:format=tostring}}";
bool logToFile = !args.Contains("-nofilelog");
bool logToMemory = !args.Contains("-nomemlog");
bool logToViewer = args.Contains("-logviewer");
// Log to file
if (logToFile)
{
logConfig.AddRule(logLevel, LogLevel.Fatal, new FileTarget("file")
{
// Archive a maximum of 5 logs. This makes it easier going back to check errors which happened on older instances of the app.
ArchiveOldFileOnStartup = true,
ArchiveFileName = Path_ArchivedLogFile,
MaxArchiveFiles = 5,
ArchiveNumbering = ArchiveNumberingMode.Sequence,
// Keep the file open and disable concurrent writes to improve performance
KeepFileOpen = true,
ConcurrentWrites = false,
// Set the file path and layout
FileName = Path_LogFile,
Layout = logLayout,
});
}
if (logToMemory)
{
logConfig.AddRule(logLevel, LogLevel.Fatal, new MemoryTarget("memory")
{
Layout = logLayout,
});
}
// Log to log viewer
if (logToViewer)
{
// TODO: Add a log viewer
//LogViewerViewModel = new LogViewerViewModel();
//// Always log from trace to fatal to include all logs
//logConfig.AddRuleForAllLevels(new MethodCallTarget("logviewer", async (logEvent, _) =>
//{
// // Await to avoid blocking
// await App.Current.Dispatcher.InvokeAsync(() =>
// {
// var log = new LogItemViewModel(logEvent.Level, logEvent.Exception, logEvent.TimeStamp, logEvent.LoggerName, logEvent.FormattedMessage);
// log.IsVisible = log.LogLevel >= LogViewerViewModel.ShowLogLevel;
// LogViewerViewModel.LogItems.Add(log);
// });
//}));
}
// Apply config
LogManager.Configuration = logConfig;
}
public void PostUpdate(Version prevVersion)
{
HasUpdated = true;
}
public void Unload()
{
Logger.Info("Unloading the application");
// Dispose the current view model
CurrentAppViewViewModel?.Dispose();
// Save the app user data
SaveAppUserData();
Logger.Info("Unloaded the application");
// Shut down the logger
LogManager.Shutdown();
}
public void LoadAppUserData()
{
Logger.Info("Loading app user data from {0}", Path_AppUserDataFile);
if (File.Exists(Path_AppUserDataFile))
{
try
{
var json = File.ReadAllText(Path_AppUserDataFile);
UserData = JsonConvert.DeserializeObject<AppUserData>(json, new StringEnumConverter());
if (UserData == null)
throw new Exception($"User data was empty");
UserData.Verify();
}
catch (Exception ex)
{
Logger.Error(ex, "Error loading app user data");
MessageBox.Show($"An error occurred when loading the app user data. Error message: {ex.Message}");
ResetAppUserData();
}
}
else
{
Logger.Info("No app user data found");
ResetAppUserData();
}
}
public void ResetAppUserData()
{
UserData = new AppUserData();
Logger.Info("Reset the app user data");
}
public void SaveAppUserData()
{
Logger.Trace("Saving the app user data");
// Serialize to JSON and save to file
try
{
var json = JsonConvert.SerializeObject(UserData, Formatting.Indented, new StringEnumConverter());
File.WriteAllText(Path_AppUserDataFile, json);
}
catch (Exception ex)
{
Logger.Fatal(ex, "Error saving the app user data");
throw;
}
Logger.Info("Saved the app user data");
}
#endregion
#region Data Types
public enum AppView
{
None,
Editor,
LoadMap
}
#endregion
}