@@ -1,20 +1,20 @@
using Dalamud.Bindings.ImGui ;
using Dalamud.Interface ;
using Dalamud.Interface.Colors ;
using Dalamud.Interface.ImGuiFileDialog ;
using Dalamud.Interface.Textures.TextureWraps ;
using Dalamud.Interface.Utility ;
using Dalamud.Interface.Utility.Raii ;
using LightlessSync.API.Data.Enum ;
using LightlessSync.API.Data.Extensions ;
using LightlessSync.API.Dto.Group ;
using LightlessSync.PlayerData.Pairs ;
using LightlessSync.Services ;
using LightlessSync.Services.Mediator ;
using LightlessSync.PlayerData.Pair s ;
using LightlessSync.Services.Profile s ;
using LightlessSync.UI.Services ;
using LightlessSync.WebAPI ;
using Microsoft.Extensions.Logging ;
using SixLabors.ImageSharp ;
using SixLabors.ImageSharp.PixelFormats ;
using System.Globalization ;
namespace LightlessSync.UI ;
@@ -25,38 +25,29 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
private readonly bool _isModerator = false ;
private readonly bool _isOwner = false ;
private readonly List < string > _oneTimeInvites = [ ] ;
private readonly PairManager _pairManager ;
private readonly LightlessProfileManager _lightlessProfileManager ;
private readonly FileDialogManager _fileDialogManager ;
private readonly UiSharedService _uiSharedService ;
private readonly PairUiService _pairUiService ;
private List < BannedGroupUserDto > _bannedUsers = [ ] ;
private LightlessGroupProfileData ? _profileData = null ;
private bool _adjustedForScollBarsLocalProfile = false ;
private bool _adjustedForScollBarsOnlineProfile = false ;
private string _descriptionText = string . Empty ;
private IDalamudTextureWrap ? _pfpTextureWrap ;
private string _profileDescription = string . Empty ;
private byte [ ] _profileImage = [ ] ;
private bool _showFileDialogError = false ;
private int _multiInvites ;
private string _newPassword ;
private bool _pwChangeSuccess ;
private Task < int > ? _pruneTestTask ;
private Task < int > ? _pruneTask ;
private int _pruneDays = 14 ;
private List < int > _selectedTags = [ ] ;
public SyncshellAdminUI ( ILogger < SyncshellAdminUI > logger , LightlessMediator mediator , ApiController apiController ,
UiSharedService uiSharedService , PairManager pairManager , GroupFullInfoDto groupFullInfo , PerformanceCollectorService performanceCollectorService , LightlessProfileManager lightlessProfileManager , FileDialogManager fileDialogManager )
UiSharedService uiSharedService , PairUiService pairUiService , GroupFullInfoDto groupFullInfo , PerformanceCollectorService performanceCollectorService , LightlessProfileManager lightlessProfileManager )
: base ( logger , mediator , "Syncshell Admin Panel (" + groupFullInfo . GroupAliasOrGID + ")" , performanceCollectorService )
{
GroupFullInfo = groupFullInfo ;
_apiController = apiController ;
_uiSharedService = uiSharedService ;
_pairManager = pairManager ;
_lightlessProfileManager = lightlessProfileManager ;
_fileDialogManager = fileDialogManager ;
_pairUiService = pairUiService ;
_isOwner = string . Equals ( GroupFullInfo . OwnerUID , _apiController . UID , StringComparison . Ordinal ) ;
_isModerator = GroupFullInfo . GroupUserInfo . IsModerator ( ) ;
_newPassword = string . Empty ;
@@ -76,6 +67,7 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
MinimumSize = new ( 700 , 500 ) ,
MaximumSize = new ( 700 , 2000 ) ,
} ;
_pairUiService = pairUiService ;
}
public GroupFullInfoDto GroupFullInfo { get ; private set ; }
@@ -85,10 +77,13 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
if ( ! _isModerator & & ! _isOwner ) return ;
_logger . LogTrace ( "Drawing Syncshell Admin UI for {group}" , GroupFullInfo . GroupAliasOrGID ) ;
GroupFullInfo = _pairManager . Groups [ GroupFullInfo . Group ] ;
var snapshot = _pairUiService . GetSnapshot ( ) ;
if ( snapshot . GroupsByGid . TryGetValue ( GroupFullInfo . Group . GID , out var updatedInfo ) )
{
GroupFullInfo = updatedInfo ;
}
_profileData = _lightlessProfileManager . GetLightlessGroupProfile ( GroupFullInfo . Group ) ;
GetTagsFromProfile ( ) ;
using var id = ImRaii . PushId ( "syncshell_admin_" + GroupFullInfo . GID ) ;
using ( _uiSharedService . UidFont . Push ( ) )
@@ -207,189 +202,51 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
ownerTab . Dispose ( ) ;
}
}
private void DrawProfile ( )
{
var profileTab = ImRaii . TabItem ( "Profile" ) ;
if ( ! profileTab )
return ;
if ( profileTab )
if ( _ profileData ! = null )
{
if ( _uiSharedService . MediumTreeNode ( "Current Profile" , UIColors . Get ( "LightlessPurple" ) ) )
if ( ! string . Equals ( _profileDescription , _profileData . Description , StringComparison . Ordinal ) )
{
ImGui . Dummy ( new Vector2 ( 5 ) ) ;
if ( _profileData = = null )
{
UiSharedService . ColorTextWrapped ( "Failed to load profile data." , ImGuiColors . DalamudRed ) ;
ImGui . TreePop ( ) ;
return ;
}
if ( ! _profileImage . SequenceEqual ( _profileData . ProfileImageData . Value ) )
{
_profileImage = _profileData . ProfileImageData . Value ;
_pfpTextureWrap ? . Dispose ( ) ;
_pfpTextureWrap = _uiSharedService . LoadImage ( _profileImage ) ;
}
if ( ! string . Equals ( _profileDescription , _profileData . Description , StringComparison . OrdinalIgnoreCase ) )
{
_profileDescription = _profileData . Description ;
_descriptionText = _profileDescription ;
}
if ( _pfpTextureWrap ! = null )
{
ImGui . Image ( _pfpTextureWrap . Handle , ImGuiHelpers . ScaledVector2 ( _pfpTextureWrap . Width , _pfpTextureWrap . Height ) ) ;
}
var spacing = ImGui . GetStyle ( ) . ItemSpacing . X ;
ImGuiHelpers . ScaledRelativeSameLine ( 256 , spacing ) ;
using ( _uiSharedService . GameFont . Push ( ) )
{
var descriptionTextSize = ImGui . CalcTextSize ( _profileData . Description , wrapWidth : 256f ) ;
var childFrame = ImGuiHelpers . ScaledVector2 ( 256 + ImGui . GetStyle ( ) . WindowPadding . X + ImGui . GetStyle ( ) . WindowBorderSize , 256 ) ;
if ( descriptionTextSize . Y > childFrame . Y )
{
_adjustedForScollBarsOnlineProfile = true ;
}
else
{
_adjustedForScollBarsOnlineProfile = false ;
}
childFrame = childFrame with
{
X = childFrame . X + ( _adjustedForScollBarsOnlineProfile ? ImGui . GetStyle ( ) . ScrollbarSize : 0 ) ,
} ;
if ( ImGui . BeginChildFrame ( 101 , childFrame ) )
{
UiSharedService . TextWrapped ( _profileData . Description ) ;
}
ImGui . EndChildFrame ( ) ;
ImGui . TreePop ( ) ;
}
var nsfw = _profileData . IsNsfw ;
ImGui . BeginDisabled ( ) ;
ImGui . Checkbox ( "Is NSFW" , ref nsfw ) ;
ImGui . EndDisabled ( ) ;
_profileDescription = _profileData . Description ;
}
ImGui . Separator ( ) ;
UiSharedService . TextWrapped ( "Preview the Syncshell profile in a standalone window." ) ;
if ( _uiSharedService . MediumTreeNode ( "Profile Settings" , UIColors . Get ( "LightlessPurple" ) ) )
if ( _uiSharedService . IconTextButton ( FontAwesomeIcon . AddressCard , "Open Syncshell Profile" ) )
{
ImGui . Dummy ( new Vector2 ( 5 ) ) ;
ImGui . TextUnformatted ( $"Profile Picture:" ) ;
if ( _uiSharedService . IconTextButton ( FontAwesomeIcon . FileUpload , "Upload new profile picture" ) )
{
_fileDialogManager . OpenFileDialog ( "Select new Profile picture" , ".png" , ( success , file ) = >
{
if ( ! success ) return ;
_ = Task . Run ( async ( ) = >
{
var fileContent = await File . ReadAllBytesAsync ( file ) . ConfigureAwait ( false ) ;
MemoryStream ms = new ( fileContent ) ;
await using ( ms . ConfigureAwait ( false ) )
{
var format = await Image . DetectFormatAsync ( ms ) . ConfigureAwait ( false ) ;
if ( ! format . FileExtensions . Contains ( "png" , StringComparer . OrdinalIgnoreCase ) )
{
_showFileDialogError = true ;
return ;
}
using var image = Image . Load < Rgba32 > ( fileContent ) ;
if ( image . Width > 512 | | image . Height > 512 | | ( fileContent . Length > 2000 * 1024 ) )
{
_showFileDialogError = true ;
return ;
}
_showFileDialogError = false ;
await _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : null , Tags : null , Convert . ToBase64String ( fileContent ) , BannerBase64 : null , IsNsfw : null , IsDisabled : null ) )
. ConfigureAwait ( false ) ;
}
} ) ;
} ) ;
}
UiSharedService . AttachToolTip ( "Select and upload a new profile picture" ) ;
ImGui . SameLine ( ) ;
if ( _uiSharedService . IconTextButton ( FontAwesomeIcon . Trash , "Clear uploaded profile picture" ) )
{
_ = _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : null , Tags : null , PictureBase64 : null , BannerBase64 : null , IsNsfw : null , IsDisabled : null ) ) ;
}
UiSharedService . AttachToolTip ( "Clear your currently uploaded profile picture" ) ;
if ( _showFileDialogError )
{
UiSharedService . ColorTextWrapped ( "The profile picture must be a PNG file with a maximum height and width of 256px and 250KiB size" , ImGuiColors . DalamudRed ) ;
}
ImGui . Separator ( ) ;
ImGui . TextUnformatted ( $"Tags:" ) ;
var childFrameLocal = ImGuiHelpers . ScaledVector2 ( 256 + ImGui . GetStyle ( ) . WindowPadding . X + ImGui . GetStyle ( ) . WindowBorderSize , 200 ) ;
var allCategoryIndexes = Enum . GetValues < ProfileTags > ( )
. Cast < int > ( )
. ToList ( ) ;
foreach ( int tag in allCategoryIndexes )
{
using ( ImRaii . PushId ( $"tag-{tag}" ) ) DrawTag ( tag ) ;
}
ImGui . Separator ( ) ;
var widthTextBox = 400 ;
var posX = ImGui . GetCursorPosX ( ) ;
ImGui . TextUnformatted ( $"Description {_descriptionText.Length}/1500" ) ;
ImGui . SetCursorPosX ( posX ) ;
ImGuiHelpers . ScaledRelativeSameLine ( widthTextBox , ImGui . GetStyle ( ) . ItemSpacing . X ) ;
ImGui . TextUnformatted ( "Preview (approximate)" ) ;
using ( _uiSharedService . GameFont . Push ( ) )
ImGui . InputTextMultiline ( "##description" , ref _descriptionText , 1500 , ImGuiHelpers . ScaledVector2 ( widthTextBox , 200 ) ) ;
ImGui . SameLine ( ) ;
using ( _uiSharedService . GameFont . Push ( ) )
{
var descriptionTextSizeLocal = ImGui . CalcTextSize ( _descriptionText , wrapWidth : 256f ) ;
if ( descriptionTextSizeLocal . Y > childFrameLocal . Y )
{
_adjustedForScollBarsLocalProfile = true ;
}
else
{
_adjustedForScollBarsLocalProfile = false ;
}
childFrameLocal = childFrameLocal with
{
X = childFrameLocal . X + ( _adjustedForScollBarsLocalProfile ? ImGui . GetStyle ( ) . ScrollbarSize : 0 ) ,
} ;
if ( ImGui . BeginChildFrame ( 102 , childFrameLocal ) )
{
UiSharedService . TextWrapped ( _descriptionText ) ;
}
ImGui . EndChildFrame ( ) ;
}
if ( _uiSharedService . IconTextButton ( FontAwesomeIcon . Save , "Save Description" ) )
{
_ = _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : _descriptionText , Tags : null , PictureBase64 : null , BannerBase64 : null , IsNsfw : null , IsDisabled : null ) ) ;
}
UiSharedService . AttachToolTip ( "Sets your profile description text" ) ;
ImGui . SameLine ( ) ;
if ( _uiSharedService . IconTextButton ( FontAwesomeIcon . Trash , "Clear Description" ) )
{
_ = _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : null , Tags : null , PictureBase64 : null , BannerBase64 : null , IsNsfw : null , IsDisabled : null ) ) ;
}
UiSharedService . AttachToolTip ( "Clears your profile description text" ) ;
ImGui . Separator ( ) ;
ImGui . TextUnformatted ( $"Profile Options:" ) ;
var isNsfw = _profileData ? . IsNsfw ? ? false ;
if ( ImGui . Checkbox ( "Profile is NSFW" , ref isNsfw ) )
{
_ = _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : null , Tags : null , PictureBase64 : null , BannerBase64 : null , IsNsfw : isNsfw , IsDisabled : null ) ) ;
}
_uiSharedService . DrawHelpText ( "If your profile description or image can be considered NSFW, toggle this to ON" ) ;
ImGui . TreePop ( ) ;
Mediator . Publish ( new GroupProfileOpenStandaloneMessage ( GroupFullInfo ) ) ;
}
UiSharedService . AttachToolTip ( "Opens the standalone Syncshell profile window for this group." ) ;
ImGuiHelpers . ScaledDummy ( 2f ) ;
ImGui . TextDisabled ( "Profile Flags" ) ;
ImGui . BulletText ( _profileData . IsNsfw ? "Marked as NSFW" : "Marked as SFW" ) ;
ImGui . BulletText ( _profileData . IsDisabled ? "Profile disabled for viewers" : "Profile active" ) ;
ImGuiHelpers . ScaledDummy ( 2f ) ;
UiSharedService . ColoredSeparator ( UIColors . Get ( "LightlessPurple" ) , 1.5f ) ;
ImGuiHelpers . ScaledDummy ( 2f ) ;
UiSharedService . TextWrapped ( "Open the syncshell profile editor to update images, description, tags, and visibility settings." ) ;
ImGuiHelpers . ScaledDummy ( 2f ) ;
if ( _uiSharedService . IconTextButton ( FontAwesomeIcon . UserEdit , "Open Syncshell Profile Editor" ) )
{
Mediator . Publish ( new OpenGroupProfileEditorMessage ( GroupFullInfo ) ) ;
}
UiSharedService . AttachToolTip ( "Launches the editor window and associated live preview for this syncshell." ) ;
}
else
{
UiSharedService . TextWrapped ( "Profile information is loading..." ) ;
}
profileTab . Dispose ( ) ;
}
@@ -400,7 +257,8 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
{
if ( _uiSharedService . MediumTreeNode ( "User List & Administration" , UIColors . Get ( "LightlessPurple" ) ) )
{
if ( ! _pairManager . GroupPairs . TryGetValue ( GroupFullInfo , out var pairs ) )
var snapshot = _pairUiService . GetSnapshot ( ) ;
if ( ! snapshot . GroupPairs . TryGetValue ( GroupFullInfo , out var pairs ) )
{
UiSharedService . ColorTextWrapped ( "No users found in this Syncshell" , ImGuiColors . DalamudYellow ) ;
}
@@ -736,33 +594,6 @@ public class SyncshellAdminUI : WindowMediatorSubscriberBase
}
inviteTab . Dispose ( ) ;
}
private void DrawTag ( int tag )
{
var HasTag = _selectedTags . Contains ( tag ) ;
var tagName = ( ProfileTags ) tag ;
if ( ImGui . Checkbox ( tagName . ToString ( ) , ref HasTag ) )
{
if ( HasTag )
{
_selectedTags . Add ( tag ) ;
_ = _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : null , Tags : _selectedTags . ToArray ( ) , PictureBase64 : null , BannerBase64 : null , IsNsfw : null , IsDisabled : null ) ) ;
}
else
{
_selectedTags . Remove ( tag ) ;
_ = _apiController . GroupSetProfile ( new GroupProfileDto ( new GroupData ( GroupFullInfo . Group . AliasOrGID ) , Description : null , Tags : _selectedTags . ToArray ( ) , PictureBase64 : null , BannerBase64 : null , IsNsfw : null , IsDisabled : null ) ) ;
}
}
}
private void GetTagsFromProfile ( )
{
if ( _profileData ! = null )
{
_selectedTags = [ . . _profileData . Tags ] ;
}
}
public override void OnClose ( )
{