Browse Source

Crash report wip

Krzysztof Krysiński 1 year ago
parent
commit
1fa7349fec

+ 1 - 1
src/PixiEditor.AvaloniaUI.Desktop/PixiEditor.AvaloniaUI.Desktop.csproj

@@ -7,7 +7,7 @@
         <Nullable>enable</Nullable>
         <Nullable>enable</Nullable>
         <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
         <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
         <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
         <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
-        <RootNamespace>PixiEditor.Avalonia.Desktop</RootNamespace>
+        <RootNamespace>PixiEditor.AvaloniaUI.Desktop</RootNamespace>
     </PropertyGroup>
     </PropertyGroup>
 
 
     <PropertyGroup>
     <PropertyGroup>

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

@@ -593,5 +593,15 @@
   "SWATCHES_DOCKABLE_TITLE": "Swatches",
   "SWATCHES_DOCKABLE_TITLE": "Swatches",
     "PALETTE_DOCKABLE_TITLE": "Palette",
     "PALETTE_DOCKABLE_TITLE": "Palette",
   
   
-  "BROWSE_DIRECTORY": "Browse Directory"
+  "BROWSE_DIRECTORY": "Browse Directory",
+  "SEND_CRASH_REPORT_TITLE": "Send crash report",
+  "OPEN_IN_EXPLORER": "Open in explorer",
+  "CRASH_SEND_METHODS": "You can send your crash report using:",
+  "EMAIL": "E-Mail",
+  "CRASH_REPORT_DESCRIPTION": "The report contains the documents that were opened when the crash happened, feel free to review it before sending.",
+  "PIXIEDITOR_CRASHED_TITLE": "PixiEditor has crashed!",
+  "CRASH_ACTION_DESCRIPTION": "You can help the developers fix this bug by sending a crash report that was generated (you will still be able to recover the files).",
+  "SEND_REPORT": "Send report",
+  "RECOVER_FILES": "Recover files",
+  "ATTACH_DEBUGGER": "(Re)Attach debugger"
 }
 }

+ 5 - 1
src/PixiEditor.AvaloniaUI/Models/ExceptionHandling/CrashReport.cs

