diff --git a/MareAPI b/MareAPI index b4b8ff4..395123f 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit b4b8ff4a52db9729660fe803bb812adf96dbcd8b +Subproject commit 395123f055d32efc7658e0b3ecbacd683ed25798 diff --git a/MareSynchronos/Services/CharaData/Models/CharaDataExtendedUpdateDto.cs b/MareSynchronos/Services/CharaData/Models/CharaDataExtendedUpdateDto.cs index 9b431d9..c0774e2 100644 --- a/MareSynchronos/Services/CharaData/Models/CharaDataExtendedUpdateDto.cs +++ b/MareSynchronos/Services/CharaData/Models/CharaDataExtendedUpdateDto.cs @@ -11,6 +11,7 @@ public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto { _charaDataFullDto = charaDataFullDto; _userList = charaDataFullDto.AllowedUsers.ToList(); + _groupList = charaDataFullDto.AllowedGroups.ToList(); _poseList = charaDataFullDto.PoseData.Select(k => new PoseEntry(k.Id) { Description = k.Description, @@ -22,6 +23,7 @@ public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto public CharaDataUpdateDto BaseDto => new(Id) { AllowedUsers = AllowedUsers, + AllowedGroups = AllowedGroups, AccessType = base.AccessType, CustomizeData = base.CustomizeData, Description = base.Description, @@ -192,15 +194,25 @@ public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto public IEnumerable UserList => _userList; private readonly List _userList; + + public IEnumerable GroupList => _groupList; + private readonly List _groupList; + public IEnumerable PoseList => _poseList; private readonly List _poseList; - public void AddToList(string user) + public void AddUserToList(string user) { _userList.Add(new(user, null)); UpdateAllowedUsers(); } + public void AddGroupToList(string group) + { + _groupList.Add(new(group, null)); + UpdateAllowedGroups(); + } + private void UpdateAllowedUsers() { AllowedUsers = [.. _userList.Select(u => u.UID)]; @@ -211,12 +223,28 @@ public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto } } - public void RemoveFromList(string user) + private void UpdateAllowedGroups() + { + AllowedGroups = [.. _groupList.Select(u => u.GID)]; + if (!AllowedGroups.Except(_charaDataFullDto.AllowedGroups.Select(u => u.GID), StringComparer.Ordinal).Any() + && !_charaDataFullDto.AllowedGroups.Select(u => u.GID).Except(AllowedGroups, StringComparer.Ordinal).Any()) + { + AllowedGroups = null; + } + } + + public void RemoveUserFromList(string user) { _userList.RemoveAll(u => string.Equals(u.UID, user, StringComparison.Ordinal)); UpdateAllowedUsers(); } + public void RemoveGroupFromList(string group) + { + _groupList.RemoveAll(u => string.Equals(u.GID, group, StringComparison.Ordinal)); + UpdateAllowedGroups(); + } + public void AddPose() { _poseList.Add(new PoseEntry(null)); @@ -279,6 +307,7 @@ public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto base.CustomizeData = null; base.ManipulationData = null; AllowedUsers = null; + AllowedGroups = null; Poses = null; _poseList.Clear(); _poseList.AddRange(_charaDataFullDto.PoseData.Select(k => new PoseEntry(k.Id) @@ -316,6 +345,7 @@ public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto || base.AccessType != null || base.ShareType != null || AllowedUsers != null + || AllowedGroups != null || base.GlamourerData != null || base.FileSwaps != null || base.FileGamePaths != null diff --git a/MareSynchronos/UI/CharaDataHubUi.Functions.cs b/MareSynchronos/UI/CharaDataHubUi.Functions.cs index 62a9d72..e2841c0 100644 --- a/MareSynchronos/UI/CharaDataHubUi.Functions.cs +++ b/MareSynchronos/UI/CharaDataHubUi.Functions.cs @@ -18,7 +18,7 @@ internal sealed partial class CharaDataHubUi private static string GetShareTypeString(ShareTypeDto dto) => dto switch { - ShareTypeDto.Private => "Private", + ShareTypeDto.Private => "Code Only", ShareTypeDto.Shared => "Shared" }; diff --git a/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs b/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs index 564c22f..958a5d3 100644 --- a/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs +++ b/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs @@ -6,6 +6,7 @@ using ImGuiNET; using MareSynchronos.API.Dto.CharaData; using MareSynchronos.Services.CharaData.Models; using System.Numerics; +using MareSynchronos.Services; namespace MareSynchronos.UI; @@ -122,15 +123,15 @@ internal sealed partial class CharaDataHubUi ImGui.EndCombo(); } _uiSharedService.DrawHelpText("You can control who has access to your character data based on the access restrictions." + UiSharedService.TooltipSeparator - + "Specified: Only people you directly specify in 'Specific Individuals' can access this character data" + Environment.NewLine + + "Specified: Only people and syncshells you directly specify in 'Specific Individuals / Syncshells' can access this character data" + Environment.NewLine + "Close Pairs: Only people you have directly paired can access this character data" + Environment.NewLine + "All Pairs: All people you have paired can access this character data" + Environment.NewLine + "Everyone: Everyone can access this character data" + UiSharedService.TooltipSeparator + "Note: To access your character data the person in question requires to have the code. Exceptions for 'Shared' data, see 'Sharing' below." + Environment.NewLine + "Note: For 'Close' and 'All Pairs' the pause state plays a role. Paused people will not be able to access your character data." + Environment.NewLine - + "Note: Directly specified individuals in the 'Specific Individuals' list will be able to access your character data regardless of pause or pair state."); + + "Note: Directly specified Individuals or Syncshells in the 'Specific Individuals / Syncshells' list will be able to access your character data regardless of pause or pair state."); - DrawSpecificIndividuals(updateDto); + DrawSpecific(updateDto); ImGui.SetNextItemWidth(200); var dtoShareType = updateDto.ShareType; @@ -150,8 +151,8 @@ internal sealed partial class CharaDataHubUi } } _uiSharedService.DrawHelpText("This regulates how you want to distribute this character data." + UiSharedService.TooltipSeparator - + "Private: People require to have the code to download this character data" + Environment.NewLine - + "Shared: People that are allowed through 'Access Restrictions' will have this character data entry displayed in 'Shared with You'" + UiSharedService.TooltipSeparator + + "Code Only: People require to have the code to download this character data" + Environment.NewLine + + "Shared: People that are allowed through 'Access Restrictions' will have this character data entry displayed in 'Shared with You' (it can also be accessed through the code)" + UiSharedService.TooltipSeparator + "Note: Shared is incompatible with Access Restriction 'Everyone'"); ImGuiHelpers.ScaledDummy(10f); @@ -649,6 +650,7 @@ internal sealed partial class CharaDataHubUi if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "New Character Data Entry")) { _charaDataManager.CreateCharaDataEntry(_closalCts.Token); + _selectNewEntry = true; } } if (_charaDataManager.DataCreationTask != null) @@ -685,49 +687,112 @@ internal sealed partial class CharaDataHubUi ImGuiHelpers.ScaledDummy(10); ImGui.Separator(); + var charaDataEntries = _charaDataManager.OwnCharaData.Count; + if (charaDataEntries != _dataEntries && _selectNewEntry && _charaDataManager.OwnCharaData.Any()) + { + _selectedDtoId = _charaDataManager.OwnCharaData.Last().Value.Id; + _selectNewEntry = false; + } + _ = _charaDataManager.OwnCharaData.TryGetValue(_selectedDtoId, out var dto); DrawEditCharaData(dto); } - private void DrawSpecificIndividuals(CharaDataExtendedUpdateDto updateDto) - { - UiSharedService.DrawTree("Access for Specific Individuals", () => - { - ImGui.SetNextItemWidth(200); - ImGui.InputText("##AliasToAdd", ref _specificIndividualAdd, 20); - ImGui.SameLine(); - using (ImRaii.Disabled(string.IsNullOrEmpty(_specificIndividualAdd) - || updateDto.UserList.Any(f => string.Equals(f.UID, _specificIndividualAdd, StringComparison.Ordinal) || string.Equals(f.Alias, _specificIndividualAdd, StringComparison.Ordinal)))) - { - if (_uiSharedService.IconButton(FontAwesomeIcon.Plus)) - { - updateDto.AddToList(_specificIndividualAdd); - _specificIndividualAdd = string.Empty; - } - } - ImGui.SameLine(); - ImGui.TextUnformatted("UID/Vanity ID to Add"); - _uiSharedService.DrawHelpText("Users added to this list will be able to access this character data regardless of your pause or pair state with them." + UiSharedService.TooltipSeparator - + "Note: Mistyped entries will be automatically removed on updating data to server."); + bool _selectNewEntry = false; + int _dataEntries = 0; - using (var lb = ImRaii.ListBox("Allowed Individuals", new(200, 200))) + private void DrawSpecific(CharaDataExtendedUpdateDto updateDto) + { + UiSharedService.DrawTree("Access for Specific Individuals / Syncshells", () => + { + using (ImRaii.PushId("user")) { - foreach (var user in updateDto.UserList) + using (ImRaii.Group()) { - var userString = string.IsNullOrEmpty(user.Alias) ? user.UID : $"{user.Alias} ({user.UID})"; - if (ImGui.Selectable(userString, string.Equals(user.UID, _selectedSpecificIndividual, StringComparison.Ordinal))) + ImGui.SetNextItemWidth(200); + ImGui.InputText("##AliasToAdd", ref _specificIndividualAdd, 20); + ImGui.SameLine(); + using (ImRaii.Disabled(string.IsNullOrEmpty(_specificIndividualAdd) + || updateDto.UserList.Any(f => string.Equals(f.UID, _specificIndividualAdd, StringComparison.Ordinal) || string.Equals(f.Alias, _specificIndividualAdd, StringComparison.Ordinal)))) { - _selectedSpecificIndividual = user.UID; + if (_uiSharedService.IconButton(FontAwesomeIcon.Plus)) + { + updateDto.AddUserToList(_specificIndividualAdd); + _specificIndividualAdd = string.Empty; + } + } + ImGui.SameLine(); + ImGui.TextUnformatted("UID/Vanity UID to Add"); + _uiSharedService.DrawHelpText("Users added to this list will be able to access this character data regardless of your pause or pair state with them." + UiSharedService.TooltipSeparator + + "Note: Mistyped entries will be automatically removed on updating data to server."); + + using (var lb = ImRaii.ListBox("Allowed Individuals", new(200, 200))) + { + foreach (var user in updateDto.UserList) + { + var userString = string.IsNullOrEmpty(user.Alias) ? user.UID : $"{user.Alias} ({user.UID})"; + if (ImGui.Selectable(userString, string.Equals(user.UID, _selectedSpecificUserIndividual, StringComparison.Ordinal))) + { + _selectedSpecificUserIndividual = user.UID; + } + } + } + + using (ImRaii.Disabled(string.IsNullOrEmpty(_selectedSpecificUserIndividual))) + { + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove selected User")) + { + updateDto.RemoveUserFromList(_selectedSpecificUserIndividual); + _selectedSpecificUserIndividual = string.Empty; + } } } } + ImGui.SameLine(); + ImGuiHelpers.ScaledDummy(20); + ImGui.SameLine(); - using (ImRaii.Disabled(string.IsNullOrEmpty(_selectedSpecificIndividual))) + using (ImRaii.PushId("group")) { - if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove selected User")) + using (ImRaii.Group()) { - updateDto.RemoveFromList(_selectedSpecificIndividual); - _selectedSpecificIndividual = string.Empty; + ImGui.SetNextItemWidth(200); + ImGui.InputText("##GroupAliasToAdd", ref _specificGroupAdd, 20); + ImGui.SameLine(); + using (ImRaii.Disabled(string.IsNullOrEmpty(_specificGroupAdd) + || updateDto.GroupList.Any(f => string.Equals(f.GID, _specificGroupAdd, StringComparison.Ordinal) || string.Equals(f.Alias, _specificGroupAdd, StringComparison.Ordinal)))) + { + if (_uiSharedService.IconButton(FontAwesomeIcon.Plus)) + { + updateDto.AddGroupToList(_specificGroupAdd); + _specificGroupAdd = string.Empty; + } + } + ImGui.SameLine(); + ImGui.TextUnformatted("GID/Vanity GID to Add"); + _uiSharedService.DrawHelpText("Users in Syncshells added to this list will be able to access this character data regardless of your pause or pair state with them." + UiSharedService.TooltipSeparator + + "Note: Mistyped entries will be automatically removed on updating data to server."); + + using (var lb = ImRaii.ListBox("Allowed Syncshells", new(200, 200))) + { + foreach (var group in updateDto.GroupList) + { + var userString = string.IsNullOrEmpty(group.Alias) ? group.GID : $"{group.Alias} ({group.GID})"; + if (ImGui.Selectable(userString, string.Equals(group.GID, _selectedSpecificGroupIndividual, StringComparison.Ordinal))) + { + _selectedSpecificGroupIndividual = group.GID; + } + } + } + + using (ImRaii.Disabled(string.IsNullOrEmpty(_selectedSpecificGroupIndividual))) + { + if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove selected Syncshell")) + { + updateDto.RemoveGroupFromList(_selectedSpecificGroupIndividual); + _selectedSpecificGroupIndividual = string.Empty; + } + } } } diff --git a/MareSynchronos/UI/CharaDataHubUi.cs b/MareSynchronos/UI/CharaDataHubUi.cs index 6b8b2ac..eb12907 100644 --- a/MareSynchronos/UI/CharaDataHubUi.cs +++ b/MareSynchronos/UI/CharaDataHubUi.cs @@ -45,11 +45,13 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase private bool _openMcdOnlineOnNextRun = false; private bool _readExport; private string _selectedDtoId = string.Empty; - private string _selectedSpecificIndividual = string.Empty; + private string _selectedSpecificUserIndividual = string.Empty; + private string _selectedSpecificGroupIndividual = string.Empty; private string _sharedWithYouDescriptionFilter = string.Empty; private bool _sharedWithYouDownloadableFilter = false; private string _sharedWithYouOwnerFilter = string.Empty; private string _specificIndividualAdd = string.Empty; + private string _specificGroupAdd = string.Empty; private bool _abbreviateCharaName = false; public CharaDataHubUi(ILogger logger, MareMediator mediator, PerformanceCollectorService performanceCollectorService, @@ -588,6 +590,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase ImGui.AlignTextToFramePadding(); DrawAddOrRemoveFavorite(_charaDataManager.LastDownloadedMetaInfo); + ImGui.NewLine(); if (!_charaDataManager.DownloadMetaInfoTask?.IsCompleted ?? false) { UiSharedService.ColorTextWrapped("Downloading meta info. Please wait.", ImGuiColors.DalamudYellow);