Files
LightlessClient/LightlessSync/UI/Services/PairUiService.cs
defnotken 835a0a637d
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m27s
2.0.0 (#92)
2.0.0 Changes:

- Reworked shell finder UI with compact or list view with profile tags showing with the listing, allowing moderators to broadcast the syncshell as well to have it be used more.
- Reworked user list in syncshell admin screen to have filter visible and moved away from table to its own thing, allowing to copy uid/note/alias when clicking on the name.
- Reworked download bars and download box to make it look more modern, removed the jitter around, so it shouldn't vibrate around much.
- Chat has been added to the top menu, working in Zone or in Syncshells to be used there.
- Paired system has been revamped to make pausing and unpausing faster, and loading people should be faster as well.
- Moved to the internal object table to have faster load times for users; people should load in faster
- Compactor is running on a multi-threaded level instead of single-threaded; this should increase the speed of compacting files
- Nameplate Service has been reworked so it wouldn't use the nameplate handler anymore.
- Files can be resized when downloading to reduce load on users if they aren't compressed. (can be toggled to resize all).
- Penumbra Collections are now only made when people are visible, reducing the load on boot-up when having many syncshells in your list.
- Lightfinder plates have been moved away from using Nameplates, but will use an overlay.
- Main UI has been changed a bit with a gradient, and on hover will glow up now.
- Reworked Profile UI for Syncshell and Users to be more user-facing with more customizable items.
- Reworked Settings UI to look more modern.
- Performance should be better due to new systems that would dispose of the collections and better caching of items.

Co-authored-by: defnotken <itsdefnotken@gmail.com>
Co-authored-by: azyges <aaaaaa@aaa.aaa>
Co-authored-by: choco <choco@patat.nl>
Co-authored-by: cake <admin@cakeandbanana.nl>
Co-authored-by: Minmoose <KennethBohr@outlook.com>
Reviewed-on: #92
2025-12-21 17:19:34 +00:00

226 lines
6.5 KiB
C#

using System.Collections.ObjectModel;
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);
}
}