1
0

feat: Create base of issue page.

This commit is contained in:
Ca2didi 2025-04-15 01:18:48 +08:00
parent 62d7398035
commit f246aa17bd
9 changed files with 306 additions and 9 deletions

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
<option name="ckgOperationStatus" value="SUCCESS" />
</component>
</project>

View File

@ -12,6 +12,7 @@
<entry key="Flawless.Client/Views/HelloView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <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/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/HomeView.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/LoginView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/MainView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/MainView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/MainWindow.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/MainWindow.axaml" value="Flawless.Client/Flawless.Client.csproj" />
@ -19,9 +20,11 @@
<entry key="Flawless.Client/Views/ModalBox/AddRepositoryMemberDialogueView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/ModalBox/AddRepositoryMemberDialogueView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/ModalBox/CreateRepositoryDialog.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <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/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/SimpleMessageDialogView.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/RegisterPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/RegisterView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/RegisterView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/RepositoryPage/IssueEditDialogView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/RepositoryPage/RepoCommitPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/RepositoryPage/RepoCommitPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/RepositoryPage/RepoDashboardPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/RepositoryPage/RepoDashboardPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
<entry key="Flawless.Client/Views/RepositoryPage/RepoFileTreePageView.axaml" value="Flawless.Client/Flawless.Client.csproj" /> <entry key="Flawless.Client/Views/RepositoryPage/RepoFileTreePageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />

View File

@ -473,7 +473,7 @@ public class RepositoryService : BaseService<RepositoryService>
} }
} }
public async ValueTask<bool> CloseIssueAsync(RepositoryModel repo, long issueId) public async ValueTask<bool> CloseIssueAsync(RepositoryModel repo, ulong issueId)
{ {
var api = Api.C; var api = Api.C;
try try
@ -484,7 +484,7 @@ public class RepositoryService : BaseService<RepositoryService>
return false; return false;
} }
await api.Gateway.Close(repo.OwnerName, repo.Name, issueId); await api.Gateway.Close(repo.OwnerName, repo.Name, (long) issueId);
return true; return true;
} }
catch (Exception e) catch (Exception e)
@ -495,7 +495,7 @@ public class RepositoryService : BaseService<RepositoryService>
} }
} }
public async ValueTask<bool> ReopenIssueAsync(RepositoryModel repo, long issueId) public async ValueTask<bool> ReopenIssueAsync(RepositoryModel repo, ulong issueId)
{ {
var api = Api.C; var api = Api.C;
try try
@ -506,7 +506,7 @@ public class RepositoryService : BaseService<RepositoryService>
return false; return false;
} }
await api.Gateway.Reopen(repo.OwnerName, repo.Name, issueId); await api.Gateway.Reopen(repo.OwnerName, repo.Name, (long) issueId);
return true; return true;
} }
catch (Exception e) catch (Exception e)

View File

