From db8de81120ee2be80686c32760f570381b743c9e Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 24 Aug 2024 11:36:02 +0800 Subject: [PATCH] refactor: rewrite file histories page to only focus on selected file (#403) --- src/Resources/Locales/en_US.axaml | 2 + src/Resources/Locales/zh_CN.axaml | 2 + src/Resources/Locales/zh_TW.axaml | 2 + src/ViewModels/FileHistories.cs | 157 ++++++++++++++++--- src/Views/FileHistories.axaml | 133 ++++++++++++---- src/Views/FileHistories.axaml.cs | 11 ++ src/Views/RevisionFileContentViewer.axaml | 58 +++++++ src/Views/RevisionFileContentViewer.axaml.cs | 13 ++ src/Views/RevisionFiles.axaml | 52 +----- 9 files changed, 327 insertions(+), 103 deletions(-) create mode 100644 src/Views/RevisionFileContentViewer.axaml create mode 100644 src/Views/RevisionFileContentViewer.axaml.cs diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index a6e1a4cf..0f275deb 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -243,6 +243,8 @@ Use Theirs (checkout --theirs) Use Mine (checkout --ours) File History + CONTENT + CHANGE FILTER Git-Flow Development Branch: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 94c92d53..16f0df3d 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -246,6 +246,8 @@ 使用 THEIRS (checkout --theirs) 使用 MINE (checkout --ours) 文件历史 + 文件内容 + 文件变更 过滤 GIT工作流 开发分支 : diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml index 8e35c100..193c25c4 100644 --- a/src/Resources/Locales/zh_TW.axaml +++ b/src/Resources/Locales/zh_TW.axaml @@ -246,6 +246,8 @@ 使用 THEIRS (checkout --theirs) 使用 MINE (checkout --ours) 檔案歷史 + 檔案内容 + 檔案更改 過濾 GIT工作流 開發分支 : diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index e1284b2f..68c5a7a1 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -1,11 +1,22 @@ using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; using System.Threading.Tasks; + +using Avalonia.Media.Imaging; using Avalonia.Threading; + using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { - public class FileHistories : ObservableObject + public class FileHistoriesRevisionFile(string path, object content) + { + public string Path { get; set; } = path; + public object Content { get; set; } = content; + } + + public partial class FileHistories : ObservableObject { public bool IsLoading { @@ -25,38 +36,30 @@ namespace SourceGit.ViewModels set { if (SetProperty(ref _selectedCommit, value)) - { - if (value == null) - { - DiffContext = null; - DetailContext.Commit = null; - } - else - { - DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(value, _file), _diffContext); - DetailContext.Commit = value; - } - } + RefreshViewContent(); } } - public DiffContext DiffContext + public int ViewMode { - get => _diffContext; - set => SetProperty(ref _diffContext, value); + get => _viewMode; + set + { + if (SetProperty(ref _viewMode, value)) + RefreshViewContent(); + } } - public CommitDetail DetailContext + public object ViewContent { - get => _detailContext; - set => SetProperty(ref _detailContext, value); + get => _viewContent; + private set => SetProperty(ref _viewContent, value); } public FileHistories(Repository repo, string file) { _repo = repo; _file = file; - _detailContext = new CommitDetail(repo); Task.Run(() => { @@ -71,12 +74,122 @@ namespace SourceGit.ViewModels }); } + public void NavigateToCommit(Models.Commit commit) + { + _repo.NavigateToCommit(commit.SHA); + } + + private void RefreshViewContent() + { + if (_selectedCommit == null) + { + ViewContent = null; + return; + } + + if (_viewMode == 0) + SetViewContentAsRevisionFile(); + else + SetViewContentAsDiff(); + } + + private void SetViewContentAsRevisionFile() + { + var objs = new Commands.QueryRevisionObjects(_repo.FullPath, _selectedCommit.SHA, _file).Result(); + if (objs.Count == 0) + { + ViewContent = new FileHistoriesRevisionFile(_file, null); + return; + } + + var obj = objs[0]; + switch (obj.Type) + { + case Models.ObjectType.Blob: + Task.Run(() => + { + var isBinary = new Commands.IsBinary(_repo.FullPath, _selectedCommit.SHA, _file).Result(); + if (isBinary) + { + var ext = Path.GetExtension(_file); + if (IMG_EXTS.Contains(ext)) + { + var stream = Commands.QueryFileContent.Run(_repo.FullPath, _selectedCommit.SHA, _file); + var bitmap = stream.Length > 0 ? new Bitmap(stream) : null; + var image = new Models.RevisionImageFile() { Image = bitmap }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, image)); + } + else + { + var size = new Commands.QueryFileSize(_repo.FullPath, _file, _selectedCommit.SHA).Result(); + var binaryFile = new Models.RevisionBinaryFile() { Size = size }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, binaryFile)); + } + + return; + } + + var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _selectedCommit.SHA, _file); + var content = new StreamReader(contentStream).ReadToEnd(); + var matchLFS = REG_LFS_FORMAT().Match(content); + if (matchLFS.Success) + { + var lfs = new Models.RevisionLFSObject() { Object = new Models.LFSObject() }; + lfs.Object.Oid = matchLFS.Groups[1].Value; + lfs.Object.Size = long.Parse(matchLFS.Groups[2].Value); + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, lfs)); + } + else + { + var txt = new Models.RevisionTextFile() { FileName = obj.Path, Content = content }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, txt)); + } + }); + break; + case Models.ObjectType.Commit: + Task.Run(() => + { + var submoduleRoot = Path.Combine(_repo.FullPath, _file); + var commit = new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).Result(); + if (commit != null) + { + var message = new Commands.QueryCommitFullMessage(submoduleRoot, obj.SHA).Result(); + var module = new Models.RevisionSubmodule() { Commit = commit, FullMessage = message }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); + } + else + { + var module = new Models.RevisionSubmodule() { Commit = new Models.Commit() { SHA = obj.SHA }, FullMessage = "" }; + Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module)); + } + }); + break; + default: + ViewContent = new FileHistoriesRevisionFile(_file, null); + break; + } + } + + private void SetViewContentAsDiff() + { + var option = new Models.DiffOption(_selectedCommit, _file); + ViewContent = new DiffContext(_repo.FullPath, option, _viewContent as DiffContext); + } + + [GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")] + private static partial Regex REG_LFS_FORMAT(); + + private static readonly HashSet IMG_EXTS = new HashSet() + { + ".ico", ".bmp", ".jpg", ".png", ".jpeg" + }; + private readonly Repository _repo = null; private readonly string _file = null; private bool _isLoading = true; private List _commits = null; private Models.Commit _selectedCommit = null; - private DiffContext _diffContext = null; - private CommitDetail _detailContext = null; + private int _viewMode = 0; + private object _viewContent = null; } } diff --git a/src/Views/FileHistories.axaml b/src/Views/FileHistories.axaml index 79e716c4..6c26fbd3 100644 --- a/src/Views/FileHistories.axaml +++ b/src/Views/FileHistories.axaml @@ -22,7 +22,7 @@ - - + @@ -104,40 +111,106 @@ HorizontalAlignment="Center" VerticalAlignment="Center" IsVisible="{Binding IsLoading}"/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - diff --git a/src/Views/FileHistories.axaml.cs b/src/Views/FileHistories.axaml.cs index fc525f14..dad2baa9 100644 --- a/src/Views/FileHistories.axaml.cs +++ b/src/Views/FileHistories.axaml.cs @@ -27,5 +27,16 @@ namespace SourceGit.Views e.Handled = true; } + + private void OnPressCommitSHA(object sender, PointerPressedEventArgs e) + { + if (sender is TextBlock { DataContext: Models.Commit commit } && + DataContext is ViewModels.FileHistories vm) + { + vm.NavigateToCommit(commit); + } + + e.Handled = true; + } } } diff --git a/src/Views/RevisionFileContentViewer.axaml b/src/Views/RevisionFileContentViewer.axaml new file mode 100644 index 00000000..eef7605c --- /dev/null +++ b/src/Views/RevisionFileContentViewer.axaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/RevisionFileContentViewer.axaml.cs b/src/Views/RevisionFileContentViewer.axaml.cs new file mode 100644 index 00000000..bca6a082 --- /dev/null +++ b/src/Views/RevisionFileContentViewer.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; + +namespace SourceGit.Views +{ + public partial class RevisionFileContentViewer : UserControl + { + public RevisionFileContentViewer() + { + InitializeComponent(); + } + } +} + diff --git a/src/Views/RevisionFiles.axaml b/src/Views/RevisionFiles.axaml index bbdd5841..0165ccab 100644 --- a/src/Views/RevisionFiles.axaml +++ b/src/Views/RevisionFiles.axaml @@ -2,7 +2,6 @@ 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:m="using:SourceGit.Models" xmlns:vm="using:SourceGit.ViewModels" xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -28,56 +27,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +