Browse Source

Send resume session after a crash instead of reopening a session

CPKreuz 11 months ago
parent
commit
b1e738ac5a

+ 1 - 0
src/PixiEditor/Models/AnalyticsAPI/AnalyticEventTypes.cs

@@ -12,6 +12,7 @@ public class AnalyticEventTypes
     public static string GeneralCommand { get; } = GetEventType("GeneralCommand");
     public static string GeneralCommand { get; } = GetEventType("GeneralCommand");
     public static string SwitchTool { get; } = GetEventType("SwitchTool");
     public static string SwitchTool { get; } = GetEventType("SwitchTool");
     public static string UseTool { get; } = GetEventType("UseTool");
     public static string UseTool { get; } = GetEventType("UseTool");
+    public static string ResumeSession { get; } = GetEventType("ResumeSession");
 
 
     private static string GetEventType(string value) => $"PixiEditor.{value}";
     private static string GetEventType(string value) => $"PixiEditor.{value}";
 }
 }

+ 25 - 7
src/PixiEditor/Models/AnalyticsAPI/AnalyticsPeriodicReporter.cs

@@ -5,6 +5,7 @@ namespace PixiEditor.Models.AnalyticsAPI;
 public class AnalyticsPeriodicReporter
 public class AnalyticsPeriodicReporter
 {
 {
     private int _sendExceptions = 0;
     private int _sendExceptions = 0;
+    private bool _resumeSession;
     
     
     private readonly SemaphoreSlim _semaphore = new(1, 1);
     private readonly SemaphoreSlim _semaphore = new(1, 1);
     private readonly AnalyticsClient _client;
     private readonly AnalyticsClient _client;
@@ -28,8 +29,16 @@ public class AnalyticsPeriodicReporter
         _client = client;
         _client = client;
     }
     }
 
 
-    public void Start()
+    public void Start(Guid? sessionId)
     {
     {
+        if (sessionId != null)
+        {
+            SessionId = sessionId.Value;
+            _resumeSession = true;
+            
+            _backlog.Add(new AnalyticEvent { Time = DateTime.UtcNow, EventType = AnalyticEventTypes.ResumeSession });
+        }
+
         Task.Run(RunAsync);
         Task.Run(RunAsync);
     }
     }
 
 