@@ -330,11 +330,15 @@ internal class CrashReport : IDisposable
 
 
     public void RestartToCrashReport()
     public void RestartToCrashReport()
     {
     {
+        // TODO: IOperatingSystem interface
         Process process = new();
         Process process = new();
 
 
+        //TODO: Handle different name for the executable, .Desktop.exe
+        string fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".Desktop.exe";
+
         process.StartInfo = new()
         process.StartInfo = new()
         {
         {
-            FileName = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "exe"),
+            FileName = fileName,
             Arguments = $"--crash \"{Path.GetFullPath(FilePath)}\""
             Arguments = $"--crash \"{Path.GetFullPath(FilePath)}\""
         };
         };
 
 

+ 6 - 5
src/PixiEditor.AvaloniaUI/ViewModels/CrashReportViewModel.cs

@@ -7,6 +7,7 @@ using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.Dialogs;
 using PixiEditor.AvaloniaUI.Models.Dialogs;
 using PixiEditor.AvaloniaUI.Models.ExceptionHandling;
 using PixiEditor.AvaloniaUI.Models.ExceptionHandling;
 using PixiEditor.AvaloniaUI.Views;
 using PixiEditor.AvaloniaUI.Views;
+using PixiEditor.AvaloniaUI.Views.Dialogs;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.Localization;
 
 
 namespace PixiEditor.AvaloniaUI.ViewModels;
 namespace PixiEditor.AvaloniaUI.ViewModels;
@@ -23,6 +24,8 @@ internal partial class CrashReportViewModel : ViewModelBase
 
 
     public bool IsDebugBuild { get; set; }
     public bool IsDebugBuild { get; set; }
 
 
+    public RelayCommand OpenSendCrashReportCommand { get; }
+
     public CrashReportViewModel(CrashReport report)
     public CrashReportViewModel(CrashReport report)
     {
     {
         SetIsDebug();
         SetIsDebug();
@@ -30,8 +33,7 @@ internal partial class CrashReportViewModel : ViewModelBase
         CrashReport = report;
         CrashReport = report;
         ReportText = report.ReportText;
         ReportText = report.ReportText;
         DocumentCount = report.GetDocumentCount();
         DocumentCount = report.GetDocumentCount();
-        //TODO: Implement
-        //OpenSendCrashReportCommand = ReactiveCommand.Create(() => new SendCrashReportWindow(CrashReport).Show());
+        OpenSendCrashReportCommand = new RelayCommand(() => new SendCrashReportDialog(CrashReport).Show());
 
 
         if (!IsDebugBuild)
         if (!IsDebugBuild)
             _ = CrashHelper.SendReportTextToWebhookAsync(report);
             _ = CrashHelper.SendReportTextToWebhookAsync(report);
@@ -56,9 +58,8 @@ internal partial class CrashReportViewModel : ViewModelBase
                 {
                 {
                     "SEND", _ =>
                     "SEND", _ =>
                     {
                     {
-                        // TODO
-                        //var sendReportDialog = new SendCrashReportWindow(CrashReport);
-                        //sendReportDialog.ShowDialog();
+                        var sendReportDialog = new SendCrashReportDialog(CrashReport);
+                        sendReportDialog.ShowDialog(window);
                     }
                     }
                 },
                 },
                 "CLOSE"
                 "CLOSE"

+ 29 - 41
src/PixiEditor.AvaloniaUI/Views/Dialogs/CrashReportDialog.axaml

@@ -1,51 +1,39 @@
-<Window x:Class="PixiEditor.AvaloniaUI.Views.Dialogs.CrashReportDialog"
+<dialogs:PixiEditorPopup x:Class="PixiEditor.AvaloniaUI.Views.Dialogs.CrashReportDialog"
         x:ClassModifier="internal"
         x:ClassModifier="internal"
         xmlns="https://github.com/avaloniaui"
         xmlns="https://github.com/avaloniaui"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:dialogs="clr-namespace:PixiEditor.AvaloniaUI.Views.Dialogs"
+        xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+        xmlns:viewModels="clr-namespace:PixiEditor.AvaloniaUI.ViewModels"
         mc:Ignorable="d"
         mc:Ignorable="d"
-        Background="{StaticResource ThemeBackgroundBrush1}" Foreground="White"
-        Title="PixiEditor has crashed!"
+        ui:Translator.Key="PIXIEDITOR_CRASHED_TITLE"
         MinWidth="480" MinHeight="195"
         MinWidth="480" MinHeight="195"
-        WindowStartupLocation="CenterScreen"
+        x:DataType="viewModels:CrashReportViewModel"
         Width="480" Height="195">
         Width="480" Height="195">
+    <Design.DataContext>
+        <viewModels:CrashReportViewModel />
+    </Design.DataContext>
+    <Grid Margin="30,30,30,0">
+        <StackPanel>
+            <Grid Background="{DynamicResource ThemeBackgroundBrush}">
+                <StackPanel Margin="7" VerticalAlignment="Center">
+                    <!--TODO: Translate string format below-->
+                    <TextBlock Text="{Binding DocumentCount, StringFormat={}{0} file(s) might be recoverable}"
+                               d:Text="2 file(s) can be recovered"/>
+                    <TextBlock TextWrapping="Wrap" ui:Translator.Key="CRASH_ACTION_DESCRIPTION"/>
+                </StackPanel>
+            </Grid>
 
 
-    <!--<WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32" GlassFrameThickness="0.1"
-                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
-    </WindowChrome.WindowChrome>
-
-    <Window.CommandBindings>
-        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
-                        Executed="CommandBinding_Executed_Close" />
-    </Window.CommandBindings>-->
-
-    <Grid>
-        <Grid.RowDefinitions>
-            <RowDefinition Height="Auto"/>
-            <RowDefinition/>
-        </Grid.RowDefinitions>
-        <!--<DialogTitleBar TitleKey="PixiEditor has crashed!" CloseCommand="{x:Static SystemCommands.CloseWindowCommand}" />-->
-        <Grid Grid.Row="1" Margin="30,30,30,0" >
-            <StackPanel>
-                <Grid Background="{StaticResource ThemeBackgroundBrush}">
-                    <StackPanel Margin="7" VerticalAlignment="Center">
-                        <TextBlock Text="{Binding DocumentCount, StringFormat={}{0} file(s) might be recoverable}"
-                       d:Text="2 file(s) can be recovered"/>
-                        <TextBlock TextWrapping="Wrap">You can help the developers fix this bug by sending a crash report that was generated (you will still be able to recover the files).</TextBlock>
-                    </StackPanel>
-                </Grid>
-
-                <WrapPanel Margin="0,20,0,5" Orientation="Horizontal" HorizontalAlignment="Center">
-                    <Button Command="{Binding OpenSendCrashReportCommand}"
-                        Width="120">Send report</Button>
-                    <Button Margin="5,0,5,0" Width="120"
-                        Command="{Binding RecoverDocumentsCommand}">Recover files</Button>
-                    <Button IsVisible="{Binding IsDebugBuild}" Width="170"
-                    Command="{Binding AttachDebuggerCommand}">(Re)Attach debugger</Button>
-                </WrapPanel>
-            </StackPanel>
-        </Grid>
+            <WrapPanel Margin="0,20,0,5" Orientation="Horizontal" HorizontalAlignment="Center">
+                <Button Command="{Binding OpenSendCrashReportCommand}"
+                        Width="120" ui:Translator.Key="SEND_REPORT"/>
+                <Button Margin="5,0,5,0" Width="120"
+                        Command="{Binding RecoverDocumentsCommand}" ui:Translator.Key="RECOVER_FILES"/>
+                <Button IsVisible="{Binding IsDebugBuild}" Width="170"
+                        Command="{Binding AttachDebuggerCommand}" ui:Translator.Key="ATTACH_DEBUGGER"/>
+            </WrapPanel>
+        </StackPanel>
     </Grid>
     </Grid>
-</Window>
+</dialogs:PixiEditorPopup>

+ 0 - 10
src/PixiEditor.AvaloniaUI/Views/Dialogs/CrashReportDialog.axaml.cs

@@ -14,14 +14,4 @@ internal partial class CrashReportDialog : Window
         DataContext = new CrashReportViewModel(report);
         DataContext = new CrashReportViewModel(report);
         InitializeComponent();
         InitializeComponent();
     }
     }
-
-    /*private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
-    {
-        e.CanExecute = true;
-    }
-
-    private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
-    {
-        SystemCommands.CloseWindow(this);
-    }*/
 }
 }

