feat: details the logic of issue commit.
This commit is contained in:
parent
f246aa17bd
commit
852d935e80
@ -12,6 +12,7 @@
|
||||
<entry key="Flawless.Client/Views/HelloView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HelloWindowView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HomeView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/IssueDetailEditView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/IssueDetailView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/LoginView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/MainView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
@ -21,6 +22,7 @@
|
||||
<entry key="Flawless.Client/Views/ModalBox/CreateRepositoryDialog.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/ModalBox/CreateRepositoryDialogView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/ModalBox/EditRepositoryMemberDialogueView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/ModalBox/IssueDetailEditView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/ModalBox/SimpleMessageDialogView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/RegisterPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/RegisterView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
|
||||
@ -346,7 +346,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
return true;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> UpdateIssueDetailsFromServerAsync(RepositoryModel repo, ulong issueId)
|
||||
public async ValueTask<bool> UpdateIssueDetailsFromServerAsync(RepositoryModel repo, ulong issueId, bool titleOnly)
|
||||
{
|
||||
var api = Api.C;
|
||||
try
|
||||
@ -358,8 +358,6 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
|
||||
var issue = await api.Gateway.Issue(repo.Name, repo.OwnerName, (long)issueId);
|
||||
var details = (await api.Gateway.Comments(repo.Name, repo.OwnerName, (long)issueId))
|
||||
.Result.ToImmutableDictionary(x => x.CommentId);
|
||||
|
||||
var entity = repo.Issues.FirstOrDefault(x => x.Id == issueId);
|
||||
var tags = issue.Tag.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
@ -380,6 +378,11 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
entity.Tags.AddRange(tags);
|
||||
repo.Issues.Add(entity);
|
||||
|
||||
if (!titleOnly)
|
||||
{
|
||||
var details = (await api.Gateway.Comments(repo.Name, repo.OwnerName, (long)issueId))
|
||||
.Result.ToImmutableDictionary(x => x.CommentId);
|
||||
|
||||
entity.Comments.AddRange(details.Select(x => new RepositoryModel.Comment
|
||||
{
|
||||
Id = (ulong)x.Key,
|
||||
@ -389,6 +392,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
ReplyTo = x.Value.ReplyToId.HasValue ? (ulong)x.Value.ReplyToId.Value : null
|
||||
}));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.Title = issue.Title;
|
||||
@ -399,6 +403,11 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
entity.Tags.Clear();
|
||||
entity.Tags.AddRange(tags);
|
||||
|
||||
if (!titleOnly)
|
||||
{
|
||||
var details = (await api.Gateway.Comments(repo.Name, repo.OwnerName, (long)issueId))
|
||||
.Result.ToImmutableDictionary(x => x.CommentId);
|
||||
|
||||
for (var i = 0; i < entity.Comments.Count; i++)
|
||||
{
|
||||
var c = entity.Comments[i];
|
||||
@ -430,6 +439,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repo.Issues.Sort((x, y) => (int) ((long) x.Id - (long) y.Id));
|
||||
}
|
||||
@ -443,7 +453,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
return true;
|
||||
}
|
||||
|
||||
public async ValueTask<IssueInfo?> CreateIssueAsync(RepositoryModel repo, string title, string description, IEnumerable<string>? tags)
|
||||
public async ValueTask<ulong?> CreateIssueAsync(RepositoryModel repo, string title, string description, IEnumerable<string>? tags)
|
||||
{
|
||||
var api = Api.C;
|
||||
try
|
||||
@ -460,10 +470,27 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
new StringBuilder().AppendJoin(',', tags);
|
||||
}
|
||||
|
||||
return await api.Gateway.Create(
|
||||
var issue = await api.Gateway.Create(
|
||||
repo.OwnerName,
|
||||
repo.Name,
|
||||
new CreateIssueRequest { Title = title, Description = description, Tag = tagString?.ToString()});
|
||||
|
||||
var entity = new RepositoryModel.Issue
|
||||
{
|
||||
Author = issue.Author,
|
||||
Id = (ulong) issue.Id,
|
||||
Title = issue.Title,
|
||||
Description = null,
|
||||
Closed = issue.Closed,
|
||||
CreatedAt = issue.CreateAt.UtcDateTime,
|
||||
LastUpdatedAt = issue.LastUpdate.UtcDateTime
|
||||
};
|
||||
|
||||
entity.Tags.AddRange(tags ?? []);
|
||||
repo.Issues.Add(entity);
|
||||
|
||||
repo.Issues.Sort((x, y) => (int) ((long) x.Id - (long) y.Id));
|
||||
return entity.Id;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -485,6 +512,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
|
||||
await api.Gateway.Close(repo.OwnerName, repo.Name, (long) issueId);
|
||||
await UpdateIssueDetailsFromServerAsync(repo, issueId, true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -507,6 +535,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
|
||||
await api.Gateway.Reopen(repo.OwnerName, repo.Name, (long) issueId);
|
||||
await UpdateIssueDetailsFromServerAsync(repo, issueId, true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -540,7 +569,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<bool> UpdateIssueAsync(RepositoryModel repo, long issueId, string? title, string? description, IEnumerable<string>? tags)
|
||||
public async ValueTask<bool> UpdateIssueAsync(RepositoryModel repo, ulong issueId, string? title, string? description, IEnumerable<string>? tags)
|
||||
{
|
||||
var api = Api.C;
|
||||
try
|
||||
@ -557,7 +586,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
new StringBuilder().AppendJoin(',', tags);
|
||||
}
|
||||
|
||||
await api.Gateway.Edit(repo.OwnerName, repo.Name, issueId,
|
||||
await api.Gateway.Edit(repo.OwnerName, repo.Name, (long) issueId,
|
||||
new UpdateIssueRequest { Title = title, Description = description, Tag = tagString?.ToString()});
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ public static class UIHelper
|
||||
}
|
||||
}
|
||||
|
||||
public static OverlayDialogOptions DefaultOverlayDialogOptions()
|
||||
public static OverlayDialogOptions DefaultOverlayDialogOptionsYesNo()
|
||||
{
|
||||
return new OverlayDialogOptions
|
||||
{
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
@ -6,7 +7,10 @@ using Flawless.Client.Models;
|
||||
using ReactiveUI;
|
||||
using Flawless.Client.Service;
|
||||
using Flawless.Client.ViewModels;
|
||||
using Flawless.Client.ViewModels.ModalBox;
|
||||
using Flawless.Client.Views.ModalBox;
|
||||
using ReactiveUI.SourceGenerators;
|
||||
using Ursa.Controls;
|
||||
|
||||
public partial class IssueDetailViewModel : RoutableViewModelBase
|
||||
{
|
||||
@ -24,16 +28,16 @@ public partial class IssueDetailViewModel : RoutableViewModelBase
|
||||
{
|
||||
_repo = repo;
|
||||
_service = RepositoryService.Current;
|
||||
LoadDataAsync(repo, issueId);
|
||||
LoadDataAsync(repo, issueId, true);
|
||||
}
|
||||
|
||||
private async Task LoadDataAsync(RepositoryModel repo, ulong issueId)
|
||||
private async Task LoadDataAsync(RepositoryModel repo, ulong issueId, bool quitIfFailed)
|
||||
{
|
||||
using var _ = UIHelper.MakeLoading("Fetching comments from server...");
|
||||
if (!await _service.UpdateIssueDetailsFromServerAsync(repo, issueId))
|
||||
if (!await _service.UpdateIssueDetailsFromServerAsync(repo, issueId, false))
|
||||
{
|
||||
await NavigateBackAsync();
|
||||
UIHelper.NotifyError("Operation failed", "Can not open issue...");
|
||||
if (quitIfFailed) await NavigateBackAsync();
|
||||
UIHelper.NotifyError("Operation failed", "Can not load issue...");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -41,7 +45,7 @@ public partial class IssueDetailViewModel : RoutableViewModelBase
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private Task RefreshIssueAsync() => LoadDataAsync(_repo, CurrentIssue!.Id);
|
||||
private Task RefreshIssueAsync() => LoadDataAsync(_repo, CurrentIssue!.Id, false);
|
||||
|
||||
[ReactiveCommand]
|
||||
private async Task ToggleIssueStatusAsync()
|
||||
@ -52,7 +56,6 @@ public partial class IssueDetailViewModel : RoutableViewModelBase
|
||||
if (CurrentIssue.Closed)
|
||||
{
|
||||
if (!await _service.ReopenIssueAsync(_repo, CurrentIssue.Id)) return;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -62,6 +65,13 @@ public partial class IssueDetailViewModel : RoutableViewModelBase
|
||||
CurrentIssue.Closed = !CurrentIssue.Closed;
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private void MarkAsReplayTo(RepositoryModel.Comment? comment)
|
||||
{
|
||||
if (CurrentIssue == null) return;
|
||||
ReplyTo = comment;
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async Task AddCommentAsync()
|
||||
{
|
||||
@ -84,6 +94,26 @@ public partial class IssueDetailViewModel : RoutableViewModelBase
|
||||
[ReactiveCommand]
|
||||
private async Task EditIssueAsync()
|
||||
{
|
||||
if (CurrentIssue == null) return;
|
||||
|
||||
var opt = UIHelper.DefaultOverlayDialogOptionsYesNo();
|
||||
var vm = new IssueEditDialogViewModel(CurrentIssue);
|
||||
|
||||
var result = await OverlayDialog.ShowModal<IssueDetailEditView, IssueEditDialogViewModel>(
|
||||
vm, AppDefaultValues.HostId, opt);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
using var l = UIHelper.MakeLoading("Updating issue...");
|
||||
var tags = vm.Tag?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
// 调用服务端更新接口
|
||||
await RepositoryService.C.UpdateIssueAsync(
|
||||
_repo, CurrentIssue.Id,
|
||||
vm.Title, vm.Description, tags
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
|
||||
@ -422,25 +422,40 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
[ReactiveCommand]
|
||||
private async Task CreateIssueAsync()
|
||||
{
|
||||
var opt = UIHelper.DefaultOverlayDialogOptionsYesNo();
|
||||
var vm = new IssueEditDialogViewModel();
|
||||
|
||||
var r = await OverlayDialog.ShowModal<IssueDetailEditView, IssueEditDialogViewModel>(vm, AppDefaultValues.HostId, opt);
|
||||
if (r == DialogResult.Yes)
|
||||
{
|
||||
using var l = UIHelper.MakeLoading("Sending issue...");
|
||||
var tags = vm.Tag?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var issueId = await RepositoryService.C.CreateIssueAsync(Repository, vm.Title, vm.Description, tags);
|
||||
if (issueId == null) return;
|
||||
|
||||
await HostScreen.Router.Navigate.Execute(new IssueDetailViewModel(HostScreen, Repository, issueId.Value));
|
||||
}
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async Task OpenIssueAsync()
|
||||
private async Task OpenIssueAsync(RepositoryModel.Issue issue)
|
||||
{
|
||||
|
||||
using var l = UIHelper.MakeLoading("Config issue...");
|
||||
await HostScreen.Router.Navigate.Execute(new IssueDetailViewModel(HostScreen, Repository, issue.Id));
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async Task CloseIssueAsync()
|
||||
private async Task CloseIssueAsync(RepositoryModel.Issue issue)
|
||||
{
|
||||
|
||||
using var l = UIHelper.MakeLoading("Config issue...");
|
||||
await RepositoryService.C.CloseIssueAsync(Repository, issue.Id);
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async Task ReopenIssueAsync()
|
||||
private async Task ReopenIssueAsync(RepositoryModel.Issue issue)
|
||||
{
|
||||
|
||||
using var l = UIHelper.MakeLoading("Config issue...");
|
||||
await RepositoryService.C.ReopenIssueAsync(Repository, issue.Id);
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
@ -553,6 +568,13 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
UpdatePermissionOfRepository();
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async ValueTask RefreshRepositoryIssuesAsync()
|
||||
{
|
||||
using var l = UIHelper.MakeLoading("Refreshing issues...");
|
||||
await RepositoryService.C.UpdateIssuesListFromServerAsync(Repository);
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async ValueTask DeleteMemberFromServerAsync(RepositoryModel.Member member)
|
||||
{
|
||||
@ -581,7 +603,7 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
return;
|
||||
}
|
||||
|
||||
var style = UIHelper.DefaultOverlayDialogOptions();
|
||||
var style = UIHelper.DefaultOverlayDialogOptionsYesNo();
|
||||
var vm = new EditRepositoryMemberDialogViewModel();
|
||||
vm.LockUsername = true;
|
||||
vm.Username = member.Username;
|
||||
@ -618,7 +640,7 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
return;
|
||||
}
|
||||
|
||||
var style = UIHelper.DefaultOverlayDialogOptions();
|
||||
var style = UIHelper.DefaultOverlayDialogOptionsYesNo();
|
||||
var vm = new EditRepositoryMemberDialogViewModel();
|
||||
var result = await OverlayDialog.ShowModal<EditRepositoryMemberDialogueView,EditRepositoryMemberDialogViewModel>
|
||||
(vm, AppDefaultValues.HostId, style);
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
xmlns:vm="using:Flawless.Client.ViewModels"
|
||||
xmlns:u="https://irihi.tech/ursa"
|
||||
xmlns:global="clr-namespace:"
|
||||
x:DataType="global:IssueDetailViewModel">
|
||||
x:DataType="global:IssueDetailViewModel"
|
||||
x:Class="Flawless.Client.Views.IssueDetailView">
|
||||
|
||||
<DockPanel Margin="20">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8">
|
||||
|
||||
14
Flawless.Client/Views/IssueDetailView.axaml.cs
Normal file
14
Flawless.Client/Views/IssueDetailView.axaml.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ursa.ReactiveUIExtension;
|
||||
|
||||
namespace Flawless.Client.Views;
|
||||
|
||||
public partial class IssueDetailView : ReactiveUrsaView<IssueDetailViewModel>
|
||||
{
|
||||
public IssueDetailView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,11 @@
|
||||
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:vm="using:Flawless.Client.ViewModels.ModalBox"
|
||||
xmlns:u="https://irihi.tech/ursa"
|
||||
xmlns:vm="clr-namespace:Flawless.Client.ViewModels.ModalBox"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:DataType="vm:IssueEditDialogViewModel"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Flawless.Client.Views.RepositoryPage.IssueEditDialogView">
|
||||
x:Class="Flawless.Client.Views.ModalBox.IssueDetailEditView">
|
||||
|
||||
<u:Form>
|
||||
<u:FormItem Label="Title" IsRequired="True">
|
||||
@ -22,4 +22,5 @@
|
||||
SelectedItem="{Binding Tag}"/>
|
||||
</u:FormItem>
|
||||
</u:Form>
|
||||
|
||||
</UserControl>
|
||||
13
Flawless.Client/Views/ModalBox/IssueDetailEditView.axaml.cs
Normal file
13
Flawless.Client/Views/ModalBox/IssueDetailEditView.axaml.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Flawless.Client.Views.ModalBox;
|
||||
|
||||
public partial class IssueDetailEditView : UserControl
|
||||
{
|
||||
public IssueDetailEditView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8" Margin="0 0 0 12">
|
||||
<u:IconButton Icon="{StaticResource SemiIconRefresh}" Content="刷新"
|
||||
Command="{Binding RefreshRepositoryIssuesAsyncCommand}"/>
|
||||
<u:IconButton Icon="{StaticResource SemiIconPlus}" Content="新建 Issue"
|
||||
Command="{Binding CreateIssueCommand}"/>
|
||||
<!-- <ComboBox ItemsSource="{Binding IssueFilters}" SelectedIndex="0" -->
|
||||
@ -41,17 +43,15 @@
|
||||
Foreground="{DynamicResource SemiSecondaryTextColor}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- <WrapPanel Grid.Column="1" Grid.Row="2"> -->
|
||||
<!-- <Border CornerRadius="4" Padding="4 2" Background="{DynamicResource SemiInfoBgColor}"> -->
|
||||
<!-- <TextBlock Text="{Binding Id, StringFormat='#{}'}" -->
|
||||
<!-- Foreground="{DynamicResource SemiInfoColor}"/> -->
|
||||
<!-- </Border> -->
|
||||
<WrapPanel Grid.Column="1" Grid.Row="2">
|
||||
<Border CornerRadius="4" Padding="4 2" Background="{DynamicResource SemiInfoBgColor}">
|
||||
<TextBlock Text="{Binding Id, StringFormat='#{}'}"
|
||||
Foreground="{DynamicResource SemiInfoColor}"/>
|
||||
</Border>
|
||||
<!-- <Border CornerRadius="4" Padding="4 2" Background="{DynamicResource SemiSuccessBgColor}" -->
|
||||
<!-- IsVisible="{Binding !!Tag}"> -->
|
||||
<!-- <TextBlock Text="{Binding Tag}" -->
|
||||
<!-- Foreground="{DynamicResource SemiSuccessColor}"/> -->
|
||||
<!-- </Border> -->
|
||||
<!-- </WrapPanel> -->
|
||||
</WrapPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8">
|
||||
<u:IconButton Icon="{StaticResource SemiIconEdit}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user