Browse Source

'Fixed crash report and integrated temp autosave files'

Equbuxu 1 year ago
parent
commit
de4b598ad9

+ 38 - 20
src/PixiEditor/App.xaml.cs

@@ -6,6 +6,7 @@ using System.Windows.Interop;
 using System.Windows.Media;
 using System.Windows.Threading;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Helpers;
 using PixiEditor.Models.AppExtensions;
 using PixiEditor.Helpers.UI;
 using PixiEditor.Models.Controllers;
@@ -42,34 +43,37 @@ internal partial class App : Application
 
         if (ParseArgument("--crash (\"?)([A-z0-9:\\/\\ -_.]+)\\1", arguments, out Group[] groups))
         {
-            CrashReport report = CrashReport.Parse(groups[2].Value);
-            MainWindow = new CrashReportDialog(report);
-            MainWindow.Show();
+            try
+            {
+                CrashReport report = CrashReport.Parse(groups[2].Value);
+                MainWindow = new CrashReportDialog(report);
+                MainWindow.Show();
+            }
+            catch (Exception exception)
+            {
+                try
+                {
+                    CrashHelper.SendExceptionInfoToWebhook(exception);
+                }
+                finally
+                {
+                    MessageBox.Show("Fatal error", $"Fatal error while trying to open crash report in App.OnStartup()\n{exception}");
+                }
+            }
+
             return;
         }
 
-        #if !STEAM
+#if !STEAM
         if (!HandleNewInstance())
         {
             return;
         }
-        #endif
-
-        LoadingWindow.ShowInNewThread();
-
-        AddNativeAssets();
-
-        InitPlatform();
-
-        ExtensionLoader extensionLoader = new ExtensionLoader();
-        extensionLoader.LoadExtensions();
-
+#endif
+        
+        var extensionLoader = InitApp();
+        
         MainWindow = new MainWindow(extensionLoader);
-        MainWindow.ContentRendered += (_, _) =>
-        {
-            LoadingWindow.Instance.SafeClose();
-            MainWindow.Activate();
-        };
         MainWindow.Show();
     }
 
@@ -80,6 +84,20 @@ internal partial class App : Application
         platform.PerformHandshake();
     }
 
+    public ExtensionLoader InitApp()
+    {
+        LoadingWindow.ShowInNewThread();
+
+        AddNativeAssets();
+
+        InitPlatform();
+
+        ExtensionLoader extensionLoader = new ExtensionLoader();
+        extensionLoader.LoadExtensions();
+        
+        return extensionLoader;
+    }
+
     private IPlatform GetActivePlatform()
     {
 #if STEAM

+ 4 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -589,5 +589,8 @@
   "COPY_COLOR": "Copy color",
 
   "FAILED_DOWNLOADING_TITLE": "Downloading update failed",
-  "FAILED_DOWNLOADING": "Failed downloading the update, you might not have enough space on the disk"
+  "FAILED_DOWNLOADING": "Failed downloading the update, you might not have enough space on the disk",
+  
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Could not recover all",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Could not fully recover all files.\nIf you send the crash report to the developers\nthey might be able to help you."
 }

+ 14 - 0
src/PixiEditor/Models/DataHolders/CrashFilePathInfo.cs

@@ -0,0 +1,14 @@
+namespace PixiEditor.Models.DataHolders;
+
+public class CrashFilePathInfo
+{
+    public string? OriginalPath { get; set; }
+    
+    public string? AutosavePath { get; set; }
+    
+    public CrashFilePathInfo(string originalPath, string autosavePath)
+    {
+        OriginalPath = originalPath;
+        AutosavePath = autosavePath;
+    }
+}

+ 21 - 16
src/PixiEditor/Models/DataHolders/CrashReport.cs

@@ -1,4 +1,5 @@
 using System.Diagnostics;
+using System.Globalization;
 using System.IO;
 using System.IO.Compression;
 using System.Reflection;
@@ -90,7 +91,7 @@ internal class CrashReport : IDisposable
 
     public int GetDocumentCount() => ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")).Count();
 
