refactor<Exception>: add context to exception to filter exceptions; each page has it's own error display control

This commit is contained in:
leo 2023-10-13 11:16:03 +08:00
parent c85052bbcc
commit 838e688a0c
15 changed files with 79 additions and 44 deletions

View file

@ -8,6 +8,7 @@ namespace SourceGit {
/// 程序入口. /// 程序入口.
/// </summary> /// </summary>
public partial class App : Application { public partial class App : Application {
public static event Action<string, string> ExceptionRaised;
/// <summary> /// <summary>
/// 读取本地化字串 /// 读取本地化字串
@ -21,6 +22,15 @@ namespace SourceGit {
return string.Format(data, args); return string.Format(data, args);
} }
/// <summary>
/// 触发错误
/// </summary>
/// <param name="ctx">错误上下文</param>
/// <param name="detail">错误内容</param>
public static void Exception(string ctx, string detail) {
ExceptionRaised?.Invoke(ctx, detail);
}
/// <summary> /// <summary>
/// 启动. /// 启动.
/// </summary> /// </summary>

View file

@ -178,7 +178,7 @@ namespace SourceGit.Commands {
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
public virtual void OnException(string message) { public virtual void OnException(string message) {
Models.Exception.Raise(message); App.Exception(Cwd, message);
} }
} }
} }

View file

@ -1,15 +0,0 @@
using System;
namespace SourceGit.Models {
/// <summary>
/// 错误通知
/// </summary>
public static class Exception {
public static Action<string> Handler { get; set; }
public static void Raise(string error) {
Handler?.Invoke(error);
}
}
}

View file

@ -63,7 +63,7 @@ namespace SourceGit.Views {
} }
if (!Directory.Exists(path)) { if (!Directory.Exists(path)) {
Models.Exception.Raise($"Folder {path} not found!"); Dispatcher.Invoke(() => txtError.Text = $"Folder {path} not found!");
return false; return false;
} }

View file

@ -70,11 +70,5 @@
<!-- Contents --> <!-- Contents -->
<controls:PageContainer x:Name="container" Grid.Row="1"/> <controls:PageContainer x:Name="container" Grid.Row="1"/>
<!-- Alerts -->
<widgets:Exceptions
Grid.Row="1"
HorizontalAlignment="Right" VerticalAlignment="Top"
Width="330" Height="Auto"/>
</Grid> </Grid>
</controls:Window> </controls:Window>

View file