@ -0,0 +1,91 @@
using System.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading.Tasks;
using Flawless.Client;
using Flawless.Client.Models;
using ReactiveUI;
using Flawless.Client.Service;
using Flawless.Client.ViewModels;
using ReactiveUI.SourceGenerators;
public partial class IssueDetailViewModel : RoutableViewModelBase
{
[Reactive] private RepositoryModel.Issue? _currentIssue;
[Reactive] private string _newComment;
[Reactive] private RepositoryModel.Comment? _replyTo;
private readonly RepositoryService _service;
private readonly RepositoryModel _repo;
public IssueDetailViewModel(IScreen screen, RepositoryModel repo, ulong issueId) : base(screen)
{
_repo = repo;
_service = RepositoryService.Current;
LoadDataAsync(repo, issueId);
}
private async Task LoadDataAsync(RepositoryModel repo, ulong issueId)
{
using var _ = UIHelper.MakeLoading("Fetching comments from server...");
if (!await _service.UpdateIssueDetailsFromServerAsync(repo, issueId))
{
await NavigateBackAsync();
UIHelper.NotifyError("Operation failed", "Can not open issue...");
return;
}
CurrentIssue = repo.Issues.First(x => x.Id == issueId);
}
[ReactiveCommand]
private Task RefreshIssueAsync() => LoadDataAsync(_repo, CurrentIssue!.Id);
[ReactiveCommand]
private async Task ToggleIssueStatusAsync()
{
if (CurrentIssue == null) return;
using var _ = UIHelper.MakeLoading("Update issue...");
if (CurrentIssue.Closed)
{
if (!await _service.ReopenIssueAsync(_repo, CurrentIssue.Id)) return;
}
else
{
if (!await _service.CloseIssueAsync(_repo, CurrentIssue.Id)) return;
}
CurrentIssue.Closed = !CurrentIssue.Closed;
}
[ReactiveCommand]
private async Task AddCommentAsync()
{
if (CurrentIssue == null) return;
using var _ = UIHelper.MakeLoading("Update issue...");
if (string.IsNullOrWhiteSpace(NewComment))
{
UIHelper.NotifyError("No input", "No comment has been written!");
}
else
{
if (!await _service.AddCommentAsync(_repo, CurrentIssue.Id, NewComment, _replyTo?.Id ?? null)) return;
_replyTo = null;
NewComment = string.Empty;
}
}
[ReactiveCommand]
private async Task EditIssueAsync()
{
}
[ReactiveCommand]
private Task NavigateBackAsync() => HostScreen.Router.NavigateBack.Execute().ToTask();
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Text;
using Flawless.Client.Models;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace Flawless.Client.ViewModels.ModalBox;
public partial class IssueEditDialogViewModel : ReactiveObject
{
[Reactive] public string Title { get; set; } = string.Empty;
[Reactive] public string Description { get; set; } = string.Empty;
[Reactive] public string? Tag { get; set; }
public bool IsEditMode { get; }
public List<string> AvailableTags { get; } = new() { "bug", "feature", "question" };
public IssueEditDialogViewModel(RepositoryModel.Issue? existingIssue = null)
{
IsEditMode = existingIssue != null;
if (IsEditMode)
{
Title = existingIssue!.Title;
Description = existingIssue.Description ?? string.Empty;
Tag = new StringBuilder().AppendJoin(',', existingIssue.Tags).ToString();
}
}
}

View File

@ -401,7 +401,6 @@ public partial class RepositoryViewModel : RoutableViewModelBase
var changes = new List<ChangeInfo>(); var changes = new List<ChangeInfo>();
CollectChanges(changes, LocalChangeSetRaw); CollectChanges(changes, LocalChangeSetRaw);
var result = await UIHelper.SimpleAskAsync( var result = await UIHelper.SimpleAskAsync(
"All files will being matched with this commit. Do you wish to execute it?", "All files will being matched with this commit. Do you wish to execute it?",
DialogMode.Warning); DialogMode.Warning);
@ -420,6 +419,29 @@ public partial class RepositoryViewModel : RoutableViewModelBase
} }
} }
[ReactiveCommand]
private async Task CreateIssueAsync()
{
}
[ReactiveCommand]
private async Task OpenIssueAsync()
{
}
[ReactiveCommand]
private async Task CloseIssueAsync()
{
}
[ReactiveCommand]
private async Task ReopenIssueAsync()
{
}
[ReactiveCommand] [ReactiveCommand]
private async Task PullLatestRepositoryAsync() private async Task PullLatestRepositoryAsync()

View File

@ -0,0 +1,63 @@
<UserControl 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:vm="using:Flawless.Client.ViewModels"
xmlns:u="https://irihi.tech/ursa"
xmlns:global="clr-namespace:"
x:DataType="global:IssueDetailViewModel">
<DockPanel Margin="20">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8">
<u:IconButton Icon="{StaticResource SemiIconArrowLeft}"
Command="{Binding NavigateBackCommand}"/>
<TextBlock Text="{Binding CurrentIssue.Title}" FontSize="24"/>
</StackPanel>
<ScrollViewer>
<StackPanel Spacing="16">
<!-- Issue基本信息 -->
<Border Classes="CardBorder" Padding="16">
<StackPanel Spacing="8">
<TextBlock Text="{Binding CurrentIssue.Description}"/>
<WrapPanel >
<!-- <u:Badge Content="{Binding CurrentIssue.}"/> -->
<TextBlock Text="{Binding CurrentIssue.Author}"/>
<TextBlock Text="{Binding CurrentIssue.CreatedAt}"/>
</WrapPanel>
</StackPanel>
</Border>
<!-- 操作栏 -->
<StackPanel Orientation="Horizontal" Spacing="8">
<u:IconButton Content="{Binding ToggleIssueStatusCommand}"
Command="{Binding ToggleIssueStatusCommand}"/>
<u:IconButton Icon="{StaticResource SemiIconEdit}"
Command="{Binding EditIssueCommand}"/>
</StackPanel>
<!-- 评论列表 -->
<ListBox ItemsSource="{Binding CurrentIssue.Comments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0 8">
<TextBlock Text="{Binding Content}"/>
<TextBlock Text="{Binding Author}"/>
<TextBlock Text="{Binding CreatedAt}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
<!-- 添加评论 -->
<StackPanel DockPanel.Dock="Bottom">
<TextBox Text="{Binding NewComment}"
Watermark="输入评论..."
AcceptsReturn="True"/>
<u:IconButton Icon="{StaticResource SemiIconSend}"
Command="{Binding AddCommentCommand}"/>
</StackPanel>
</DockPanel>
</UserControl>

