XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Collections.ObjectModel;
using System.Text.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Presentation;

namespace XFEEDToolBox.ViewModels;

public partial class ToolboxViewModel
{
    private static readonly GameEventTypeOption allGameEventTypes=new("","全部事件");
    public LiveCollection<GameEventLiveRow,GameEventEntry,long> GameEventRows{get;}=new(x=>x.Id,x=>new(x));
    public ObservableCollection<GameEventTypeOption> GameEventTypes{get;}=[allGameEventTypes];
    [ObservableProperty] public partial string GameEventSearch{get;set;}="";
    [ObservableProperty] public partial GameEventTypeOption? GameEventType{get;set;}=allGameEventTypes;
    [ObservableProperty] public partial int GameEventSortIndex{get;set;}
    [ObservableProperty] public partial string GameEventLogStatus{get;set;}="正在读取游戏事件";
    [ObservableProperty] public partial string GameEventPageLabel{get;set;}="1 / 1";
    [ObservableProperty] public partial bool GameEventLogBusy{get;set;}
    [ObservableProperty] public partial bool GameEventLogHasMore{get;set;}
    [ObservableProperty,NotifyPropertyChangedFor(nameof(HasGameEventDetails))]
    public partial GameEventDetails? SelectedGameEventDetail{get;set;}
    public bool HasGameEventDetails=>SelectedGameEventDetail is not null;
    public bool GameLogPageVisible{get;set;}
    private readonly List<GameEventCursor?> gameLogPages=[null];
    private int gameLogPage,gameLogQuery;

    partial void OnGameEventSearchChanged(string value)=>ResetGameLogSearch();
    partial void OnGameEventTypeChanged(GameEventTypeOption? value)=>ResetGameLogSearch();
    partial void OnGameEventSortIndexChanged(int value)=>ResetGameLogSearch();
    partial void OnGameEventLogBusyChanged(bool value)
    {
        LatestGameLogCommand.NotifyCanExecuteChanged();
        PreviousGameLogPageCommand.NotifyCanExecuteChanged();
        NextGameLogPageCommand.NotifyCanExecuteChanged();
    }
    private async void ResetGameLogSearch()
    {
        var query=++gameLogQuery;gameLogPages.Clear();gameLogPages.Add(null);gameLogPage=0;
        try{await Task.Delay(200,lifetime.Token);if(query==gameLogQuery)await RefreshGameEventsAsync();}
        catch(OperationCanceledException){}
    }
    public async Task RefreshGameEventsAsync()
    {
        var query=++gameLogQuery;var cursor=gameLogPages[gameLogPage];var search=GameEventSearch;
        var eventName=GameEventType?.Event??"";var oldestFirst=GameEventSortIndex==1;var firstPage=gameLogPage==0;
        GameEventLogBusy=true;
        try
        {
            var result=await Task.Run(()=>
                (Page:runtime.GameEvents.Query(search,cursor,eventName:eventName,oldestFirst:oldestFirst),
                 Types:firstPage?runtime.GameEvents.EventTypes():null),lifetime.Token);
            if(query!=gameLogQuery)return;
            if(result.Types is not null)
                foreach(var type in result.Types)
                    if(!GameEventTypes.Any(option=>option.Event==type))
                    {
                        var translated=GameEventNames.Translate(type);
                        var option=new GameEventTypeOption(type,translated=="游戏事件"?type:$"{translated} · {type}");
                        var position=1;
                        while(position<GameEventTypes.Count&&StringComparer.OrdinalIgnoreCase.Compare(GameEventTypes[position].Event,type)<0)position++;
                        GameEventTypes.Insert(position,option);
                    }
            GameEventRows.Apply(result.Page.Entries);GameEventLogHasMore=result.Page.HasMore;
            GameEventLogStatus=$"共 {result.Page.Count:N0} 条事件 · 第 {gameLogPage+1} 页 · 当前 {result.Page.Entries.Count} 条";
            GameEventPageLabel=$"{gameLogPage+1:N0} / {Math.Max(1,(result.Page.Count+199)/200):N0}";
        }
        catch(Exception error)when(error is not OperationCanceledException){GameEventLogStatus="游戏日志读取失败 · "+error.Message;}
        finally{if(query==gameLogQuery)GameEventLogBusy=false;}
    }
    public void SelectGameEvent(GameEventLiveRow? row)
    {
        if(SelectedGameEventDetail?.Id==row?.Id)return;
        SelectedGameEventDetail=row is null?null:GameEventDetails.From(row.Record);
    }
    private bool CanRefreshGameLog()=>!GameEventLogBusy;
    [RelayCommand(CanExecute=nameof(CanRefreshGameLog))]
    private async Task LatestGameLogAsync(){gameLogPage=0;gameLogPages.Clear();gameLogPages.Add(null);await RefreshGameEventsAsync();}
    private bool CanPreviousGameLogPage()=>!GameEventLogBusy&&gameLogPage>0;
    private bool CanNextGameLogPage()=>!GameEventLogBusy&&GameEventLogHasMore&&GameEventRows.Count>0;
    [RelayCommand(CanExecute=nameof(CanPreviousGameLogPage))]
    private async Task PreviousGameLogPageAsync(){gameLogPage--;await RefreshGameEventsAsync();}
    [RelayCommand(CanExecute=nameof(CanNextGameLogPage))]
    private async Task NextGameLogPageAsync()
    {
        var cursor=GameEventRows.Last().Record.Cursor;
        if(gameLogPages.Count>gameLogPage+1)gameLogPages[gameLogPage+1]=cursor;else gameLogPages.Add(cursor);
        gameLogPage++;await RefreshGameEventsAsync();
    }
}

public sealed record GameEventTypeOption(string Event,string Display);
public sealed record GameEventDetailField(string Label,string Value);
public sealed record GameEventDetails(long Id,string Name,string OriginalName,string Time,string Source,string Summary,IReadOnlyList<GameEventDetailField> Fields,string Json)
{
    public static GameEventDetails From(GameEventEntry entry)
    {
        using var document=JsonDocument.Parse(entry.Json);
        var fields=document.RootElement.EnumerateObject().Where(p=>p.Name is not("timestamp" or "event"))
            .Select(p=>new GameEventDetailField(GameEventNames.Field(p.Name),p.Value.ValueKind switch
            {
                JsonValueKind.Array=>$"{p.Value.GetArrayLength():N0} 项",
                JsonValueKind.Object=>$"{p.Value.EnumerateObject().Count():N0} 个字段",
                JsonValueKind.True=>"是",JsonValueKind.False=>"否",JsonValueKind.Null=>"—",_=>p.Value.ToString()
            })).ToArray();
        return new(entry.Id,entry.Name,entry.Event,entry.Time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss zzz"),
            Path.GetFileName(entry.Source),entry.Summary,fields,
            JsonSerializer.Serialize(document.RootElement,new JsonSerializerOptions{WriteIndented=true}));
    }
}