Files
LightlessClient/LightlessSync/UI/Services/PairUiService.cs
2025-11-25 07:14:59 +09:00

229 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using LightlessSync.API.Dto.Group;
using LightlessSync.PlayerData.Factories;
using LightlessSync.PlayerData.Pairs;
using LightlessSync.Services.Mediator;
using LightlessSync.UI.Models;
using Microsoft.Extensions.Logging;
namespace LightlessSync.UI.Services;
public sealed class PairUiService : DisposableMediatorSubscriberBase
{
private readonly PairLedger _pairLedger;
private readonly PairFactory _pairFactory;
private readonly PairManager _pairManager;
private readonly object _snapshotGate = new();
private PairUiSnapshot _snapshot = PairUiSnapshot.Empty;
private Pair? _lastAddedPair;
private bool _needsRefresh = true;
public PairUiService(
ILogger<PairUiService> logger,
LightlessMediator mediator,
PairLedger pairLedger,
PairFactory pairFactory,
PairManager pairManager) : base(logger, mediator)
{
_pairLedger = pairLedger;
_pairFactory = pairFactory;
_pairManager = pairManager;
Mediator.Subscribe<PairDataChangedMessage>(this, _ => MarkDirty());
Mediator.Subscribe<GroupCollectionChangedMessage>(this, _ => MarkDirty());
Mediator.Subscribe<VisibilityChange>(this, _ => MarkDirty());
EnsureSnapshot();
}
public PairUiSnapshot GetSnapshot()
{
EnsureSnapshot();
lock (_snapshotGate)
{
return _snapshot;
}
}
public Pair? GetLastAddedPair()
{
EnsureSnapshot();
lock (_snapshotGate)
{
return _lastAddedPair;
}
}
public void ClearLastAddedPair()
{
_pairManager.ClearLastAddedUser();
lock (_snapshotGate)
{
_lastAddedPair = null;
}
}
private void MarkDirty()
{
lock (_snapshotGate)
{
_needsRefresh = true;
}
}
private void EnsureSnapshot()
{
bool shouldBuild;
lock (_snapshotGate)
{
shouldBuild = _needsRefresh;
if (shouldBuild)
{
_needsRefresh = false;
}
}
if (!shouldBuild)
{
return;
}
PairUiSnapshot snapshot;
Pair? lastAddedPair;
try
{
(snapshot, lastAddedPair) = BuildSnapshot();
}
catch
{
lock (_snapshotGate)
{
_needsRefresh = true;
}
throw;
}
lock (_snapshotGate)
{
_snapshot = snapshot;
_lastAddedPair = lastAddedPair;
}
Mediator.Publish(new PairUiUpdatedMessage(snapshot));
}
private (PairUiSnapshot Snapshot, Pair? LastAddedPair) BuildSnapshot()
{
var entries = _pairLedger.GetAllEntries();
var pairByUid = new Dictionary<string, Pair>(StringComparer.Ordinal);
var directPairsList = new List<Pair>();
var groupPairsTemp = new Dictionary<GroupFullInfoDto, List<Pair>>();
var pairsWithGroupsTemp = new Dictionary<Pair, List<GroupFullInfoDto>>();
foreach (var entry in entries)
{
var pair = _pairFactory.Create(entry);
if (pair is null)
{
continue;
}
pairByUid[entry.Ident.UserId] = pair;
if (entry.IsDirectlyPaired)
{
directPairsList.Add(pair);
}
var uniqueGroups = new HashSet<string>(StringComparer.Ordinal);
var groupList = new List<GroupFullInfoDto>();
foreach (var group in entry.Groups)
{
if (!uniqueGroups.Add(group.Group.GID))
{
continue;
}
if (!groupPairsTemp.TryGetValue(group, out var members))
{
members = new List<Pair>();
groupPairsTemp[group] = members;
}
members.Add(pair);
groupList.Add(group);
}
pairsWithGroupsTemp[pair] = groupList;
}
var allGroupsList = _pairLedger.GetAllSyncshells()
.Values
.Select(s => s.GroupFullInfo)
.ToList();
foreach (var group in allGroupsList)
{
if (!groupPairsTemp.ContainsKey(group))
{
groupPairsTemp[group] = new List<Pair>();
}
}
var directPairs = new ReadOnlyCollection<Pair>(directPairsList);
var groupPairsFinal = new Dictionary<GroupFullInfoDto, IReadOnlyList<Pair>>();
foreach (var (group, members) in groupPairsTemp)
{
groupPairsFinal[group] = new ReadOnlyCollection<Pair>(members);
}
var pairsWithGroupsFinal = new Dictionary<Pair, IReadOnlyList<GroupFullInfoDto>>();
foreach (var (pair, groups) in pairsWithGroupsTemp)
{
pairsWithGroupsFinal[pair] = new ReadOnlyCollection<GroupFullInfoDto>(groups);
}
var groupsReadOnly = new ReadOnlyCollection<GroupFullInfoDto>(allGroupsList);
var pairsByUidReadOnly = new ReadOnlyDictionary<string, Pair>(pairByUid);
var groupsByGidReadOnly = new ReadOnlyDictionary<string, GroupFullInfoDto>(allGroupsList.ToDictionary(g => g.Group.GID, g => g, StringComparer.Ordinal));
Pair? lastAddedPair = null;
var lastAdded = _pairManager.GetLastAddedUser();
if (lastAdded is not null)
{
if (!pairByUid.TryGetValue(lastAdded.User.UID, out lastAddedPair))
{
var groups = lastAdded.Groups.Keys
.Select(gid =>
{
var result = _pairManager.GetGroup(gid);
return result.Success ? result.Value.GroupFullInfo : null;
})
.Where(g => g is not null)
.Cast<GroupFullInfoDto>()
.ToList();
var entry = new PairDisplayEntry(new PairUniqueIdentifier(lastAdded.User.UID), lastAdded, groups, null);
lastAddedPair = _pairFactory.Create(entry);
}
}
var snapshot = new PairUiSnapshot(
pairsByUidReadOnly,
directPairs,
new ReadOnlyDictionary<GroupFullInfoDto, IReadOnlyList<Pair>>(groupPairsFinal),
new ReadOnlyDictionary<Pair, IReadOnlyList<GroupFullInfoDto>>(pairsWithGroupsFinal),
groupsByGidReadOnly,
groupsReadOnly);
return (snapshot, lastAddedPair);
}
}