@ -34,7 +34,7 @@ namespace SourceGit.Views.Popups {
var squash = chkSquash.IsChecked == true; var squash = chkSquash.IsChecked == true;
if (repo.SubTrees.FindIndex(x => x.Prefix == Prefix) >= 0) { if (repo.SubTrees.FindIndex(x => x.Prefix == Prefix) >= 0) {
Models.Exception.Raise($"Subtree add failed. Prefix({Prefix}) already exists!"); App.Exception(repo.Path, $"Subtree add failed. Prefix({Prefix}) already exists!");
return null; return null;
} }

View file

@ -14,7 +14,7 @@ namespace SourceGit.Views.Popups {
public FastForwardWithoutCheckout(string repo, string branch, string upstream) { public FastForwardWithoutCheckout(string repo, string branch, string upstream) {
int idx = upstream.IndexOf('/'); int idx = upstream.IndexOf('/');
if (idx < 0 || idx == upstream.Length - 1) { if (idx < 0 || idx == upstream.Length - 1) {
Models.Exception.Raise($"Invalid upstream: {upstream}"); App.Exception(repo, $"Invalid upstream: {upstream}");
return; return;
} }

View file

@ -1,4 +1,5 @@
<UserControl x:Class="SourceGit.Views.Widgets.Dashboard" <UserControl x:Class="SourceGit.Views.Widgets.Dashboard"
x:Name="me"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -592,5 +593,12 @@
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>
<!-- Alerts -->
<widgets:Exceptions
Grid.Row="1"
HorizontalAlignment="Right" VerticalAlignment="Top"
Width="330" Height="Auto"
Context="{Binding ElementName=me, Path=ExceptionContext}"/>
</Grid> </Grid>
</UserControl> </UserControl>

View file

@ -54,6 +54,8 @@ namespace SourceGit.Views.Widgets {
} }
} }
public string ExceptionContext => repo.Path;
public Dashboard(Models.Repository repo) { public Dashboard(Models.Repository repo) {
this.repo = repo; this.repo = repo;
@ -402,7 +404,7 @@ namespace SourceGit.Views.Widgets {
private void OpenInTerminal(object sender, RoutedEventArgs e) { private void OpenInTerminal(object sender, RoutedEventArgs e) {
var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe"); var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe");
if (!File.Exists(bash)) { if (!File.Exists(bash)) {
Models.Exception.Raise(App.Text("MissingBash")); App.Exception(repo.Path, App.Text("MissingBash"));
return; return;
} }
@ -428,7 +430,7 @@ namespace SourceGit.Views.Widgets {
private void OpenFetch(object sender, RoutedEventArgs e) { private void OpenFetch(object sender, RoutedEventArgs e) {
if (repo.Remotes.Count == 0) { if (repo.Remotes.Count == 0) {
Models.Exception.Raise("No remotes added to this repository!!!"); App.Exception(repo.Path, "No remotes added to this repository!!!");
return; return;
} }
@ -438,7 +440,7 @@ namespace SourceGit.Views.Widgets {
private void OpenPull(object sender, RoutedEventArgs e) { private void OpenPull(object sender, RoutedEventArgs e) {
if (repo.Remotes.Count == 0) { if (repo.Remotes.Count == 0) {
Models.Exception.Raise("No remotes added to this repository!!!"); App.Exception(repo.Path, "No remotes added to this repository!!!");
return; return;
} }
@ -448,7 +450,7 @@ namespace SourceGit.Views.Widgets {
private void OpenPush(object sender, RoutedEventArgs e) { private void OpenPush(object sender, RoutedEventArgs e) {
if (repo.Remotes.Count == 0) { if (repo.Remotes.Count == 0) {
Models.Exception.Raise("No remotes added to this repository!!!"); App.Exception(repo.Path, "No remotes added to this repository!!!");
return; return;
} }
@ -576,7 +578,7 @@ namespace SourceGit.Views.Widgets {
if (current != null) { if (current != null) {
new Popups.CreateBranch(repo, current).Show(); new Popups.CreateBranch(repo, current).Show();
} else { } else {
Models.Exception.Raise(App.Text("CreateBranch.Idle")); App.Exception(repo.Path, App.Text("CreateBranch.Idle"));
} }
e.Handled = true; e.Handled = true;
} }

View file

@ -604,7 +604,7 @@ namespace SourceGit.Views.Widgets {
var merger = Models.MergeTool.Supported.Find(x => x.Type == mergeType); var merger = Models.MergeTool.Supported.Find(x => x.Type == mergeType);
if (merger == null || merger.Type == 0 || !System.IO.File.Exists(mergeExe)) { if (merger == null || merger.Type == 0 || !System.IO.File.Exists(mergeExe)) {
Models.Exception.Raise("Invalid merge tool in preference setting!"); App.Exception(repo, "Invalid merge tool in preference setting!");
return; return;
} }

View file

@ -10,9 +10,31 @@ namespace SourceGit.Views.Widgets {
public partial class Exceptions : UserControl { public partial class Exceptions : UserControl {
public ObservableCollection<string> Messages { get; set; } public ObservableCollection<string> Messages { get; set; }
/// <summary>
/// 用于判断异常是否属于自己的上下文属性
/// </summary>
public static readonly DependencyProperty ContextProperty = DependencyProperty.Register(
"Context",
typeof(string),
typeof(Exceptions),
new PropertyMetadata(null));
/// <summary>
/// 上下文
/// </summary>
public string Context {
get { return (string)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
public Exceptions() { public Exceptions() {
App.ExceptionRaised += (ctx, detail) => {
Dispatcher.Invoke(() => {
if (ctx == Context) Messages.Add(detail);
});
};
Messages = new ObservableCollection<string>(); Messages = new ObservableCollection<string>();
Models.Exception.Handler = e => Dispatcher.Invoke(() => Messages.Add(e));
InitializeComponent(); InitializeComponent();
} }

View file

@ -303,7 +303,7 @@ namespace SourceGit.Views.Widgets {
} }
} }
Models.Exception.Raise("Can NOT found parent of HEAD!"); App.Exception(repo.Path, "Can NOT found parent of HEAD!");
e.Handled = true; e.Handled = true;
}; };
menu.Items.Add(squash); menu.Items.Add(squash);

View file

@ -1,4 +1,5 @@
<UserControl x:Class="SourceGit.Views.Widgets.Welcome" <UserControl x:Class="SourceGit.Views.Widgets.Welcome"
x:Name="me"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -156,6 +157,13 @@
FontWeight="Bold" FontWeight="Bold"
Text="{DynamicResource Text.Welcome.DragDropTip}" Text="{DynamicResource Text.Welcome.DragDropTip}"
Foreground="{DynamicResource Brush.FG2}"/> Foreground="{DynamicResource Brush.FG2}"/>
</StackPanel> </StackPanel>
<!-- Alerts -->
<local:Exceptions
Grid.Row="1"
HorizontalAlignment="Right" VerticalAlignment="Top"
Width="330" Height="Auto"
Context="{Binding ElementName=me, Path=ExceptionContext}"/>
</Grid> </Grid>
</UserControl> </UserControl>

View file

@ -14,8 +14,14 @@ namespace SourceGit.Views.Widgets {
/// 新标签页 /// 新标签页
/// </summary> /// </summary>
public partial class Welcome : UserControl { public partial class Welcome : UserControl {
public string ExceptionContext {
get;
set;
}
public Welcome() { public Welcome() {
ExceptionContext = Guid.NewGuid().ToString();
InitializeComponent(); InitializeComponent();
UpdateVisibles(); UpdateVisibles();
@ -34,7 +40,7 @@ namespace SourceGit.Views.Widgets {
if (MakeSureReady()) { if (MakeSureReady()) {
var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe"); var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe");
if (!File.Exists(bash)) { if (!File.Exists(bash)) {
Models.Exception.Raise(App.Text("MissingBash")); App.Exception(ExceptionContext, App.Text("MissingBash"));
return; return;
} }
@ -175,7 +181,7 @@ namespace SourceGit.Views.Widgets {
var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe"); var bash = Path.Combine(Models.Preference.Instance.Git.Path, "..", "bash.exe");
if (!File.Exists(bash)) { if (!File.Exists(bash)) {
Models.Exception.Raise(App.Text("MissingBash")); App.Exception(ExceptionContext, App.Text("MissingBash"));
return; return;
} }
@ -245,7 +251,7 @@ namespace SourceGit.Views.Widgets {
private bool MakeSureReady() { private bool MakeSureReady() {
if (!Models.Preference.Instance.IsReady) { if (!Models.Preference.Instance.IsReady) {
Models.Exception.Raise(App.Text("NotConfigured")); App.Exception(ExceptionContext, App.Text("NotConfigured"));
return false; return false;
} }
return true; return true;
@ -255,7 +261,7 @@ namespace SourceGit.Views.Widgets {
if (!MakeSureReady()) return; if (!MakeSureReady()) return;
if (!Directory.Exists(path)) { if (!Directory.Exists(path)) {
Models.Exception.Raise(App.Text("PathNotFound", path)); App.Exception(ExceptionContext, App.Text("PathNotFound", path));
return; return;
} }

View file

@ -220,7 +220,7 @@ namespace SourceGit.Views.Widgets {
var merger = Models.MergeTool.Supported.Find(x => x.Type == mergeType); var merger = Models.MergeTool.Supported.Find(x => x.Type == mergeType);
if (merger == null || merger.Type == 0 || !File.Exists(mergeExe)) { if (merger == null || merger.Type == 0 || !File.Exists(mergeExe)) {
Models.Exception.Raise("Invalid merge tool in preference setting!"); App.Exception(repo.Path, "Invalid merge tool in preference setting!");
return; return;
} }
@ -289,7 +289,7 @@ namespace SourceGit.Views.Widgets {
private void StartAmend(object sender, RoutedEventArgs e) { private void StartAmend(object sender, RoutedEventArgs e) {
var commits = new Commands.Commits(repo.Path, "-n 1", false).Result(); var commits = new Commands.Commits(repo.Path, "-n 1", false).Result();
if (commits.Count == 0) { if (commits.Count == 0) {
Models.Exception.Raise("No commits to amend!"); App.Exception(repo.Path, "No commits to amend!");
chkAmend.IsChecked = false; chkAmend.IsChecked = false;
return; return;
} }
@ -316,12 +316,12 @@ namespace SourceGit.Views.Widgets {
var changes = await Task.Run(() => new Commands.LocalChanges(repo.Path).Result()); var changes = await Task.Run(() => new Commands.LocalChanges(repo.Path).Result());
var conflict = changes.Find(x => x.IsConflit); var conflict = changes.Find(x => x.IsConflit);
if (conflict != null) { if (conflict != null) {
Models.Exception.Raise("You have unsolved conflicts in your working copy!"); App.Exception(repo.Path, "You have unsolved conflicts in your working copy!");
return; return;
} }
if (stagedContainer.Changes.Count == 0) { if (stagedContainer.Changes.Count == 0) {
Models.Exception.Raise("No files added to commit!"); App.Exception(repo.Path, "No files added to commit!");
return; return;
} }
@ -351,12 +351,12 @@ namespace SourceGit.Views.Widgets {
var changes = await Task.Run(() => new Commands.LocalChanges(repo.Path).Result()); var changes = await Task.Run(() => new Commands.LocalChanges(repo.Path).Result());
var conflict = changes.Find(x => x.IsConflit); var conflict = changes.Find(x => x.IsConflit);
if (conflict != null) { if (conflict != null) {
Models.Exception.Raise("You have unsolved conflicts in your working copy!"); App.Exception(repo.Path, "You have unsolved conflicts in your working copy!");
return; return;
} }
if (stagedContainer.Changes.Count == 0) { if (stagedContainer.Changes.Count == 0) {
Models.Exception.Raise("No files added to commit!"); App.Exception(repo.Path, "No files added to commit!");
return; return;
} }