Files
Delta-v/Content.Client/_Funkystation/NanoChat/InviteToGroupPopup.xaml.cs
T
Evaisa daf070f876 Fix issues with nanochat
When pAI and PDA owner open nanochat at the same time it should no longer break.
"Invite To Group" list now shows accurate/live data instead of outdated cached recipient data
Popups should no longer stay open when switching apps or closing the PDA.
Group chats should now properly show when messages failed to deliver.
Invite menus now show job titles.
Invite menus are now sorted by department.

(cherry picked from commit f23580452a0a43cc105e5642ead35999a820c675)
2025-12-10 17:55:44 +01:00

192 lines
6.4 KiB
C#

// SPDX-FileCopyrightText: 2025 Evaisa <evagiacosa1@gmail.com>
// SPDX-FileCopyrightText: 2025 EvaisaDev <mail@evaisa.dev>
//
// SPDX-License-Identifier: AGPL-3.0-or-later AND MIT
using System.Linq;
using System.Numerics;
using Content.Shared._DV.CartridgeLoader.Cartridges;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client._Funkystation.NanoChat;
[GenerateTypedNameReferences]
public sealed partial class InviteToGroupPopup : DefaultWindow
{
private List<NanoChatRecipient> _allContacts = new();
private HashSet<uint> _existingMembers = new();
private uint _ownNumber;
public event Action<uint>? OnInvite;
public InviteToGroupPopup()
{
RobustXamlLoader.Load(this);
ContentsContainer.Margin = new Thickness(3);
InviteCloseButton.OnPressed += _ => Close();
SearchInput.OnTextChanged += _ => UpdateContactList();
}
public void SetContacts(List<NanoChatRecipient> contacts, HashSet<uint> existingMembers, uint ownNumber)
{
_allContacts = contacts;
_existingMembers = existingMembers;
_ownNumber = ownNumber;
UpdateContactList();
}
private void UpdateContactList()
{
ContactsList.RemoveAllChildren();
if (_allContacts.Count == 0)
{
ContactsList.AddChild(new Label { Text = Loc.GetString("nano-chat-look-up-no-server") });
return;
}
var searchText = SearchInput.Text.ToLower();
var filteredContacts = _allContacts
.Where(c => c.Number != _ownNumber &&
!_existingMembers.Contains(c.Number) &&
(string.IsNullOrWhiteSpace(searchText) ||
c.Name.ToLower().Contains(searchText) ||
c.Number.ToString().Contains(searchText) ||
(!string.IsNullOrWhiteSpace(c.JobTitle) && c.JobTitle.ToLower().Contains(searchText))))
.ToList();
if (filteredContacts.Count == 0)
{
ContactsList.AddChild(new Label { Text = Loc.GetString("nano-chat-no-results") });
return;
}
// Group contacts by department like crew manifest
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var departmentGroups = new Dictionary<DepartmentPrototype, List<NanoChatRecipient>>();
var noDepartmentContacts = new List<NanoChatRecipient>();
foreach (var contact in filteredContacts)
{
if (!string.IsNullOrWhiteSpace(contact.Department) &&
prototypeManager.TryIndex<DepartmentPrototype>(contact.Department, out var dept))
{
departmentGroups.GetOrNew(dept).Add(contact);
}
else
{
noDepartmentContacts.Add(contact);
}
}
// Sort departments by weight
var sortedDepartments = departmentGroups.Keys.ToList();
sortedDepartments.Sort(DepartmentUIComparer.Instance);
var rowIndex = 0;
foreach (var department in sortedDepartments)
{
var departmentContacts = departmentGroups[department];
departmentContacts.Sort((a, b) => string.CompareOrdinal(a.Name, b.Name));
// Add department header
ContactsList.AddChild(new Label
{
Text = Loc.GetString(department.Name),
StyleClasses = { "LabelBig" },
Margin = new Thickness(0, rowIndex == 0 ? 0 : 8, 0, 2)
});
foreach (var contact in departmentContacts)
{
var isEvenRow = rowIndex % 2 == 0;
var contactControl = new ContactContainer(contact, isEvenRow, OnInvite);
ContactsList.AddChild(contactControl);
rowIndex++;
}
}
// Add contacts without departments at the end
if (noDepartmentContacts.Count > 0)
{
noDepartmentContacts.Sort((a, b) => string.CompareOrdinal(a.Name, b.Name));
if (sortedDepartments.Count > 0)
{
ContactsList.AddChild(new Label
{
Text = Loc.GetString("nano-chat-other-department"),
StyleClasses = { "LabelBig" },
Margin = new Thickness(0, 8, 0, 2)
});
}
foreach (var contact in noDepartmentContacts)
{
var isEvenRow = rowIndex % 2 == 0;
var contactControl = new ContactContainer(contact, isEvenRow, OnInvite);
ContactsList.AddChild(contactControl);
rowIndex++;
}
}
}
public void ClearSearch()
{
SearchInput.Text = string.Empty;
}
private sealed class ContactContainer : PanelContainer
{
public ContactContainer(NanoChatRecipient contact, bool isEvenRow, Action<uint>? onInvite)
{
HorizontalExpand = true;
StyleClasses.Add(isEvenRow ? "PanelBackgroundBaseDark" : "PanelBackgroundLight");
// Show name with job title if available
var displayName = contact.Name;
if (!string.IsNullOrWhiteSpace(contact.JobTitle))
displayName = $"{contact.Name} ({contact.JobTitle})";
var nameLabel = new Label
{
Text = displayName,
HorizontalAlignment = HAlignment.Left,
HorizontalExpand = true
};
var numberLabel = new Label
{
Text = $"#{contact.Number:D4}",
HorizontalAlignment = HAlignment.Right,
Margin = new Thickness(0, 0, 36, 0),
};
var inviteButton = new Button
{
Text = "+",
HorizontalAlignment = HAlignment.Right,
MinSize = new Vector2(32, 32),
MaxSize = new Vector2(32, 32),
ToolTip = Loc.GetString("nano-chat-invite"),
};
inviteButton.AddStyleClass("OpenBoth");
inviteButton.OnPressed += _ => onInvite?.Invoke(contact.Number);
AddChild(nameLabel);
AddChild(numberLabel);
AddChild(inviteButton);
}
}
}