+ 25 - 0
src/PixiEditor.AvaloniaUI/Views/Dialogs/SendCrashReportDialog.axaml

@@ -0,0 +1,25 @@
+<dialogs:PixiEditorPopup xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:dialogs="clr-namespace:PixiEditor.AvaloniaUI.Views.Dialogs"
+        xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+        x:Class="PixiEditor.AvaloniaUI.Views.Dialogs.SendCrashReportDialog"
+        MinHeight="195" MinWidth="340"
+        Height="195" Width="340"
+        ui:Translator.Key="SEND_CRASH_REPORT_TITLE">
+    <StackPanel Margin="10">
+        <TextBlock>You can find the report here:</TextBlock>
+        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
+            <Button Width="140" Click="OpenInExplorer" ui:Translator.Key="OPEN_IN_EXPLORER"/>
+        </StackPanel>
+        <TextBlock TextAlignment="Center" ui:Translator.Key="CRASH_SEND_METHODS"/>
+        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
+            <Button Click="OpenHyperlink" Tag="github" ui:Translator.Key="GITHUB"/>
+            <Button Click="OpenHyperlink" Tag="discord" ui:Translator.Key="DISCORD"/>
+            <Button Click="OpenHyperlink" Tag="email" ui:Translator.Key="EMAIL"/>
+        </StackPanel>
+        <TextBlock TextWrapping="Wrap" TextAlignment="Center" ui:Translator.Key="CRASH_REPORT_DESCRIPTION"/>
+    </StackPanel>
+</dialogs:PixiEditorPopup>

