diff --git a/src/Models/CommitLink.cs b/src/Models/CommitLink.cs new file mode 100644 index 00000000..6a421797 --- /dev/null +++ b/src/Models/CommitLink.cs @@ -0,0 +1,8 @@ +namespace SourceGit.Models +{ + public class CommitLink + { + public string Name { get; set; } = null; + public string URLTemplate { get; set; } = null; + } +} diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml index a159d764..5f148b50 100644 --- a/src/Resources/Icons.axaml +++ b/src/Resources/Icons.axaml @@ -59,6 +59,7 @@ M40 9 15 23 15 31 9 28 9 20 34 5 24 0 0 14 0 34 25 48 25 28 49 14zM26 29 26 48 49 34 49 15z M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l132 0 0-128 64 0 0 128 132 0 0 64-132 0 0 128-64 0 0-128-132 0Z M408 232C408 210 426 192 448 192h416a40 40 0 110 80H448a40 40 0 01-40-40zM408 512c0-22 18-40 40-40h416a40 40 0 110 80H448A40 40 0 01408 512zM448 752A40 40 0 00448 832h416a40 40 0 100-80H448zM32 480l328 0 0 64-328 0Z + M 968 418 l -95 94 c -59 59 -146 71 -218 37 L 874 331 a 64 64 0 0 0 0 -90 L 783 150 a 64 64 0 0 0 -90 0 L 475 368 c -34 -71 -22 -159 37 -218 l 94 -94 c 75 -75 196 -75 271 0 l 90 90 c 75 75 75 196 0 271 z M 332 693 a 64 64 0 0 1 0 -90 l 271 -271 c 25 -25 65 -25 90 0 s 25 65 0 90 L 422 693 a 64 64 0 0 1 -90 0 z M 151 783 l 90 90 a 64 64 0 0 0 90 0 l 218 -218 c 34 71 22 159 -37 218 l -86 94 a 192 192 0 0 1 -271 0 l -98 -98 a 192 192 0 0 1 0 -271 l 94 -86 c 59 -59 146 -71 218 -37 L 151 693 a 64 64 0 0 0 0 90 z M0 33h1024v160H0zM0 432h1024v160H0zM0 831h1024v160H0z M512 0C233 0 7 223 0 500C6 258 190 64 416 64c230 0 416 200 416 448c0 53 43 96 96 96s96-43 96-96c0-283-229-512-512-512zm0 1023c279 0 505-223 512-500c-6 242-190 436-416 436c-230 0-416-200-416-448c0-53-43-96-96-96s-96 43-96 96c0 283 229 512 512 512z M976 0h-928A48 48 0 000 48v652a48 48 0 0048 48h416V928H200a48 48 0 000 96h624a48 48 0 000-96H560v-180h416a48 48 0 0048-48V48A48 48 0 00976 0zM928 652H96V96h832v556z diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index ae0e2cb9..04fe4d06 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -66,7 +66,7 @@ namespace SourceGit.ViewModels if (value == null || value.Count != 1) DiffContext = null; else - DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value[0]), _diffContext); + DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_commit, value[0]), _diffContext); } } } @@ -89,15 +89,19 @@ namespace SourceGit.ViewModels set => SetProperty(ref _viewRevisionFileContent, value); } - public AvaloniaList IssueTrackerRules + public AvaloniaList WebLinks { - get => _issueTrackerRules; + get => _repo.TryGetCommitLinks(); } - public CommitDetail(string repo, AvaloniaList issueTrackerRules) + public AvaloniaList IssueTrackerRules + { + get => _repo.Settings?.IssueTrackerRules; + } + + public CommitDetail(Repository repo) { _repo = repo; - _issueTrackerRules = issueTrackerRules; } public void Cleanup() @@ -118,8 +122,7 @@ namespace SourceGit.ViewModels public void NavigateTo(string commitSHA) { - var repo = App.FindOpenedRepository(_repo); - repo?.NavigateToCommit(commitSHA); + _repo?.NavigateToCommit(commitSHA); } public void ClearSearchChangeFilter() @@ -129,7 +132,7 @@ namespace SourceGit.ViewModels public List GetRevisionFilesUnderFolder(string parentFolder) { - return new Commands.QueryRevisionObjects(_repo, _commit.SHA, parentFolder).Result(); + return new Commands.QueryRevisionObjects(_repo.FullPath, _commit.SHA, parentFolder).Result(); } public void ViewRevisionFile(Models.Object file) @@ -145,13 +148,13 @@ namespace SourceGit.ViewModels case Models.ObjectType.Blob: Task.Run(() => { - var isBinary = new Commands.IsBinary(_repo, _commit.SHA, file.Path).Result(); + var isBinary = new Commands.IsBinary(_repo.FullPath, _commit.SHA, file.Path).Result(); if (isBinary) { var ext = Path.GetExtension(file.Path); if (IMG_EXTS.Contains(ext)) { - var stream = Commands.QueryFileContent.Run(_repo, _commit.SHA, file.Path); + var stream = Commands.QueryFileContent.Run(_repo.FullPath, _commit.SHA, file.Path); var bitmap = stream.Length > 0 ? new Bitmap(stream) : null; Dispatcher.UIThread.Invoke(() => { @@ -160,7 +163,7 @@ namespace SourceGit.ViewModels } else { - var size = new Commands.QueryFileSize(_repo, file.Path, _commit.SHA).Result(); + var size = new Commands.QueryFileSize(_repo.FullPath, file.Path, _commit.SHA).Result(); Dispatcher.UIThread.Invoke(() => { ViewRevisionFileContent = new Models.RevisionBinaryFile() { Size = size }; @@ -170,7 +173,7 @@ namespace SourceGit.ViewModels return; } - var contentStream = Commands.QueryFileContent.Run(_repo, _commit.SHA, file.Path); + var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _commit.SHA, file.Path); var content = new StreamReader(contentStream).ReadToEnd(); var matchLFS = REG_LFS_FORMAT().Match(content); if (matchLFS.Success) @@ -191,7 +194,7 @@ namespace SourceGit.ViewModels case Models.ObjectType.Commit: Task.Run(() => { - var submoduleRoot = Path.Combine(_repo, file.Path); + var submoduleRoot = Path.Combine(_repo.FullPath, file.Path); var commit = new Commands.QuerySingleCommit(submoduleRoot, file.SHA).Result(); if (commit != null) { @@ -237,7 +240,7 @@ namespace SourceGit.ViewModels var toolPath = Preference.Instance.ExternalMergeToolPath; var opt = new Models.DiffOption(_commit, change); - Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, toolType, toolPath, opt)); + Task.Run(() => Commands.MergeTool.OpenForDiff(_repo.FullPath, toolType, toolPath, opt)); ev.Handled = true; }; menu.Items.Add(diffWithMerger); @@ -249,7 +252,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path, _issueTrackerRules) }; + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) }; window.Show(); ev.Handled = true; }; @@ -259,12 +262,12 @@ namespace SourceGit.ViewModels blame.Icon = App.CreateMenuIcon("Icons.Blame"); blame.Click += (_, ev) => { - var window = new Views.Blame() { DataContext = new Blame(_repo, change.Path, _commit.SHA) }; + var window = new Views.Blame() { DataContext = new Blame(_repo.FullPath, change.Path, _commit.SHA) }; window.Show(); ev.Handled = true; }; - var full = Path.GetFullPath(Path.Combine(_repo, change.Path)); + var full = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path)); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); explore.Icon = App.CreateMenuIcon("Icons.Explore"); @@ -312,7 +315,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, ev) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, file.Path, _issueTrackerRules) }; + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, file.Path) }; window.Show(); ev.Handled = true; }; @@ -323,12 +326,12 @@ namespace SourceGit.ViewModels blame.IsEnabled = file.Type == Models.ObjectType.Blob; blame.Click += (_, ev) => { - var window = new Views.Blame() { DataContext = new Blame(_repo, file.Path, _commit.SHA) }; + var window = new Views.Blame() { DataContext = new Blame(_repo.FullPath, file.Path, _commit.SHA) }; window.Show(); ev.Handled = true; }; - var full = Path.GetFullPath(Path.Combine(_repo, file.Path)); + var full = Path.GetFullPath(Path.Combine(_repo.FullPath, file.Path)); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); explore.Icon = App.CreateMenuIcon("Icons.Explore"); @@ -353,7 +356,7 @@ namespace SourceGit.ViewModels if (selected.Count == 1) { var saveTo = Path.Combine(selected[0].Path.LocalPath, Path.GetFileName(file.Path)); - Commands.SaveRevisionFile.Run(_repo, _commit.SHA, file.Path, saveTo); + Commands.SaveRevisionFile.Run(_repo.FullPath, _commit.SHA, file.Path, saveTo); } ev.Handled = true; @@ -406,9 +409,9 @@ namespace SourceGit.ViewModels Task.Run(() => { - var fullMessage = new Commands.QueryCommitFullMessage(_repo, _commit.SHA).Result(); + var fullMessage = new Commands.QueryCommitFullMessage(_repo.FullPath, _commit.SHA).Result(); var parent = _commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : _commit.Parents[0]; - var cmdChanges = new Commands.CompareRevisions(_repo, parent, _commit.SHA) { Cancel = _cancelToken }; + var cmdChanges = new Commands.CompareRevisions(_repo.FullPath, parent, _commit.SHA) { Cancel = _cancelToken }; var changes = cmdChanges.Result(); var visible = changes; if (!string.IsNullOrWhiteSpace(_searchChangeFilter)) @@ -463,8 +466,7 @@ namespace SourceGit.ViewModels ".ico", ".bmp", ".jpg", ".png", ".jpeg" }; - private string _repo; - private AvaloniaList _issueTrackerRules = null; + private Repository _repo = null; private int _activePageIndex = 0; private Models.Commit _commit = null; private string _fullMessage = string.Empty; diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 79696a7e..e1284b2f 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Avalonia.Collections; using Avalonia.Threading; - using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -35,7 +33,7 @@ namespace SourceGit.ViewModels } else { - DiffContext = new DiffContext(_repo, new Models.DiffOption(value, _file), _diffContext); + DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(value, _file), _diffContext); DetailContext.Commit = value; } } @@ -54,15 +52,15 @@ namespace SourceGit.ViewModels set => SetProperty(ref _detailContext, value); } - public FileHistories(string repo, string file, AvaloniaList issueTrackerRules) + public FileHistories(Repository repo, string file) { _repo = repo; _file = file; - _detailContext = new CommitDetail(repo, issueTrackerRules); + _detailContext = new CommitDetail(repo); Task.Run(() => { - var commits = new Commands.QueryCommits(_repo, $"-n 10000 -- \"{file}\"", false).Result(); + var commits = new Commands.QueryCommits(_repo.FullPath, $"-n 10000 -- \"{file}\"", false).Result(); Dispatcher.UIThread.Invoke(() => { IsLoading = false; @@ -73,7 +71,7 @@ namespace SourceGit.ViewModels }); } - private readonly string _repo = null; + private readonly Repository _repo = null; private readonly string _file = null; private bool _isLoading = true; private List _commits = null; diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 8f21bf70..4a0a05a0 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -93,7 +93,7 @@ namespace SourceGit.ViewModels } else { - var commitDetail = new CommitDetail(_repo.FullPath, _repo.Settings.IssueTrackerRules); + var commitDetail = new CommitDetail(_repo); commitDetail.Commit = commit; DetailContext = commitDetail; } @@ -121,7 +121,7 @@ namespace SourceGit.ViewModels } else { - var commitDetail = new CommitDetail(_repo.FullPath, _repo.Settings.IssueTrackerRules); + var commitDetail = new CommitDetail(_repo); commitDetail.Commit = commit; DetailContext = commitDetail; } diff --git a/src/ViewModels/InteractiveRebase.cs b/src/ViewModels/InteractiveRebase.cs index 32417e01..0c8838e0 100644 --- a/src/ViewModels/InteractiveRebase.cs +++ b/src/ViewModels/InteractiveRebase.cs @@ -114,7 +114,7 @@ namespace SourceGit.ViewModels Current = current; On = on; IsLoading = true; - DetailContext = new CommitDetail(repoPath, repo.Settings.IssueTrackerRules); + DetailContext = new CommitDetail(repo); Task.Run(() => { diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index e248967a..6cbe9576 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -491,6 +491,25 @@ namespace SourceGit.ViewModels PopupHost.ShowAndStartPopup(new Cleanup(this)); } + public AvaloniaList TryGetCommitLinks() + { + var rs = new AvaloniaList(); + foreach (var remote in _remotes) + { + if (remote.TryGetVisitURL(out var url)) + { + if (url.StartsWith("https://github.com/", StringComparison.Ordinal)) + rs.Add(new Models.CommitLink() { Name = "Github", URLTemplate = $"{url}/commit/SOURCEGIT_COMMIT_HASH_CODE" }); + else if (url.StartsWith("https://gitlab.com/", StringComparison.Ordinal)) + rs.Add(new Models.CommitLink() { Name = "GitLab", URLTemplate = $"{url}/-/commit/SOURCEGIT_COMMIT_HASH_CODE" }); + else if (url.StartsWith("https://gitee.com/", StringComparison.Ordinal)) + rs.Add(new Models.CommitLink() { Name = "Gitee", URLTemplate = $"{url}/commit/SOURCEGIT_COMMIT_HASH_CODE" }); + } + } + + return rs; + } + public void ClearHistoriesFilter() { _settings.Filters.Clear(); diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index c72900ec..9658eb48 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -567,7 +567,7 @@ namespace SourceGit.ViewModels history.Icon = App.CreateMenuIcon("Icons.Histories"); history.Click += (_, e) => { - var window = new Views.FileHistories() { DataContext = new FileHistories(_repo.FullPath, change.Path, _repo.Settings.IssueTrackerRules) }; + var window = new Views.FileHistories() { DataContext = new FileHistories(_repo, change.Path) }; window.Show(); e.Handled = true; }; diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index d2c57810..53e194ff 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -54,7 +54,17 @@ - + + + + + + diff --git a/src/Views/CommitBaseInfo.axaml.cs b/src/Views/CommitBaseInfo.axaml.cs index 86451dfe..8c5e3d98 100644 --- a/src/Views/CommitBaseInfo.axaml.cs +++ b/src/Views/CommitBaseInfo.axaml.cs @@ -2,20 +2,12 @@ using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; namespace SourceGit.Views { public partial class CommitBaseInfo : UserControl { - public static readonly StyledProperty CanNavigateProperty = - AvaloniaProperty.Register(nameof(CanNavigate), true); - - public bool CanNavigate - { - get => GetValue(CanNavigateProperty); - set => SetValue(CanNavigateProperty, value); - } - public static readonly StyledProperty MessageProperty = AvaloniaProperty.Register(nameof(Message), string.Empty); @@ -25,6 +17,15 @@ namespace SourceGit.Views set => SetValue(MessageProperty, value); } + public static readonly StyledProperty> WebLinksProperty = + AvaloniaProperty.Register>(nameof(WebLinks)); + + public AvaloniaList WebLinks + { + get => GetValue(WebLinksProperty); + set => SetValue(WebLinksProperty, value); + } + public static readonly StyledProperty> IssueTrackerRulesProperty = AvaloniaProperty.Register>(nameof(IssueTrackerRules)); @@ -39,11 +40,43 @@ namespace SourceGit.Views InitializeComponent(); } + private void OnOpenWebLink(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.CommitDetail detail) + { + var links = WebLinks; + if (links.Count > 1) + { + var menu = new ContextMenu(); + + foreach (var link in links) + { + var url = link.URLTemplate.Replace("SOURCEGIT_COMMIT_HASH_CODE", detail.Commit.SHA); + var item = new MenuItem() { Header = link.Name }; + item.Click += (_, ev) => + { + Native.OS.OpenBrowser(url); + ev.Handled = true; + }; + + menu.Items.Add(item); + } + + (sender as Control)?.OpenContextMenu(menu); + } + else if (links.Count == 1) + { + var url = links[0].URLTemplate.Replace("SOURCEGIT_COMMIT_HASH_CODE", detail.Commit.SHA); + Native.OS.OpenBrowser(url); + } + } + + e.Handled = true; + } + private void OnParentSHAPressed(object sender, PointerPressedEventArgs e) { - if (sender is Control { DataContext: string sha } && - DataContext is ViewModels.CommitDetail detail && - CanNavigate) + if (DataContext is ViewModels.CommitDetail detail && sender is Control { DataContext: string sha }) { detail.NavigateTo(sha); } diff --git a/src/Views/CommitDetail.axaml b/src/Views/CommitDetail.axaml index af733f49..432fa737 100644 --- a/src/Views/CommitDetail.axaml +++ b/src/Views/CommitDetail.axaml @@ -21,6 +21,7 @@ diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index b573a32d..9e927205 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -176,7 +176,7 @@ - + @@ -190,7 +190,7 @@ - + diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index 5066195d..aef71e19 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.axaml @@ -73,7 +73,7 @@ - +