Merge branch '2.0.0' of https://git.lightless-sync.org/Lightless-Sync/LightlessClient into 2.0.0
This commit is contained in:
@@ -1,11 +1,80 @@
|
||||
tagline: "Lightless Sync v1.12.4"
|
||||
subline: "Bugfixes and various improvements across Lightless"
|
||||
tagline: "Lightless Sync v2.0.0"
|
||||
subline: "LIGHTLESS IS EVOLVING!!"
|
||||
changelog:
|
||||
- name: "v2.0.0"
|
||||
tagline: "Thank you for 4 months!"
|
||||
date: "December 2025"
|
||||
# be sure to set this every new version
|
||||
isCurrent: true
|
||||
versions:
|
||||
- number: "Lightless Chat"
|
||||
icon: ""
|
||||
items:
|
||||
- "Chat has been added to the top of the main UI. It will work in certain Zones or in Syncshells!"
|
||||
- "You will only be able to use the chat feature after enabling it and accepting the rules. If you're not interested, don't use it!"
|
||||
- "Breaking the rules may result in a mute or ban from chat. Serious offenses may result in a ban from the Lightless service altogether."
|
||||
- "You can right click the offender in the chat and report them within the chat, reports will be reviewed asap."
|
||||
- "Syncshells can enforce their own chat rules and moderate their own chat. This however does not apply to serious offenses."
|
||||
- "Your name in chat will not be shown unless you are paired with the person OR you are in the same syncshell. Otherwise, you will be anonymous."
|
||||
- "Refer to #release-notes in the Discord for more information. Feel free to ask questions in the Discord as well."
|
||||
- number: "Changes to LightFinder"
|
||||
icon: ""
|
||||
items:
|
||||
- "We have recieve quite a bit of reports of users crashing due to how Nameplates are handled across various plugins. As a result, we have moved the LightFinder icon and text to Imgui."
|
||||
- "This should resolve the crashing issues, however, it may not look as nice as before. We are looking into ways to improve the Imgui experience in the future."
|
||||
- "We will always prioritize stability and safety over visuals."
|
||||
- "Refer to #release-notes in the Discord for an example of the error."
|
||||
- number: "User Profiles, ShellFinder, Syncshells, Syncshell Profiles"
|
||||
icon: ""
|
||||
items:
|
||||
- "Both User Profiles and Syncshell Profiles have been revamped for 2.0.0."
|
||||
- "We have added profile tags to both Users and Syncshells that will show when a profile is being viewed"
|
||||
- "Syncshell Admin Panel has been reworked to make it a friendlier experience"
|
||||
- "Syncshell Moderators can now also broadcast on ShellFinder"
|
||||
- "ShellFinder has been revamped to be more visually friends and also show more information (Tags) about the Syncshell"
|
||||
- "Syncshells has an auto-prune feature now that will remove inactive members after a set amount of time, options available are 1, 3, 7, and 14 days that runs in 1 hour intervals"
|
||||
- "IF YOUR SYNCSHELL IS NSFW, PLEASE MARK IT AS NSFW!"
|
||||
- "Refer to #release-notes in the Discord for pretty pictures or try it yourself!."
|
||||
- number: "Texture Optimization"
|
||||
icon: ""
|
||||
items:
|
||||
- "In 2.0.0, we've added the option for Texture Optimization to improve the performance of scenarios such as overwhelmingly big "
|
||||
- "NOTE: ALL OF THESE ARE OPTIONAL AND DISABLED BY DEFAULT"
|
||||
- "Within Texture Optimization, you will be able to safely downscale all textures of new downloads around you."
|
||||
- "This downscale DOES NOT APPLY to DIRECT PAIRS or those who've updated their preferred settings to not be downscaled"
|
||||
- "The first time this is enabled, you may experience some lag or frame drops, but in the long run, it will help performance."
|
||||
- "This can be found in Lightless Settings > Performance > Texture Optimization"
|
||||
- "Like a broken record, please refer to #release-notes in the Discord for more information."
|
||||
- number: "Character Analysis - The big scary UI no one knew about"
|
||||
icon: ""
|
||||
items:
|
||||
- "We have made the Character Analysis UI more user friendly. This includes a revamp of the look and functionality"
|
||||
- "You can now see more information about your character and how it affects performance"
|
||||
- "It will show you the Textures tab by default with an option for \"Other file types\""
|
||||
- "You can now choose if you want to BC7/BC5/BC4/BC3/BC1 compress a certain texture."
|
||||
- "The UI will give you a recommendation on what BC compression to use based on the file."
|
||||
- "Shows a small preview of what the texture looks like with some general info about it."
|
||||
- "Shows you how much VRAM you would take up."
|
||||
- "This can be found in Lightless Settings > Performance > Character Analysis"
|
||||
- number: "Performance"
|
||||
icon: ""
|
||||
items:
|
||||
- "Moved to the internal object table to have improved overall plugin performance."
|
||||
- "Compactor is now running on a multi-threaded level instead of single-threaded; This should increase the speed of compacting files."
|
||||
- "Penumbra Collections are now only made when people are visible, reducing the load on boot-up when having many Syncshells in your list."
|
||||
- "Pairing system has been revamped to make pausing and unpausing faster, and loading people should be faster as well."
|
||||
- number: "Miscellaneous Changes and Bugfixes"
|
||||
icon: ""
|
||||
items:
|
||||
- "UI has been updated to look more modern"
|
||||
- "We have started on file compression for Linux with the option for BTRFS or ZFS but it's not very great yet and will release later."
|
||||
- "Nameplate colours now use sigs to client structs as an alternative to the Nameplate Handler, also preventing crashes on that from our end."
|
||||
- "Notifications now work with the \"Enable multi-monitor windows\" settings of Dalamud."
|
||||
- "Fixed a bug where nothing above the notifications was clickable in certain cases."
|
||||
- "Added a check that prevents small messages from going below 0 resulting in an ArgumentOutOfRangeException."
|
||||
- name: "v1.12.4"
|
||||
tagline: "Preparation for future features"
|
||||
date: "November 11th 2025"
|
||||
# be sure to set this every new version
|
||||
isCurrent: true
|
||||
versions:
|
||||
- number: "Syncshells"
|
||||
icon: ""
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors></Authors>
|
||||
<Company></Company>
|
||||
<Version>1.12.4</Version>
|
||||
<Version>2.0.0</Version>
|
||||
<Description></Description>
|
||||
<Copyright></Copyright>
|
||||
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
||||
|
||||
@@ -467,7 +467,8 @@ public sealed class Plugin : IDalamudPlugin
|
||||
sp.GetRequiredService<ApiController>(),
|
||||
sp.GetRequiredService<LightFinderScannerService>(),
|
||||
sp.GetRequiredService<PairUiService>(),
|
||||
sp.GetRequiredService<DalamudUtilService>()));
|
||||
sp.GetRequiredService<DalamudUtilService>(),
|
||||
sp.GetRequiredService<LightlessProfileManager>()));
|
||||
|
||||
services.AddScoped<IPopupHandler, BanUserPopupHandler>();
|
||||
services.AddScoped<IPopupHandler, CensusPopupHandler>();
|
||||
|
||||
@@ -12,11 +12,11 @@ namespace LightlessSync.Services.Chat;
|
||||
public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedService
|
||||
{
|
||||
private const int MaxMessageHistory = 150;
|
||||
internal const int MaxOutgoingLength = 400;
|
||||
internal const int MaxOutgoingLength = 200;
|
||||
private const int MaxUnreadCount = 999;
|
||||
private const string ZoneUnavailableMessage = "Zone chat is only available in major cities.";
|
||||
private const string ZoneChannelKey = "zone";
|
||||
private const int MaxReportReasonLength = 500;
|
||||
private const int MaxReportReasonLength = 100;
|
||||
private const int MaxReportContextLength = 1000;
|
||||
|
||||
private readonly ApiController _apiController;
|
||||
|
||||
@@ -218,7 +218,7 @@ internal class ContextMenuService : IHostedService
|
||||
return;
|
||||
}
|
||||
|
||||
var senderCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetBlake3Hash();
|
||||
var senderCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
|
||||
var receiverCid = DalamudUtilService.GetHashedCIDFromPlayerPointer(targetData.Address);
|
||||
|
||||
_logger.LogInformation("Sending pair request: sender {SenderCid}, receiver {ReceiverCid}", senderCid, receiverCid);
|
||||
|
||||
@@ -560,9 +560,12 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
foreach (var p in perPlayer)
|
||||
{
|
||||
boxHeight += lineHeight + spacingY;
|
||||
boxHeight += lineHeight + spacingY;
|
||||
|
||||
if (_configService.Current.ShowPlayerSpeedBarsTransferWindow && p.DlProg > 0)
|
||||
var showBar = _configService.Current.ShowPlayerSpeedBarsTransferWindow
|
||||
&& p.TransferredBytes > 0;
|
||||
|
||||
if (showBar)
|
||||
{
|
||||
boxHeight += perPlayerBarHeight + spacingY;
|
||||
}
|
||||
@@ -630,46 +633,23 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
);
|
||||
cursor.Y += lineHeight * 1.4f + spacingY;
|
||||
|
||||
if (_configService.Current.ShowPlayerLinesTransferWindow)
|
||||
var orderedPlayers = perPlayer.OrderByDescending(p => p.TotalBytes).ToList();
|
||||
|
||||
foreach (var p in orderedPlayers)
|
||||
{
|
||||
var orderedPlayers = perPlayer.OrderByDescending(p => p.TotalBytes).ToList();
|
||||
var hasSpeed = p.SpeedBytesPerSecond > 0;
|
||||
var playerSpeedText = hasSpeed
|
||||
? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s"
|
||||
: "-";
|
||||
|
||||
foreach (var p in orderedPlayers)
|
||||
var showBar = _configService.Current.ShowPlayerSpeedBarsTransferWindow
|
||||
&& p.TransferredBytes > 0;
|
||||
|
||||
var labelLine =
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
|
||||
if (!showBar)
|
||||
{
|
||||
var hasSpeed = p.SpeedBytesPerSecond > 0;
|
||||
var playerSpeedText = hasSpeed
|
||||
? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s"
|
||||
: "-";
|
||||
|
||||
// Label line for the player
|
||||
var labelLine =
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
|
||||
// State flags
|
||||
var isDownloading = p.DlProg > 0;
|
||||
var isDecompressing = p.DlDecomp > 0
|
||||
|| (!isDownloading && p.TotalBytes > 0 && p.TransferredBytes >= p.TotalBytes);
|
||||
|
||||
|
||||
var showBar = _configService.Current.ShowPlayerSpeedBarsTransferWindow
|
||||
&& (isDownloading || isDecompressing);
|
||||
|
||||
if (!showBar)
|
||||
{
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
labelLine,
|
||||
cursor,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Top label line (only name + W/Q/P/D + files)
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
labelLine,
|
||||
@@ -678,82 +658,90 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
|
||||
// Bar background
|
||||
var barBgMin = new Vector2(boxMin.X + padding, cursor.Y);
|
||||
var barBgMax = new Vector2(boxMax.X - padding, cursor.Y + perPlayerBarHeight);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
barBgMin,
|
||||
barBgMax,
|
||||
UiSharedService.Color(40, 40, 40, _transferBoxTransparency),
|
||||
3f
|
||||
);
|
||||
|
||||
float ratio = 0f;
|
||||
if (isDownloading && p.TotalBytes > 0)
|
||||
{
|
||||
ratio = (float)p.TransferredBytes / p.TotalBytes;
|
||||
}
|
||||
else if (isDecompressing)
|
||||
{
|
||||
ratio = 1f;
|
||||
}
|
||||
|
||||
if (ratio < 0f) ratio = 0f;
|
||||
if (ratio > 1f) ratio = 1f;
|
||||
|
||||
var fillX = barBgMin.X + (barBgMax.X - barBgMin.X) * ratio;
|
||||
var barFillMax = new Vector2(fillX, barBgMax.Y);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
barBgMin,
|
||||
barFillMax,
|
||||
UiSharedService.Color(UIColors.Get("LightlessPurple")),
|
||||
3f
|
||||
);
|
||||
|
||||
string barText;
|
||||
|
||||
if (isDownloading)
|
||||
{
|
||||
var bytesInside =
|
||||
$"{UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}";
|
||||
|
||||
barText = hasSpeed
|
||||
? $"{bytesInside} @ {playerSpeedText}"
|
||||
: bytesInside;
|
||||
}
|
||||
else if (isDecompressing)
|
||||
{
|
||||
barText = "Decompressing...";
|
||||
}
|
||||
else
|
||||
{
|
||||
barText = string.Empty;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(barText))
|
||||
{
|
||||
var barTextSize = ImGui.CalcTextSize(barText);
|
||||
var barTextPos = new Vector2(
|
||||
barBgMin.X + ((barBgMax.X - barBgMin.X) - barTextSize.X) / 2f - 1,
|
||||
barBgMin.Y + ((perPlayerBarHeight - barTextSize.Y) / 2f) - 1
|
||||
);
|
||||
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
barText,
|
||||
barTextPos,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
cursor.Y += perPlayerBarHeight + spacingY;
|
||||
continue;
|
||||
}
|
||||
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
labelLine,
|
||||
cursor,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
|
||||
// Bar background
|
||||
var barBgMin = new Vector2(boxMin.X + padding, cursor.Y);
|
||||
var barBgMax = new Vector2(boxMax.X - padding, cursor.Y + perPlayerBarHeight);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
barBgMin,
|
||||
barBgMax,
|
||||
UiSharedService.Color(40, 40, 40, _transferBoxTransparency),
|
||||
3f
|
||||
);
|
||||
|
||||
// Fill based on Progress of download
|
||||
float ratio = 0f;
|
||||
if (p.TotalBytes > 0)
|
||||
ratio = (float)p.TransferredBytes / p.TotalBytes;
|
||||
|
||||
if (ratio < 0f) ratio = 0f;
|
||||
if (ratio > 1f) ratio = 1f;
|
||||
|
||||
var fillX = barBgMin.X + (barBgMax.X - barBgMin.X) * ratio;
|
||||
var barFillMax = new Vector2(fillX, barBgMax.Y);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
barBgMin,
|
||||
barFillMax,
|
||||
UiSharedService.Color(UIColors.Get("LightlessPurple")),
|
||||
3f
|
||||
);
|
||||
|
||||
// Text inside bar: downloading vs decompressing
|
||||
string barText;
|
||||
|
||||
var isDecompressing = p.DlDecomp > 0 && p.TransferredBytes >= p.TotalBytes && p.TotalBytes > 0;
|
||||
|
||||
if (isDecompressing)
|
||||
{
|
||||
// Keep bar full, static text showing decompressing
|
||||
barText = "Decompressing...";
|
||||
}
|
||||
else
|
||||
{
|
||||
var bytesInside =
|
||||
$"{UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}";
|
||||
|
||||
barText = hasSpeed
|
||||
? $"{bytesInside} @ {playerSpeedText}"
|
||||
: bytesInside;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(barText))
|
||||
{
|
||||
var barTextSize = ImGui.CalcTextSize(barText);
|
||||
|
||||
var barTextPos = new Vector2(
|
||||
barBgMin.X + ((barBgMax.X - barBgMin.X) - barTextSize.X) / 2f - 1,
|
||||
barBgMin.Y + ((perPlayerBarHeight - barTextSize.Y) / 2f) - 1
|
||||
);
|
||||
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
barText,
|
||||
barTextPos,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
cursor.Y += perPlayerBarHeight + spacingY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Utility;
|
||||
using LightlessSync.API.Data.Extensions;
|
||||
using LightlessSync.API.Dto.Group;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Plugin.Services;
|
||||
using LightlessSync.API.Data;
|
||||
using LightlessSync.API.Data.Enum;
|
||||
using LightlessSync.API.Data.Extensions;
|
||||
using LightlessSync.API.Dto;
|
||||
using LightlessSync.API.Dto.Group;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.LightFinder;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.UI.Services;
|
||||
using LightlessSync.UI.Tags;
|
||||
using LightlessSync.Utils;
|
||||
using LightlessSync.WebAPI;
|
||||
using LightlessSync.UI.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Numerics;
|
||||
using LightlessSync.Services.LightFinder;
|
||||
|
||||
namespace LightlessSync.UI;
|
||||
|
||||
@@ -28,6 +31,10 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
private readonly PairUiService _pairUiService;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
|
||||
private Vector4 _tagBackgroundColor = new(0.18f, 0.18f, 0.18f, 0.95f);
|
||||
private Vector4 _tagBorderColor = new(0.35f, 0.35f, 0.35f, 0.4f);
|
||||
|
||||
private readonly List<SeStringUtils.SeStringSegment> _seResolvedSegments = new();
|
||||
private readonly List<GroupJoinDto> _nearbySyncshells = [];
|
||||
private List<GroupFullInfoDto> _currentSyncshells = [];
|
||||
private int _selectedNearbyIndex = -1;
|
||||
@@ -40,6 +47,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
private bool _useTestSyncshells = false;
|
||||
|
||||
private bool _compactView = false;
|
||||
private readonly LightlessProfileManager _lightlessProfileManager;
|
||||
|
||||
public SyncshellFinderUI(
|
||||
ILogger<SyncshellFinderUI> logger,
|
||||
@@ -50,7 +58,8 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
ApiController apiController,
|
||||
LightFinderScannerService broadcastScannerService,
|
||||
PairUiService pairUiService,
|
||||
DalamudUtilService dalamudUtilService) : base(logger, mediator, "Shellfinder###LightlessSyncshellFinderUI", performanceCollectorService)
|
||||
DalamudUtilService dalamudUtilService,
|
||||
LightlessProfileManager lightlessProfileManager) : base(logger, mediator, "Shellfinder###LightlessSyncshellFinderUI", performanceCollectorService)
|
||||
{
|
||||
_broadcastService = broadcastService;
|
||||
_uiSharedService = uiShared;
|
||||
@@ -58,11 +67,12 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
_broadcastScannerService = broadcastScannerService;
|
||||
_pairUiService = pairUiService;
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
_lightlessProfileManager = lightlessProfileManager;
|
||||
|
||||
IsOpen = false;
|
||||
WindowBuilder.For(this)
|
||||
.SetSizeConstraints(new Vector2(600, 400), new Vector2(600, 550))
|
||||
.Apply();
|
||||
.SetSizeConstraints(new Vector2(600, 400), new Vector2(600, 550))
|
||||
.Apply();
|
||||
|
||||
Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
|
||||
Mediator.Subscribe<BroadcastStatusChangedMessage>(this, async _ => await RefreshSyncshellsAsync().ConfigureAwait(false));
|
||||
@@ -80,7 +90,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
{
|
||||
ImGui.BeginGroup();
|
||||
_uiSharedService.MediumText("Nearby Syncshells", UIColors.Get("LightlessPurple"));
|
||||
|
||||
|
||||
#if DEBUG
|
||||
if (ImGui.SmallButton("Show test syncshells"))
|
||||
{
|
||||
@@ -92,7 +102,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
|
||||
string checkboxLabel = "Compact view";
|
||||
float availWidth = ImGui.GetContentRegionAvail().X;
|
||||
float checkboxWidth = ImGui.CalcTextSize(checkboxLabel).X + ImGui.GetFrameHeight();
|
||||
float checkboxWidth = ImGui.CalcTextSize(checkboxLabel).X + ImGui.GetFrameHeight();
|
||||
|
||||
float rightX = ImGui.GetCursorPosX() + availWidth - checkboxWidth - 4.0f;
|
||||
ImGui.SetCursorPosX(rightX);
|
||||
@@ -130,20 +140,34 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
return;
|
||||
}
|
||||
|
||||
string? myHashedCid = null;
|
||||
try
|
||||
{
|
||||
var cid = _dalamudUtilService.GetCID();
|
||||
myHashedCid = cid.ToString().GetHash256();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to get CID, not excluding own broadcast.");
|
||||
}
|
||||
var broadcasts = _broadcastScannerService.GetActiveSyncshellBroadcasts().Where(b => !string.Equals(b.HashedCID, myHashedCid, StringComparison.Ordinal)).ToList() ?? [];
|
||||
|
||||
var cardData = new List<(GroupJoinDto Shell, string BroadcasterName)>();
|
||||
var broadcasts = _broadcastScannerService.GetActiveSyncshellBroadcasts();
|
||||
|
||||
foreach (var shell in _nearbySyncshells)
|
||||
{
|
||||
string broadcasterName;
|
||||
|
||||
if (shell?.Group == null || string.IsNullOrEmpty(shell.Group.GID))
|
||||
continue;
|
||||
|
||||
if (_useTestSyncshells)
|
||||
{
|
||||
var displayName = !string.IsNullOrEmpty(shell.Group.Alias)
|
||||
? shell.Group.Alias
|
||||
: shell.Group.GID;
|
||||
|
||||
broadcasterName = $"Tester of {displayName}";
|
||||
broadcasterName = $"{displayName} (Tester of TestWorld)";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -206,7 +230,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
var (shell, broadcasterName) = listData[index];
|
||||
|
||||
ImGui.PushID(shell.Group.GID);
|
||||
float rowHeight = 90f * ImGuiHelpers.GlobalScale;
|
||||
float rowHeight = 74f * ImGuiHelpers.GlobalScale;
|
||||
|
||||
ImGui.BeginChild($"ShellRow##{shell.Group.GID}", new Vector2(-1, rowHeight), border: true);
|
||||
|
||||
@@ -234,10 +258,48 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));
|
||||
|
||||
ImGui.Dummy(new Vector2(0, 6 * ImGuiHelpers.GlobalScale));
|
||||
var groupProfile = _lightlessProfileManager.GetLightlessGroupProfile(shell.Group);
|
||||
|
||||
IReadOnlyList<ProfileTagDefinition> groupTags =
|
||||
groupProfile != null && groupProfile.Tags.Count > 0
|
||||
? ProfileTagService.ResolveTags(groupProfile.Tags)
|
||||
: [];
|
||||
|
||||
var limitedTags = groupTags.Count > 3
|
||||
? [.. groupTags.Take(3)]
|
||||
: groupTags;
|
||||
|
||||
float tagScale = ImGuiHelpers.GlobalScale * 0.9f;
|
||||
|
||||
Vector2 rowStartLocal = ImGui.GetCursorPos();
|
||||
|
||||
float tagsWidth = 0f;
|
||||
float tagsHeight = 0f;
|
||||
|
||||
if (limitedTags.Count > 0)
|
||||
{
|
||||
(tagsWidth, tagsHeight) = RenderProfileTagsSingleRow(limitedTags, tagScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.SetCursorPosX(startX);
|
||||
ImGui.TextDisabled("-- No tags set --");
|
||||
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
|
||||
}
|
||||
|
||||
float btnBaselineY = rowStartLocal.Y;
|
||||
float joinX = rowStartLocal.X + (tagsWidth > 0 ? tagsWidth + style.ItemSpacing.X : 0f);
|
||||
|
||||
ImGui.SetCursorPos(new Vector2(joinX, btnBaselineY));
|
||||
DrawJoinButton(shell);
|
||||
|
||||
float btnHeight = ImGui.GetFrameHeightWithSpacing();
|
||||
float rowHeightUsed = MathF.Max(tagsHeight, btnHeight);
|
||||
|
||||
ImGui.SetCursorPos(new Vector2(
|
||||
rowStartLocal.X,
|
||||
rowStartLocal.Y + rowHeightUsed));
|
||||
|
||||
ImGui.EndChild();
|
||||
ImGui.PopID();
|
||||
|
||||
@@ -311,10 +373,39 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
ImGui.SetTooltip("Broadcaster of the syncshell.");
|
||||
|
||||
ImGui.EndGroup();
|
||||
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));
|
||||
|
||||
ImGui.Dummy(new Vector2(0, 6 * ImGuiHelpers.GlobalScale));
|
||||
|
||||
var groupProfile = _lightlessProfileManager.GetLightlessGroupProfile(shell.Group);
|
||||
|
||||
IReadOnlyList<ProfileTagDefinition> groupTags =
|
||||
groupProfile != null && groupProfile.Tags.Count > 0
|
||||
? ProfileTagService.ResolveTags(groupProfile.Tags)
|
||||
: [];
|
||||
|
||||
float tagScale = ImGuiHelpers.GlobalScale * 0.9f;
|
||||
|
||||
if (groupTags.Count > 0)
|
||||
{
|
||||
var limitedTags = groupTags.Count > 2
|
||||
? [.. groupTags.Take(2)]
|
||||
: groupTags;
|
||||
|
||||
ImGui.SetCursorPosX(startX);
|
||||
|
||||
var (_, tagsHeight) = RenderProfileTagsSingleRow(limitedTags, tagScale);
|
||||
|
||||
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.SetCursorPosX(startX);
|
||||
ImGui.TextDisabled("-- No tags set --");
|
||||
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
|
||||
}
|
||||
|
||||
var buttonHeight = ImGui.GetFrameHeightWithSpacing();
|
||||
var remainingY = ImGui.GetContentRegionAvail().Y - buttonHeight;
|
||||
if (remainingY > 0)
|
||||
@@ -338,7 +429,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
{
|
||||
if (totalPages > 1)
|
||||
{
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("PairBlue"));
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"));
|
||||
|
||||
var style = ImGui.GetStyle();
|
||||
string pageLabel = $"Page {_syncshellPageIndex + 1}/{totalPages}";
|
||||
@@ -371,10 +462,6 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
const string visibleLabel = "Join";
|
||||
var label = $"{visibleLabel}##{shell.Group.GID}";
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessGreen"));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen").WithAlpha(0.85f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreen").WithAlpha(0.75f));
|
||||
|
||||
var isAlreadyMember = _currentSyncshells.Exists(g => string.Equals(g.GID, shell.GID, StringComparison.Ordinal));
|
||||
var isRecentlyJoined = _recentlyJoined.Contains(shell.GID);
|
||||
|
||||
@@ -386,7 +473,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
var textSize = ImGui.CalcTextSize(visibleLabel);
|
||||
|
||||
var width = textSize.X + style.FramePadding.X * 20f;
|
||||
buttonSize = new Vector2(width, 0);
|
||||
buttonSize = new Vector2(width, 30f);
|
||||
|
||||
float availX = ImGui.GetContentRegionAvail().X;
|
||||
float curX = ImGui.GetCursorPosX();
|
||||
@@ -400,6 +487,9 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
|
||||
if (!isAlreadyMember && !isRecentlyJoined)
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("LightlessGreen"));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen").WithAlpha(0.85f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreen").WithAlpha(0.75f));
|
||||
if (ImGui.Button(label, buttonSize))
|
||||
{
|
||||
_logger.LogInformation($"Join requested for Syncshell {shell.Group.GID} ({shell.Group.Alias})");
|
||||
@@ -436,6 +526,10 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("DimRed"));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("DimRed").WithAlpha(0.85f));
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("DimRed").WithAlpha(0.75f));
|
||||
|
||||
using (ImRaii.Disabled())
|
||||
{
|
||||
ImGui.Button(label, buttonSize);
|
||||
@@ -446,6 +540,72 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
|
||||
ImGui.PopStyleColor(3);
|
||||
}
|
||||
|
||||
private (float widthUsed, float rowHeight) RenderProfileTagsSingleRow(IReadOnlyList<ProfileTagDefinition> tags, float scale)
|
||||
{
|
||||
if (tags == null || tags.Count == 0)
|
||||
return (0f, 0f);
|
||||
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
var style = ImGui.GetStyle();
|
||||
var defaultTextColorU32 = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
|
||||
var baseLocal = ImGui.GetCursorPos();
|
||||
var baseScreen = ImGui.GetCursorScreenPos();
|
||||
float availableWidth = ImGui.GetContentRegionAvail().X;
|
||||
if (availableWidth <= 0f)
|
||||
availableWidth = 1f;
|
||||
|
||||
float cursorLocalX = baseLocal.X;
|
||||
float cursorScreenX = baseScreen.X;
|
||||
float rowHeight = 0f;
|
||||
|
||||
for (int i = 0; i < tags.Count; i++)
|
||||
{
|
||||
var tag = tags[i];
|
||||
if (!tag.HasContent)
|
||||
continue;
|
||||
|
||||
var tagSize = ProfileTagRenderer.MeasureTag(tag, scale, style, _tagBackgroundColor, _tagBorderColor, defaultTextColorU32, _seResolvedSegments, GetIconWrap, _logger);
|
||||
|
||||
float tagWidth = tagSize.X;
|
||||
float tagHeight = tagSize.Y;
|
||||
|
||||
if (cursorLocalX > baseLocal.X && cursorLocalX + tagWidth > baseLocal.X + availableWidth)
|
||||
break;
|
||||
|
||||
var tagScreenPos = new Vector2(cursorScreenX, baseScreen.Y);
|
||||
ImGui.SetCursorScreenPos(tagScreenPos);
|
||||
ImGui.InvisibleButton($"##profileTagInline_{i}", tagSize);
|
||||
|
||||
ProfileTagRenderer.RenderTag(tag, tagScreenPos, scale, drawList, style, _tagBackgroundColor, _tagBorderColor, defaultTextColorU32, _seResolvedSegments, GetIconWrap, _logger);
|
||||
|
||||
cursorLocalX += tagWidth + style.ItemSpacing.X;
|
||||
cursorScreenX += tagWidth + style.ItemSpacing.X;
|
||||
rowHeight = MathF.Max(rowHeight, tagHeight);
|
||||
}
|
||||
|
||||
ImGui.SetCursorPos(new Vector2(baseLocal.X, baseLocal.Y + rowHeight));
|
||||
|
||||
float widthUsed = cursorLocalX - baseLocal.X;
|
||||
return (widthUsed, rowHeight);
|
||||
}
|
||||
|
||||
private IDalamudTextureWrap? GetIconWrap(uint iconId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_uiSharedService.TryGetIcon(iconId, out var wrap) && wrap != null)
|
||||
return wrap;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to resolve icon {IconId} for profile tags", iconId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void DrawConfirmation()
|
||||
{
|
||||
if (_joinDto != null && _joinInfo != null)
|
||||
@@ -470,9 +630,9 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase
|
||||
finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX);
|
||||
|
||||
_ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions));
|
||||
|
||||
|
||||
_recentlyJoined.Add(_joinDto.Group.GID);
|
||||
|
||||
|
||||
_joinDto = null;
|
||||
_joinInfo = null;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
using System;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
using LightlessSync.API.Data.Enum;
|
||||
using LightlessSync.API.Data.Extensions;
|
||||
using LightlessSync.LightlessConfiguration.Models;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.LightFinder;
|
||||
using LightlessSync.Utils;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.UI.Models;
|
||||
using LightlessSync.UI.Style;
|
||||
using LightlessSync.Utils;
|
||||
using LightlessSync.WebAPI;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace LightlessSync.UI;
|
||||
@@ -799,9 +800,22 @@ public class TopTabMenu
|
||||
if (!_lightFinderService.IsBroadcasting)
|
||||
return "Syncshell Finder";
|
||||
|
||||
string? myHashedCid = null;
|
||||
try
|
||||
{
|
||||
var cid = _dalamudUtilService.GetCID();
|
||||
myHashedCid = cid.ToString().GetHash256();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Couldnt get own CID, log and return default table
|
||||
}
|
||||
|
||||
var nearbyCount = _lightFinderScannerService
|
||||
.GetActiveSyncshellBroadcasts()
|
||||
.Where(b => !string.IsNullOrEmpty(b.GID))
|
||||
.Where(b =>
|
||||
!string.IsNullOrEmpty(b.GID) &&
|
||||
!string.Equals(b.HashedCID, myHashedCid, StringComparison.Ordinal))
|
||||
.Select(b => b.GID!)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Count();
|
||||
|
||||
Reference in New Issue
Block a user