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

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using XFEToolBox.Client.Models;
using XFEToolBox.Client.Profiles.CrossVersionProfiles;

namespace XFEToolBox.Client.Utilities;

public static class PinnedItemService
{
    public const int MaximumPinnedItems = 8;
    private static readonly object SyncRoot = new();
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
    private static readonly List<PinnedItemEntry> Items = [];
    private static bool isLoaded;

    public static event EventHandler? Changed;

    public static IReadOnlyList<PinnedItemEntry> GetPinnedItems()
    {
        EnsureLoaded();
        lock (SyncRoot)
            return Items.Select(Clone).ToArray();
    }

    public static bool IsPinned(LauncherItemKind kind, string targetId)
    {
        EnsureLoaded();
        lock (SyncRoot)
            return Items.Any(item => IsSame(item, kind, targetId));
    }

    public static bool TryPin(LauncherItemKind kind, string targetId)
    {
        if (string.IsNullOrWhiteSpace(targetId)) return false;
        EnsureLoaded();
        lock (SyncRoot)
        {
            if (Items.Any(item => IsSame(item, kind, targetId))) return true;
            if (Items.Count >= MaximumPinnedItems) return false;
            Items.Add(new PinnedItemEntry
            {
                Kind = kind,
                TargetId = targetId.Trim(),
                AddedAtUtc = DateTimeOffset.UtcNow
            });
            SaveLocked();
        }

        Changed?.Invoke(null, EventArgs.Empty);
        return true;
    }

    public static bool Unpin(LauncherItemKind kind, string targetId)
    {
        EnsureLoaded();
        bool changed;
        lock (SyncRoot)
        {
            changed = Items.RemoveAll(item => IsSame(item, kind, targetId)) > 0;
            if (changed) SaveLocked();
        }

        if (changed) Changed?.Invoke(null, EventArgs.Empty);
        return changed;
    }

    public static bool Toggle(LauncherItemKind kind, string targetId) =>
        IsPinned(kind, targetId) ? Unpin(kind, targetId) : TryPin(kind, targetId);

    public static bool Move(LauncherItemKind kind, string targetId, int offset)
    {
        EnsureLoaded();
        lock (SyncRoot)
        {
            var index = Items.FindIndex(item => IsSame(item, kind, targetId));
            var destination = index + offset;
            if (index < 0 || destination < 0 || destination >= Items.Count) return false;
            var entry = Items[index];
            Items.RemoveAt(index);
            Items.Insert(destination, entry);
            SaveLocked();
        }

        Changed?.Invoke(null, EventArgs.Empty);
        return true;
    }

    public static string CreateKey(LauncherItemKind kind, string targetId) =>
        $"{kind}:{targetId.Trim()}";

    private static void EnsureLoaded()
    {
        lock (SyncRoot)
        {
            if (isLoaded) return;
            isLoaded = true;
            if (string.IsNullOrWhiteSpace(SystemProfile.PinnedItemsJson)) return;
            Items.AddRange(ParseEntries(SystemProfile.PinnedItemsJson));
        }
    }

    internal static IReadOnlyList<PinnedItemEntry> ParseEntries(string? json)
    {
        if (string.IsNullOrWhiteSpace(json)) return [];
        try
        {
            var result = new List<PinnedItemEntry>();
            var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (var item in JsonSerializer.Deserialize<PinnedItemEntry?[]>(json, JsonOptions) ?? [])
            {
                if (item is null) continue;
                if (!Enum.IsDefined(item.Kind) || string.IsNullOrWhiteSpace(item.TargetId)) continue;
                item.TargetId = item.TargetId.Trim();
                if (keys.Add(CreateKey(item.Kind, item.TargetId))) result.Add(Clone(item));
                if (result.Count == MaximumPinnedItems) break;
            }

            return result;
        }
        catch (JsonException)
        {
            return [];
        }
    }

    private static void SaveLocked()
    {
        SystemProfile.PinnedItemsJson = JsonSerializer.Serialize(Items, JsonOptions);
        SystemProfile.SaveProfile();
    }

    private static bool IsSame(PinnedItemEntry item, LauncherItemKind kind, string targetId) =>
        item.Kind == kind && string.Equals(item.TargetId, targetId, StringComparison.OrdinalIgnoreCase);

    private static PinnedItemEntry Clone(PinnedItemEntry item) => new()
    {
        Kind = item.Kind,
        TargetId = item.TargetId,
        AddedAtUtc = item.AddedAtUtc
    };
}