@@ -42,6 +51,12 @@ public class AnalyticsPeriodicReporter
 
 
     public void AddEvent(AnalyticEvent value)
     public void AddEvent(AnalyticEvent value)
     {
     {
+        // Don't send startup as it gives invalid results for crash resumed sessions
+        if (value.EventType == AnalyticEventTypes.Startup && _resumeSession)
+        {
+            return;
+        }
+        
         Task.Run(() =>
         Task.Run(() =>
         {
         {
             _semaphore.Wait();
             _semaphore.Wait();
@@ -59,14 +74,17 @@ public class AnalyticsPeriodicReporter
 
 
     private async Task RunAsync()
     private async Task RunAsync()
     {
     {
-        var createSession = await _client.CreateSessionAsync(_cancellationToken.Token);
-
-        if (!createSession.HasValue)
+        if (!_resumeSession)
         {
         {
-            return;
-        }
+            var createSession = await _client.CreateSessionAsync(_cancellationToken.Token);
+
+            if (!createSession.HasValue)
+            {
+                return;
+            }
 
 
-        SessionId = createSession.Value;
+            SessionId = createSession.Value;
+        }
 
 
         Task.Run(RunHeartbeatAsync);
         Task.Run(RunHeartbeatAsync);
 
 

+ 15 - 11
src/PixiEditor/Models/ExceptionHandling/CrashReport.cs

@@ -12,6 +12,7 @@ using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
+using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands;
 using PixiEditor.Parser;
 using PixiEditor.Parser;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
@@ -115,6 +116,8 @@ internal class CrashReport : IDisposable
         builder
         builder
             .AppendLine("Environment:")
             .AppendLine("Environment:")
             .AppendLine($"  Thread Count: {GetFormatted(() => Process.GetCurrentProcess().Threads.Count)}")
             .AppendLine($"  Thread Count: {GetFormatted(() => Process.GetCurrentProcess().Threads.Count)}")
+            .AppendLine("Analytics:")
+            .AppendLine($"  Analytics Id: {GetFormatted(() => AnalyticsPeriodicReporter.Instance?.SessionId)}")
             .AppendLine("\nCulture:")
             .AppendLine("\nCulture:")
             .AppendLine($"  Selected language: {GetPreferenceFormatted("LanguageCode", true, "system")}")
             .AppendLine($"  Selected language: {GetPreferenceFormatted("LanguageCode", true, "system")}")
             .AppendLine($"  Current Culture: {GetFormatted(() => CultureInfo.CurrentCulture)}")
             .AppendLine($"  Current Culture: {GetFormatted(() => CultureInfo.CurrentCulture)}")
@@ -267,15 +270,16 @@ internal class CrashReport : IDisposable
 
 
     public int GetDocumentCount() => ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")).Count();
     public int GetDocumentCount() => ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")).Count();
 
 
-    public bool TryRecoverDocuments(out List<RecoveredPixi> list)
+    public bool TryRecoverDocuments(out List<RecoveredPixi> list, out CrashedSessionInfo? sessionInfo)
     {
     {
         try
         try
         {
         {
-            list = RecoverDocuments();
+            list = RecoverDocuments(out sessionInfo);
         }
         }
         catch (Exception e)
         catch (Exception e)
         {
         {
             list = null;
             list = null;
+            sessionInfo = null;
             CrashHelper.SendExceptionInfoToWebhook(e);
             CrashHelper.SendExceptionInfoToWebhook(e);
             return false;
             return false;
         }
         }
@@ -283,12 +287,12 @@ internal class CrashReport : IDisposable
         return true;
         return true;
     }
     }
 
 
-    public List<RecoveredPixi> RecoverDocuments()
+    public List<RecoveredPixi> RecoverDocuments(out CrashedSessionInfo? sessionInfo)
     {
     {
         List<RecoveredPixi> recoveredDocuments = new();
         List<RecoveredPixi> recoveredDocuments = new();
 
 
-        var paths = TryGetOriginalPaths();
-        if (paths == null)
+        sessionInfo = TryGetSessionInfo();
+        if (sessionInfo?.OpenedDocuments == null)
         {
         {
             recoveredDocuments.AddRange(
             recoveredDocuments.AddRange(
                 ZipFile.Entries
                 ZipFile.Entries
@@ -300,11 +304,11 @@ internal class CrashReport : IDisposable
             return recoveredDocuments;
             return recoveredDocuments;
         }
         }
 
 
-        recoveredDocuments.AddRange(paths.Select(path => new RecoveredPixi(path.Value, ZipFile.GetEntry($"Documents/{path.Key}"))));
+        recoveredDocuments.AddRange(sessionInfo.OpenedDocuments.Select(path => new RecoveredPixi(path.OriginalPath, ZipFile.GetEntry($"Documents/{path.ZipName}"))));
 
 
         return recoveredDocuments;
         return recoveredDocuments;
 
 
-        Dictionary<string, string>? TryGetOriginalPaths()
+        CrashedSessionInfo? TryGetSessionInfo()
         {
         {
             var originalPathsEntry = ZipFile.Entries.FirstOrDefault(entry => entry.FullName == "DocumentInfo.json");
             var originalPathsEntry = ZipFile.Entries.FirstOrDefault(entry => entry.FullName == "DocumentInfo.json");
 
 
@@ -317,7 +321,7 @@ internal class CrashReport : IDisposable
                 using var reader = new StreamReader(stream);
                 using var reader = new StreamReader(stream);
                 string json = reader.ReadToEnd();
                 string json = reader.ReadToEnd();
 
 
-                return JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
+                return JsonConvert.DeserializeObject<CrashedSessionInfo>(json);
             }
             }
             catch
             catch
             {
             {
@@ -373,7 +377,7 @@ internal class CrashReport : IDisposable
 
 
         // Write the documents into zip
         // Write the documents into zip
         int counter = 0;
         int counter = 0;
-        var originalPaths = new Dictionary<string, string>();
+        var originalPaths = new List<CrashedFileInfo>();
         //TODO: Implement
         //TODO: Implement
         foreach (var document in documents)
         foreach (var document in documents)
         {
         {
@@ -389,7 +393,7 @@ internal class CrashReport : IDisposable
                 using Stream documentStream = archive.CreateEntry($"Documents/{nameInZip}").Open();
                 using Stream documentStream = archive.CreateEntry($"Documents/{nameInZip}").Open();
                 documentStream.Write(serialized);
                 documentStream.Write(serialized);
 
 
-                originalPaths.Add(nameInZip, document.FullFilePath);
+                originalPaths.Add(new CrashedFileInfo(nameInZip, document.FullFilePath));
             }
             }
             catch { }
             catch { }
             counter++;
             counter++;
@@ -400,7 +404,7 @@ internal class CrashReport : IDisposable
             using Stream jsonStream = archive.CreateEntry("DocumentInfo.json").Open();
             using Stream jsonStream = archive.CreateEntry("DocumentInfo.json").Open();
             using StreamWriter writer = new StreamWriter(jsonStream);
             using StreamWriter writer = new StreamWriter(jsonStream);
 
 
-            string originalPathsJson = JsonConvert.SerializeObject(originalPaths, Formatting.Indented);
+            string originalPathsJson = JsonConvert.SerializeObject(new CrashedSessionInfo(AnalyticsPeriodicReporter.Instance?.SessionId ?? Guid.Empty, originalPaths), Formatting.Indented);
             writer.Write(originalPathsJson);
             writer.Write(originalPathsJson);
         }
         }
     }
     }

+ 16 - 0
src/PixiEditor/Models/ExceptionHandling/CrashedFileInfo.cs

@@ -0,0 +1,16 @@
+namespace PixiEditor.Models.ExceptionHandling;
+
+public class CrashedFileInfo
+{
+    public string ZipName { get; set; }
+    
+    public string OriginalPath { get; set; }
+    
+    public CrashedFileInfo() { }
+
+    public CrashedFileInfo(string zipName, string originalPath)
+    {
+        ZipName = zipName;
+        OriginalPath = originalPath;
+    }
+}

+ 18 - 0
src/PixiEditor/Models/ExceptionHandling/CrashedSessionInfo.cs

@@ -0,0 +1,18 @@
+namespace PixiEditor.Models.ExceptionHandling;
+
+public class CrashedSessionInfo
+{
+    public Guid? AnalyticsSessionId { get; set; }
+    
+    public ICollection<CrashedFileInfo>? OpenedDocuments { get; set; }
+
+    public CrashedSessionInfo()
+    {
+    }
+
+    public CrashedSessionInfo(Guid? analyticsSessionId, ICollection<CrashedFileInfo> openedDocuments)
+    {
+        AnalyticsSessionId = analyticsSessionId;
+        OpenedDocuments = openedDocuments;
+    }
+}

+ 9 - 9
src/PixiEditor/Views/MainWindow.axaml.cs

@@ -55,7 +55,7 @@ internal partial class MainWindow : Window
         }
         }
     }
     }
 
 
-    public MainWindow(ExtensionLoader extensionLoader)
+    public MainWindow(ExtensionLoader extensionLoader, Guid? analyticsSessionId = null)
     {
     {
         StartupPerformance.ReportToMainWindow();
         StartupPerformance.ReportToMainWindow();
         
         
@@ -83,7 +83,7 @@ internal partial class MainWindow : Window
         StartupPerformance.ReportToMainViewModel();
         StartupPerformance.ReportToMainViewModel();
 
 
         var analytics = services.GetService<AnalyticsPeriodicReporter>();
         var analytics = services.GetService<AnalyticsPeriodicReporter>();
-        analytics?.Start();
+        analytics?.Start(analyticsSessionId);
         
         
         InitializeComponent();
         InitializeComponent();
     }
     }
@@ -115,15 +115,15 @@ internal partial class MainWindow : Window
 
 
     public static MainWindow CreateWithRecoveredDocuments(CrashReport report, out bool showMissingFilesDialog)
     public static MainWindow CreateWithRecoveredDocuments(CrashReport report, out bool showMissingFilesDialog)
     {
     {
-        var window = GetMainWindow();
-        var fileVM = window.services.GetRequiredService<FileViewModel>();
-
-        if (!report.TryRecoverDocuments(out var documents))
+        if (!report.TryRecoverDocuments(out var documents, out var sessionInfo))
         {
         {
             showMissingFilesDialog = true;
             showMissingFilesDialog = true;
-            return window;
+            return GetMainWindow(null);
         }
         }
 
 
+        var window = GetMainWindow(sessionInfo?.AnalyticsSessionId);
+        var fileVM = window.services.GetRequiredService<FileViewModel>();
+
         var i = 0;
         var i = 0;
 
 
         foreach (var document in documents)
         foreach (var document in documents)
@@ -143,13 +143,13 @@ internal partial class MainWindow : Window
 
 
         return window;
         return window;
 
 
-        MainWindow GetMainWindow()
+        MainWindow GetMainWindow(Guid? analyticsSession)
         {
         {
             try
             try
             {
             {
                 var app = (App)Application.Current;
                 var app = (App)Application.Current;
                 ClassicDesktopEntry entry = new(app.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime);
                 ClassicDesktopEntry entry = new(app.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime);
-                return new MainWindow(entry.InitApp());
+                return new MainWindow(entry.InitApp(), analyticsSession);
             }
             }
             catch (Exception e)
             catch (Exception e)
             {
             {