-    public List<(string? originalPath, byte[] dotPixiBytes)> RecoverDocuments()
+    public List<(CrashFilePathInfo originalPath, byte[] dotPixiBytes)> RecoverDocuments()
     {
         // Load .pixi files
         Dictionary<string, byte[]> recoveredDocuments = new();
@@ -99,27 +100,29 @@ internal class CrashReport : IDisposable
             using Stream stream = entry.Open();
             using MemoryStream memStream = new();
             stream.CopyTo(memStream);
-            recoveredDocuments.Add(entry.Name, memStream.ToArray());
+            recoveredDocuments.Add(entry.FullName["Documents/".Length..], memStream.ToArray());
         }
 
-        ZipArchiveEntry? originalPathsEntry = ZipFile.Entries.Where(entry => entry.FullName == "DocumentInfo.json").FirstOrDefault();
-        if (originalPathsEntry is null)
-            return recoveredDocuments.Select<KeyValuePair<string, byte[]>, (string?, byte[])>(keyValue => (null, keyValue.Value)).ToList();
-
+        var originalPathsEntry = ZipFile.Entries.First(entry => entry.FullName == "DocumentInfo.json");
+        
         // Load original paths
-        Dictionary<string, string?> originalPaths;
+        Dictionary<string, CrashFilePathInfo> originalPaths;
         {
             using Stream stream = originalPathsEntry.Open();
             using StreamReader reader = new(stream);
             string json = reader.ReadToEnd();
-            originalPaths = JsonConvert.DeserializeObject<Dictionary<string, string?>>(json);
+            originalPaths = JsonConvert.DeserializeObject<Dictionary<string, CrashFilePathInfo>>(json);
+        }
+
+        var list = new List<(CrashFilePathInfo originalPath, byte[] dotPixiBytes)>();
+
+        foreach (var document in recoveredDocuments)
+        {
+            var originalPath = originalPaths[document.Key];
+            list.Add((originalPath, document.Value));
         }
 
-        return (
-            from docKeyValue in recoveredDocuments
-            join pathKeyValue in originalPaths on docKeyValue.Key equals pathKeyValue.Key
-            select (pathKeyValue.Value, docKeyValue.Value)
-            ).ToList();
+        return list;
     }
 
     public void Dispose()
@@ -169,20 +172,22 @@ internal class CrashReport : IDisposable
 
         // Write the documents into zip
         int counter = 0;
-        Dictionary<string, string?> originalPaths = new();
+        Dictionary<string, CrashFilePathInfo> originalPaths = new();
         foreach (DocumentViewModel document in vm.DocumentManagerSubViewModel.Documents)
         {
             try
             {
                 string fileName = string.IsNullOrWhiteSpace(document.FullFilePath) ? "Unsaved" : Path.GetFileNameWithoutExtension(document.FullFilePath);
-                string nameInZip = $"{fileName}-{document.OpenedUTC}-{counter}.pixi".Replace(':', '_');
+                string nameInZip = $"{fileName}-{document.OpenedUTC.ToString(CultureInfo.InvariantCulture)}-{counter.ToString(CultureInfo.InvariantCulture)}.pixi"
+                    .Replace(':', '_')
+                    .Replace('/', '_');
 
                 byte[] serialized = PixiParser.Serialize(document.ToSerializable());
 
                 using Stream documentStream = archive.CreateEntry($"Documents/{nameInZip}").Open();
                 documentStream.Write(serialized);
 
-                originalPaths.Add(nameInZip, document.FullFilePath);
+                originalPaths.Add(nameInZip, new CrashFilePathInfo(document.FullFilePath, null));
             }
             catch { }
             counter++;

+ 5 - 1
src/PixiEditor/Models/Dialogs/OptionsDialog.cs

@@ -2,6 +2,7 @@
 using System.Windows.Controls;
 using System.Windows.Media;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Extensions.UI;
 using PixiEditor.Models.Localization;
 using PixiEditor.Views.Dialogs;
 
@@ -51,9 +52,12 @@ internal class OptionsDialog<T> : CustomDialog, IEnumerable<T>
         set => _results.Add(name, value);
     }
 