+ 89 - 0
src/PixiEditor.AvaloniaUI/Views/Dialogs/SendCrashReportDialog.axaml.cs

@@ -0,0 +1,89 @@
+using System.IO;
+using System.Text;
+using System.Web;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using PixiEditor.AvaloniaUI.Models.ExceptionHandling;
+using PixiEditor.AvaloniaUI.ViewModels;
+using PixiEditor.OperatingSystem;
+
+namespace PixiEditor.AvaloniaUI.Views.Dialogs;
+
+internal partial class SendCrashReportDialog : PixiEditorPopup
+{
+    const string DiscordInviteLink = "https://discord.gg/eh8gx6vNEp";
+
+    private readonly CrashReport report;
+
+    public SendCrashReportDialog(CrashReport report)
+    {
+        this.report = report;
+        InitializeComponent();
+    }
+
+    private void OpenInExplorer(object sender, RoutedEventArgs e)
+    {
+        string tempPath = Path.Combine(
+            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+            "PixiEditor",
+            "crash_logs",
+            "to-copy");
+
+        DirectoryInfo info = Directory.CreateDirectory(tempPath);
+
+        foreach (var file in info.EnumerateFiles())
+        {
+            file.Delete();
+        }
+
+        File.Copy(report.FilePath, Path.Combine(tempPath, Path.GetFileName(report.FilePath)), true);
+
+        IOperatingSystem.Current.ProcessUtility.ShellExecute(tempPath);
+    }
+
+    private void OpenHyperlink(object sender, RoutedEventArgs e)
+    {
+        var button = sender as Button;
+        var tag = button.Tag as string;
+
+        string body =
+            HttpUtility.UrlEncode($"** IMPORTANT: Drop the \"{Path.GetFileName(report.FilePath)}\" file in here **");
+
+        var result = tag switch
+        {
+            "github" => GetGitHubLink(),
+            "discord" => DiscordInviteLink,
+            "email" => GetMailtoLink(),
+            _ => throw new NotImplementedException()
+        };
+
+        OpenInExplorer(null, null);
+        IOperatingSystem.Current.ProcessUtility.ShellExecute(result);
+
+        string GetGitHubLink()
+        {
+            StringBuilder builder = new();
+
+            builder.Append("https://github.com/PixiEditor/PixiEditor/issues/new?title=");
+            builder.Append(HttpUtility.UrlEncode($"Crash Report"));
+            builder.Append("&body=");
+            builder.Append(body);
+
+            return builder.ToString();
+        }
+
+        string GetMailtoLink()
+        {
+            StringBuilder builder = new();
+
+            builder.Append("mailto:[email protected]?subject=");
+            builder.Append(HttpUtility.UrlEncode($"Crash Report"));
+            builder.Append("&body=");
+            builder.Append(body);
+
+            return builder.ToString();
+        }
+    }
+}

+ 1 - 0
src/PixiEditor.AvaloniaUI/Views/MainWindow.axaml

@@ -14,6 +14,7 @@
         WindowState="Maximized"
         WindowState="Maximized"
         Name="Window"
         Name="Window"
         Icon="/Images/favicon.ico"
         Icon="/Images/favicon.ico"
+        Initialized="MainWindow_Initialized"
         ui:Translator.UseLanguageFlowDirection="True"
         ui:Translator.UseLanguageFlowDirection="True"
         Title="PixiEditor">
         Title="PixiEditor">
     <views1:MainView />
     <views1:MainView />

+ 5 - 0
src/PixiEditor.AvaloniaUI/Views/MainWindow.axaml.cs

@@ -137,4 +137,9 @@ internal partial class MainWindow : Window
 
 
         base.OnClosing(e);
         base.OnClosing(e);
     }
     }
+
+    private void MainWindow_Initialized(object? sender, EventArgs e)
+    {
+        AppDomain.CurrentDomain.UnhandledException += (sender, e) => Helpers.CrashHelper.SaveCrashInfo((Exception)e.ExceptionObject);
+    }
 }
 }