View File

@ -0,0 +1,25 @@
<UserControl 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:vm="using:Flawless.Client.ViewModels.ModalBox"
xmlns:u="https://irihi.tech/ursa"
x:DataType="vm:IssueEditDialogViewModel"
mc:Ignorable="d"
x:Class="Flawless.Client.Views.RepositoryPage.IssueEditDialogView">
<u:Form>
<u:FormItem Label="Title" IsRequired="True">
<TextBox Text="{Binding Title}"/>
</u:FormItem>
<u:FormItem Label="Description">
<TextBox AcceptsReturn="True"
Text="{Binding Description}"
MinHeight="100"/>
</u:FormItem>
<u:FormItem Label="Tags">
<ComboBox ItemsSource="{Binding AvailableTags}"
SelectedItem="{Binding Tag}"/>
</u:FormItem>
</u:Form>
</UserControl>

View File

@ -3,10 +3,68 @@
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:vm="using:Flawless.Client.ViewModels" xmlns:vm="using:Flawless.Client.ViewModels"
xmlns:u="https://irihi.tech/ursa"
x:DataType="vm:RepositoryViewModel" x:DataType="vm:RepositoryViewModel"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Flawless.Client.Views.RepositoryPage.RepoIssuePageView"> x:Class="Flawless.Client.Views.RepositoryPage.RepoIssuePageView">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Label Content="Sit down and wait patience."></Label> <DockPanel>
</StackPanel> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8" Margin="0 0 0 12">
<u:IconButton Icon="{StaticResource SemiIconPlus}" Content="新建 Issue"
Command="{Binding CreateIssueCommand}"/>
<!-- <ComboBox ItemsSource="{Binding IssueFilters}" SelectedIndex="0" -->
<!-- Width="120" PlaceholderText="筛选状态"/> -->
<!-- <ComboBox ItemsSource="{Binding IssueTags}" SelectedIndex="0" -->
<!-- Width="160" PlaceholderText="筛选标签"/> -->
</StackPanel>
<ListBox ItemsSource="{Binding Repository.Issues}" SelectionMode="Single">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="auto,*,auto" RowDefinitions="auto,auto,auto"
Margin="0 4" Classes="CardBorder">
<PathIcon Grid.Column="0" Grid.RowSpan="3" Width="24" Height="24" Margin="0 0 12 0"
Data="{StaticResource SemiIconCheckCircle}"
IsVisible="{Binding !Closed}"/>
<PathIcon Grid.Column="0" Grid.RowSpan="3" Width="24" Height="24" Margin="0 0 12 0"
Data="{StaticResource SemiIconCloseCircle}"
IsVisible="{Binding Closed}"/>
<TextBlock Grid.Column="1" Grid.Row="0"
FontSize="16" FontWeight="SemiBold"
Text="{Binding Title}"/>
<StackPanel Grid.Column="1" Grid.Row="1" Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Author}"
Foreground="{DynamicResource SemiSecondaryTextColor}"/>
<TextBlock Text="{Binding CreatedAt, StringFormat='创建于 {0:yyyy-MM-dd}'}"
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> -->
<!-- <Border CornerRadius="4" Padding="4 2" Background="{DynamicResource SemiSuccessBgColor}" -->
<!-- IsVisible="{Binding !!Tag}"> -->
<!-- <TextBlock Text="{Binding Tag}" -->
<!-- Foreground="{DynamicResource SemiSuccessColor}"/> -->
<!-- </Border> -->
<!-- </WrapPanel> -->
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8">
<u:IconButton Icon="{StaticResource SemiIconEdit}"
Command="{Binding $parent[ItemsControl].((vm:RepositoryViewModel)DataContext).OpenIssueCommand}"
CommandParameter="{Binding}"/>
<u:IconButton Icon="{StaticResource SemiIconClose}"
Command="{Binding $parent[ItemsControl].((vm:RepositoryViewModel)DataContext).CloseIssueCommand}"
CommandParameter="{Binding}"
IsVisible="{Binding !Closed}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</UserControl> </UserControl>