-    public override bool ShowDialog()
+    public override bool ShowDialog() => ShowDialog(false);
+
+    public bool ShowDialog(bool topmost)
     {
         var popup = new OptionPopup(Title, Content, new(_results.Keys.Select(x => (object)x)));
+        popup.Topmost = topmost;
         var popupResult = popup.ShowDialog();
 
         Result = (T)popup.Result;

+ 23 - 1
src/PixiEditor/ViewModels/CrashReportViewModel.cs

@@ -3,8 +3,11 @@ using System.IO;
 using System.Net.Http;
 using System.Text;
 using System.Windows;
+using System.Windows.Threading;
+using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
 using PixiEditor.Views;
 using PixiEditor.Views.Dialogs;
 
@@ -45,11 +48,30 @@ internal class CrashReportViewModel : ViewModelBase
 
     public void RecoverDocuments(object args)
     {
-        MainWindow window = MainWindow.CreateWithDocuments(CrashReport.RecoverDocuments());
+        MainWindow window = MainWindow.CreateWithRecoveredDocuments(CrashReport, out var showMissingFilesDialog);
 
         Application.Current.MainWindow = window;
         window.Show();
         hasRecoveredDocuments = false;
+
+        if (showMissingFilesDialog)
+        {
+            var dialog = new OptionsDialog<LocalizedString>(
+                "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE",
+                new LocalizedString("CRASH_NOT_ALL_DOCUMENTS_RECOVERED"))
+            {
+                {
+                    "SEND", _ =>
+                    {
+                        var sendReportDialog = new SendCrashReportWindow(CrashReport);
+                        sendReportDialog.ShowDialog();
+                    }
+                },
+                "CLOSE"
+            };
+
+            dialog.ShowDialog(true);
+        }
     }
 
     [Conditional("DEBUG")]

+ 1 - 2
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -102,8 +102,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         {
             OpenFromPath(file);
         }
-        else if ((Owner.DocumentManagerSubViewModel.Documents.Count == 0
-                  || !args.Contains("--crash")) && !args.Contains("--openedInExisting"))
+        else if ((Owner.DocumentManagerSubViewModel.Documents.Count == 0 && !args.Contains("--crash")) && !args.Contains("--openedInExisting"))
         {
             if (IPreferences.Current.GetPreference("ShowStartupWindow", true))
             {

+ 1 - 0
src/PixiEditor/Views/Dialogs/OptionsPopup.xaml.cs

@@ -39,6 +39,7 @@ internal partial class OptionPopup : Window
 
     public OptionPopup(string title, object content, ObservableCollection<object> options)
     {
+        Title = title;
         PopupContent = content;
         Options = options;
         CancelCommand = new RelayCommand(Cancel);

+ 37 - 3
src/PixiEditor/Views/MainWindow.xaml.cs

@@ -16,11 +16,14 @@ using PixiEditor.Extensions.UI;
 using PixiEditor.Helpers;
 using PixiEditor.Models.AppExtensions;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.IO;
 using PixiEditor.Platform;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
+using PixiEditor.Views.Dialogs;
 
 namespace PixiEditor.Views;
 
@@ -79,6 +82,14 @@ internal partial class MainWindow : Window
         });
 
         DataContext.DocumentManagerSubViewModel.ActiveDocumentChanged += DocumentChanged;
+        
+        ContentRendered += OnContentRendered;
+    }
+
+    private void OnContentRendered(object sender, EventArgs e)
+    {
+        LoadingWindow.Instance?.SafeClose();
+        Activate();
     }
 
     private void SetupTranslator()
@@ -96,16 +107,39 @@ internal partial class MainWindow : Window
         GlobalMouseHook.Instance.Initilize(this);
     }
 
-    public static MainWindow CreateWithDocuments(IEnumerable<(string? originalPath, byte[] dotPixiBytes)> documents)
+    public static MainWindow CreateWithRecoveredDocuments(CrashReport report, out bool showMissingFilesDialog)
     {
-        MainWindow window = new(extLoader);
+        var app = (App)Application.Current;
+        MainWindow window = new(app.InitApp());
         FileViewModel fileVM = window.services.GetRequiredService<FileViewModel>();
+        var documents = report.RecoverDocuments();
+
+        var i = 0;
 
         foreach (var (path, bytes) in documents)
         {
-            fileVM.OpenRecoveredDotPixi(path, bytes);
+            try
+            {
+                fileVM.OpenRecoveredDotPixi(path.OriginalPath, bytes);
+                i++;
+            }
+            catch (Exception e)
+            {
+                try
+                {
+                    fileVM.OpenFromPath(path.AutosavePath, false);
+                }
+                catch (Exception deepE)
+                {
+                    CrashHelper.SendExceptionInfoToWebhook(deepE);
+                }
+                
+                CrashHelper.SendExceptionInfoToWebhook(e);
+            }
         }
 
+        showMissingFilesDialog = documents.Count != i;
+
         return window;
     }