Добавлена поддержка закрепления чатов и отображения иконок ролей пользователей в мессенджере.
This commit is contained in:
parent
19274a2d49
commit
5a72fbd4a0
16 changed files with 423 additions and 38 deletions
|
|
@ -12,6 +12,13 @@
|
|||
Orientation="Horizontal"
|
||||
Margin="0,0,0,4"
|
||||
Name="Header">
|
||||
<TextureRect
|
||||
Name="JobIconRect"
|
||||
SetWidth="20"
|
||||
SetHeight="20"
|
||||
Stretch="Scale"
|
||||
Margin="0,0,4,0"
|
||||
Visible="False" />
|
||||
<Label
|
||||
Name="SenderLabel"
|
||||
Text=""
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Content.Client._Sunrise.Messenger;
|
|||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared._Sunrise.Messenger;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
|
|
@ -9,6 +10,7 @@ using Robust.Client.ResourceManagement;
|
|||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client._Sunrise.CartridgeLoader.Cartridges;
|
||||
|
|
@ -18,6 +20,7 @@ public sealed partial class MessagePanel : PanelContainer
|
|||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private ClientEmojiSystem? EmojiSystem => _entitySystemManager.GetEntitySystemOrNull<ClientEmojiSystem>();
|
||||
private SpriteSystem GetSpriteSystem() => _entitySystemManager.GetEntitySystem<SpriteSystem>();
|
||||
|
|
@ -53,6 +56,25 @@ public sealed partial class MessagePanel : PanelContainer
|
|||
SenderLabel.Text = message.SenderName;
|
||||
TimeLabel.Text = message.Timestamp.ToString(@"hh\:mm");
|
||||
|
||||
if (message.SenderJobIconId != null && _prototypeManager.TryIndex(message.SenderJobIconId.Value, out JobIconPrototype? jobIcon))
|
||||
{
|
||||
JobIconRect.Texture = GetSpriteSystem().Frame0(jobIcon.Icon);
|
||||
JobIconRect.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var unknownIconId = new ProtoId<JobIconPrototype>("JobIconUnknown");
|
||||
if (_prototypeManager.TryIndex(unknownIconId, out JobIconPrototype? unknownIcon))
|
||||
{
|
||||
JobIconRect.Texture = GetSpriteSystem().Frame0(unknownIcon.Icon);
|
||||
JobIconRect.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
JobIconRect.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
var parsedContent = EmojiSystem?.ParseEmojis(message.Content) ?? message.Content;
|
||||
ContentLabel.SetMessage(FormattedMessage.FromMarkupPermissive(parsedContent), MessageTagsAllowed);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ public sealed partial class MessengerUi : UIFragment
|
|||
SendMessengerMessage(MessengerUiAction.LeaveGroup, userInterface, groupId: groupId);
|
||||
_fragment.OnDeleteMessage += (chatId, messageId) =>
|
||||
SendMessengerMessage(MessengerUiAction.DeleteMessage, userInterface, chatId: chatId, messageId: messageId);
|
||||
_fragment.OnTogglePin += (chatId) =>
|
||||
SendMessengerMessage(MessengerUiAction.TogglePin, userInterface, chatId: chatId);
|
||||
}
|
||||
|
||||
public override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ using Robust.Client.UserInterface.RichText;
|
|||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.StatusIcon;
|
||||
|
||||
namespace Content.Client._Sunrise.CartridgeLoader.Cartridges;
|
||||
|
||||
|
|
@ -33,6 +35,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
public event Action<string>? OnDeclineInvite;
|
||||
public event Action<string>? OnLeaveGroup;
|
||||
public event Action<string, long>? OnDeleteMessage;
|
||||
public event Action<string>? OnTogglePin;
|
||||
|
||||
private string? _currentChatId;
|
||||
private MessengerUiState? _currentState;
|
||||
|
|
@ -52,11 +55,13 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
private Dictionary<string, int>? _lastUnreadCounts;
|
||||
private HashSet<string>? _lastUserIds;
|
||||
private HashSet<string>? _lastGroupIds;
|
||||
private HashSet<string>? _lastPinnedChats;
|
||||
private int _lastActiveTab = -1;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private CreateGroupDialog? _createGroupDialog;
|
||||
private AddUserDialog? _addUserDialog;
|
||||
|
|
@ -413,9 +418,6 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
}
|
||||
}
|
||||
|
||||
if (textContainer == null || nameLabel == null)
|
||||
return;
|
||||
|
||||
var existingCounter = textContainer.Children.OfType<Label>()
|
||||
.FirstOrDefault(l => l != nameLabel && l.StyleClasses.Contains("Bold"));
|
||||
|
||||
|
|
@ -492,15 +494,18 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
{
|
||||
var currentUserIds = new HashSet<string>(state.Users.Select(u => u.UserId));
|
||||
var currentGroupIds = new HashSet<string>(state.Groups.Select(g => g.GroupId));
|
||||
var currentPinnedChats = new HashSet<string>(state.PinnedChats);
|
||||
|
||||
var tabChanged = _lastActiveTab != _activeTab;
|
||||
|
||||
var needsUpdate = _lastUserIds == null ||
|
||||
_lastGroupIds == null ||
|
||||
_lastUnreadCounts == null ||
|
||||
_lastPinnedChats == null ||
|
||||
tabChanged ||
|
||||
!currentUserIds.SetEquals(_lastUserIds) ||
|
||||
!currentGroupIds.SetEquals(_lastGroupIds) ||
|
||||
!currentPinnedChats.SetEquals(_lastPinnedChats) ||
|
||||
!AreUnreadCountsEqual(_lastUnreadCounts, state.UnreadCounts) ||
|
||||
_searchFilter != string.Empty;
|
||||
|
||||
|
|
@ -513,6 +518,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
_lastUserIds = currentUserIds;
|
||||
_lastGroupIds = currentGroupIds;
|
||||
_lastUnreadCounts = new Dictionary<string, int>(state.UnreadCounts);
|
||||
_lastPinnedChats = new HashSet<string>(currentPinnedChats);
|
||||
_lastActiveTab = _activeTab;
|
||||
|
||||
ChatsContainer.RemoveAllChildren();
|
||||
|
|
@ -536,13 +542,18 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
|
||||
if (_activeTab == 0)
|
||||
{
|
||||
foreach (var user in state.Users)
|
||||
{
|
||||
if (user.UserId == state.CurrentUserId)
|
||||
continue;
|
||||
var usersList = state.Users.Where(u => u.UserId != state.CurrentUserId).ToList();
|
||||
var pinnedChats = state.PinnedChats;
|
||||
|
||||
var sortedUsers = usersList.OrderByDescending(u => pinnedChats.Contains(GetPersonalChatId(u.UserId)))
|
||||
.ThenBy(u => u.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var user in sortedUsers)
|
||||
{
|
||||
var userName = user.Name;
|
||||
var chatId = GetPersonalChatId(user.UserId);
|
||||
var isPinned = pinnedChats.Contains(chatId);
|
||||
|
||||
if (!string.IsNullOrEmpty(searchLower) && !userName.ToLowerInvariant().Contains(searchLower))
|
||||
continue;
|
||||
|
|
@ -571,9 +582,52 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
if (isPinned)
|
||||
{
|
||||
var starLabel = new Label
|
||||
{
|
||||
Text = "★",
|
||||
HorizontalExpand = false,
|
||||
Margin = new Thickness(0, 0, 4, 0),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
textContainer.AddChild(starLabel);
|
||||
}
|
||||
|
||||
if (user.JobIconId != null && _prototypeManager.TryIndex(user.JobIconId.Value, out JobIconPrototype? jobIcon))
|
||||
{
|
||||
var iconRect = new TextureRect
|
||||
{
|
||||
Texture = GetSpriteSystem().Frame0(jobIcon.Icon),
|
||||
SetWidth = 20,
|
||||
SetHeight = 20,
|
||||
Stretch = TextureRect.StretchMode.Scale,
|
||||
Margin = new Thickness(0, 0, 4, 0)
|
||||
};
|
||||
textContainer.AddChild(iconRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unknownIconId = new ProtoId<JobIconPrototype>("JobIconUnknown");
|
||||
if (_prototypeManager.TryIndex(unknownIconId, out JobIconPrototype? unknownIcon))
|
||||
{
|
||||
var iconRect = new TextureRect
|
||||
{
|
||||
Texture = GetSpriteSystem().Frame0(unknownIcon.Icon),
|
||||
SetWidth = 20,
|
||||
SetHeight = 20,
|
||||
Stretch = TextureRect.StretchMode.Scale,
|
||||
Margin = new Thickness(0, 0, 4, 0)
|
||||
};
|
||||
textContainer.AddChild(iconRect);
|
||||
}
|
||||
}
|
||||
|
||||
var displayName = userName;
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Text = userName,
|
||||
Text = displayName,
|
||||
HorizontalExpand = true,
|
||||
ClipText = true,
|
||||
Align = Label.AlignMode.Left
|
||||
|
|
@ -598,6 +652,29 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
button.AddChild(textContainer);
|
||||
|
||||
button.OnPressed += _ => SelectChat(chatId, userName);
|
||||
|
||||
button.OnKeyBindDown += args =>
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIRightClick)
|
||||
{
|
||||
args.Handle();
|
||||
|
||||
if (_currentState != null)
|
||||
{
|
||||
if (_currentState.PinnedChats.Contains(chatId))
|
||||
_currentState.PinnedChats.Remove(chatId);
|
||||
else
|
||||
_currentState.PinnedChats.Add(chatId);
|
||||
|
||||
_lastPinnedChats = null;
|
||||
|
||||
UpdateChatsList(_currentState);
|
||||
}
|
||||
|
||||
OnTogglePin?.Invoke(chatId);
|
||||
}
|
||||
};
|
||||
|
||||
buttonContainer.AddChild(button);
|
||||
|
||||
ChatsContainer.AddChild(buttonContainer);
|
||||
|
|
@ -605,7 +682,13 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
}
|
||||
else if (_activeTab == 1)
|
||||
{
|
||||
foreach (var group in state.Groups)
|
||||
var pinnedChats = state.PinnedChats;
|
||||
|
||||
var sortedGroups = state.Groups.OrderByDescending(g => pinnedChats.Contains(g.GroupId))
|
||||
.ThenBy(g => g.Name)
|
||||
.ToList();
|
||||
|
||||
foreach (var group in sortedGroups)
|
||||
{
|
||||
var groupContainer = new BoxContainer
|
||||
{
|
||||
|
|
@ -616,6 +699,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
|
||||
var groupId = group.GroupId;
|
||||
var groupName = group.Name;
|
||||
var isPinned = pinnedChats.Contains(groupId);
|
||||
|
||||
if (!string.IsNullOrEmpty(searchLower) && !groupName.ToLowerInvariant().Contains(searchLower))
|
||||
continue;
|
||||
|
|
@ -637,9 +721,23 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
if (isPinned)
|
||||
{
|
||||
var starLabel = new Label
|
||||
{
|
||||
Text = "★",
|
||||
HorizontalExpand = false,
|
||||
Margin = new Thickness(0, 0, 4, 0),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
textContainer.AddChild(starLabel);
|
||||
}
|
||||
|
||||
var displayName = groupName;
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Text = groupName,
|
||||
Text = displayName,
|
||||
HorizontalExpand = true,
|
||||
ClipText = true,
|
||||
Align = Label.AlignMode.Left
|
||||
|
|
@ -664,6 +762,29 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
button.AddChild(textContainer);
|
||||
|
||||
button.OnPressed += _ => SelectChat(groupId, groupName);
|
||||
|
||||
button.OnKeyBindDown += args =>
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIRightClick)
|
||||
{
|
||||
args.Handle();
|
||||
|
||||
if (_currentState != null)
|
||||
{
|
||||
if (_currentState.PinnedChats.Contains(groupId))
|
||||
_currentState.PinnedChats.Remove(groupId);
|
||||
else
|
||||
_currentState.PinnedChats.Add(groupId);
|
||||
|
||||
_lastPinnedChats = null;
|
||||
|
||||
UpdateChatsList(_currentState);
|
||||
}
|
||||
|
||||
OnTogglePin?.Invoke(groupId);
|
||||
}
|
||||
};
|
||||
|
||||
groupContainer.AddChild(button);
|
||||
|
||||
ChatsContainer.AddChild(groupContainer);
|
||||
|
|
@ -992,9 +1113,49 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
var displayText = isMemberOwner
|
||||
? $"★ {userName}"
|
||||
: userName;
|
||||
if (isMemberOwner)
|
||||
{
|
||||
var ownerStarLabel = new Label
|
||||
{
|
||||
Text = "★",
|
||||
HorizontalExpand = false,
|
||||
Margin = new Thickness(0, 0, 4, 0),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
memberContainer.AddChild(ownerStarLabel);
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (user.JobIconId != null && _prototypeManager.TryIndex(user.JobIconId.Value, out JobIconPrototype? jobIcon))
|
||||
{
|
||||
var iconRect = new TextureRect
|
||||
{
|
||||
Texture = GetSpriteSystem().Frame0(jobIcon.Icon),
|
||||
SetWidth = 20,
|
||||
SetHeight = 20,
|
||||
Stretch = TextureRect.StretchMode.Scale,
|
||||
Margin = new Thickness(0, 0, 4, 0)
|
||||
};
|
||||
memberContainer.AddChild(iconRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unknownIconId = new ProtoId<JobIconPrototype>("JobIconUnknown");
|
||||
if (_prototypeManager.TryIndex(unknownIconId, out JobIconPrototype? unknownIcon))
|
||||
{
|
||||
var iconRect = new TextureRect
|
||||
{
|
||||
Texture = GetSpriteSystem().Frame0(unknownIcon.Icon),
|
||||
SetWidth = 20,
|
||||
SetHeight = 20,
|
||||
Stretch = TextureRect.StretchMode.Scale,
|
||||
Margin = new Thickness(0, 0, 4, 0)
|
||||
};
|
||||
memberContainer.AddChild(iconRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tooltipText = isMemberOwner
|
||||
? Loc.GetString("messenger-member-owner", ("name", userName))
|
||||
|
|
@ -1002,7 +1163,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
|
||||
var memberLabel = new Label
|
||||
{
|
||||
Text = displayText,
|
||||
Text = userName,
|
||||
HorizontalExpand = true,
|
||||
ClipText = true,
|
||||
ToolTip = tooltipText
|
||||
|
|
|
|||
|
|
@ -103,4 +103,10 @@ public sealed partial class MessengerCartridgeComponent : Component
|
|||
/// </summary>
|
||||
[ViewVariables]
|
||||
public List<MessengerGroupInvite> ActiveInvites = new();
|
||||
|
||||
/// <summary>
|
||||
/// Закрепленные чаты (chatId)
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public HashSet<string> PinnedChats = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ public sealed partial class MessengerCartridgeSystem
|
|||
if (message.ChatId != null && message.MessageId.HasValue)
|
||||
DeleteMessage(uid, component, loaderUid, deviceNetwork, message.ChatId, message.MessageId.Value);
|
||||
break;
|
||||
case MessengerUiAction.TogglePin:
|
||||
if (message.ChatId != null)
|
||||
TogglePin(uid, component, message.ChatId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ using Content.Shared.DeviceNetwork.Events;
|
|||
using Content.Shared._Sunrise.Messenger;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Content.Shared.PDA.Ringer;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Sunrise.CartridgeLoader.Cartridges;
|
||||
|
||||
|
|
@ -116,11 +118,17 @@ public sealed partial class MessengerCartridgeSystem
|
|||
|
||||
userData.TryGetValue("job_title", out object? jobTitleObj);
|
||||
userData.TryGetValue("department_id", out object? departmentIdObj);
|
||||
userData.TryGetValue("job_icon_id", out object? jobIconIdObj);
|
||||
|
||||
var jobTitle = jobTitleObj?.ToString();
|
||||
var departmentId = departmentIdObj?.ToString();
|
||||
ProtoId<JobIconPrototype>? jobIconId = null;
|
||||
if (jobIconIdObj != null && !string.IsNullOrEmpty(jobIconIdObj.ToString()))
|
||||
{
|
||||
jobIconId = new ProtoId<JobIconPrototype>(jobIconIdObj.ToString()!);
|
||||
}
|
||||
|
||||
users.Add(new MessengerUser(userId, userName, jobTitle, departmentId));
|
||||
users.Add(new MessengerUser(userId, userName, jobTitle, departmentId, jobIconId));
|
||||
}
|
||||
|
||||
component.Users = users;
|
||||
|
|
@ -273,6 +281,7 @@ public sealed partial class MessengerCartridgeSystem
|
|||
messageData.TryGetValue("recipient_id", out object? recipientIdObj);
|
||||
messageData.TryGetValue("is_read", out object? isReadObj);
|
||||
messageData.TryGetValue("message_id", out object? messageIdObj);
|
||||
messageData.TryGetValue("sender_job_icon_id", out object? senderJobIconIdObj);
|
||||
|
||||
var groupId = groupIdObj?.ToString();
|
||||
var recipientId = recipientIdObj?.ToString();
|
||||
|
|
@ -289,8 +298,14 @@ public sealed partial class MessengerCartridgeSystem
|
|||
messageId = messageIdValue;
|
||||
}
|
||||
|
||||
ProtoId<JobIconPrototype>? senderJobIconId = null;
|
||||
if (senderJobIconIdObj != null && !string.IsNullOrEmpty(senderJobIconIdObj.ToString()))
|
||||
{
|
||||
senderJobIconId = new ProtoId<JobIconPrototype>(senderJobIconIdObj.ToString()!);
|
||||
}
|
||||
|
||||
var timestamp = TimeSpan.FromSeconds(timestampSeconds);
|
||||
messages.Add(new MessengerMessage(senderId, senderName, content, timestamp, groupId, recipientId, isRead, messageId));
|
||||
messages.Add(new MessengerMessage(senderId, senderName, content, timestamp, groupId, recipientId, isRead, messageId, senderJobIconId));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(chatId) && messages.Count >= 0)
|
||||
|
|
@ -403,6 +418,7 @@ public sealed partial class MessengerCartridgeSystem
|
|||
packet.Data.TryGetValue("recipient_id", out object? recipientIdObj);
|
||||
packet.Data.TryGetValue("is_read", out object? isReadObj);
|
||||
packet.Data.TryGetValue("message_id", out object? messageIdObj);
|
||||
packet.Data.TryGetValue("sender_job_icon_id", out object? senderJobIconIdObj);
|
||||
|
||||
var groupId = groupIdObj?.ToString();
|
||||
var recipientId = recipientIdObj?.ToString();
|
||||
|
|
@ -419,8 +435,14 @@ public sealed partial class MessengerCartridgeSystem
|
|||
messageId = messageIdValue;
|
||||
}
|
||||
|
||||
ProtoId<JobIconPrototype>? senderJobIconId = null;
|
||||
if (senderJobIconIdObj != null && !string.IsNullOrEmpty(senderJobIconIdObj.ToString()))
|
||||
{
|
||||
senderJobIconId = new ProtoId<JobIconPrototype>(senderJobIconIdObj.ToString()!);
|
||||
}
|
||||
|
||||
var timestamp = TimeSpan.FromSeconds(timestampSeconds);
|
||||
var message = new MessengerMessage(senderId, senderName, content, timestamp, groupId, recipientId, isRead, messageId);
|
||||
var message = new MessengerMessage(senderId, senderName, content, timestamp, groupId, recipientId, isRead, messageId, senderJobIconId);
|
||||
|
||||
string chatId;
|
||||
if (!string.IsNullOrEmpty(groupId))
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ public sealed partial class MessengerCartridgeSystem
|
|||
component.MutedPersonalChats,
|
||||
component.MutedGroupChats,
|
||||
unreadCounts,
|
||||
component.ActiveInvites
|
||||
component.ActiveInvites,
|
||||
component.PinnedChats
|
||||
);
|
||||
|
||||
_cartridgeLoader.UpdateCartridgeUiState(loaderUid, state);
|
||||
|
|
@ -84,4 +85,15 @@ public sealed partial class MessengerCartridgeSystem
|
|||
if (component.LoaderUid.HasValue)
|
||||
UpdateUiState(uid, component.LoaderUid.Value, component);
|
||||
}
|
||||
|
||||
private void TogglePin(EntityUid uid, MessengerCartridgeComponent component, string chatId)
|
||||
{
|
||||
if (component.PinnedChats.Contains(chatId))
|
||||
component.PinnedChats.Remove(chatId);
|
||||
else
|
||||
component.PinnedChats.Add(chatId);
|
||||
|
||||
if (component.LoaderUid.HasValue)
|
||||
UpdateUiState(uid, component.LoaderUid.Value, component);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ public sealed partial class MessengerServerSystem
|
|||
if (!component.Users.TryGetValue(args.SenderAddress, out var sender))
|
||||
return;
|
||||
|
||||
UpdateUserInfoFromPda(uid, component, args.SenderAddress, sender);
|
||||
|
||||
var timestamp = GetStationTime();
|
||||
|
||||
if (messageData.TryGetValue("group_id", out string? groupId) && !string.IsNullOrWhiteSpace(groupId))
|
||||
|
|
@ -40,7 +42,7 @@ public sealed partial class MessengerServerSystem
|
|||
return;
|
||||
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var message = new MessengerMessage(sender.UserId, sender.Name, content, timestamp, null, recipientId, isRead: false, messageId);
|
||||
var message = new MessengerMessage(sender.UserId, sender.Name, content, timestamp, null, recipientId, isRead: false, messageId, sender.JobIconId);
|
||||
var chatId = GetPersonalChatId(sender.UserId, recipientId);
|
||||
|
||||
if (!component.MessageHistory.TryGetValue(chatId, out var history))
|
||||
|
|
@ -88,7 +90,8 @@ public sealed partial class MessengerServerSystem
|
|||
["group_id"] = message.GroupId ?? string.Empty,
|
||||
["recipient_id"] = message.RecipientId ?? string.Empty,
|
||||
["is_read"] = message.IsRead,
|
||||
["message_id"] = message.MessageId
|
||||
["message_id"] = message.MessageId,
|
||||
["sender_job_icon_id"] = message.SenderJobIconId?.Id ?? string.Empty
|
||||
};
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
|
|
@ -136,7 +139,7 @@ public sealed partial class MessengerServerSystem
|
|||
return;
|
||||
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var message = new MessengerMessage(sender.UserId, sender.Name, content, timestamp, groupId, null, false, messageId);
|
||||
var message = new MessengerMessage(sender.UserId, sender.Name, content, timestamp, groupId, null, false, messageId, sender.JobIconId);
|
||||
|
||||
if (!component.MessageHistory.TryGetValue(groupId, out var history))
|
||||
{
|
||||
|
|
@ -185,7 +188,8 @@ public sealed partial class MessengerServerSystem
|
|||
["group_id"] = message.GroupId ?? string.Empty,
|
||||
["recipient_id"] = message.RecipientId ?? string.Empty,
|
||||
["is_read"] = message.IsRead,
|
||||
["message_id"] = message.MessageId
|
||||
["message_id"] = message.MessageId,
|
||||
["sender_job_icon_id"] = message.SenderJobIconId?.Id ?? string.Empty
|
||||
};
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ public sealed partial class MessengerServerSystem
|
|||
["user_id"] = user.UserId,
|
||||
["user_name"] = user.Name,
|
||||
["job_title"] = user.JobTitle ?? string.Empty,
|
||||
["department_id"] = user.DepartmentId ?? string.Empty
|
||||
["department_id"] = user.DepartmentId ?? string.Empty,
|
||||
["job_icon_id"] = user.JobIconId?.Id ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +209,8 @@ public sealed partial class MessengerServerSystem
|
|||
["group_id"] = msg.GroupId ?? string.Empty,
|
||||
["recipient_id"] = msg.RecipientId ?? string.Empty,
|
||||
["is_read"] = msg.IsRead,
|
||||
["message_id"] = msg.MessageId
|
||||
["message_id"] = msg.MessageId,
|
||||
["sender_job_icon_id"] = msg.SenderJobIconId?.Id ?? string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -250,7 +252,8 @@ public sealed partial class MessengerServerSystem
|
|||
["group_id"] = msg.GroupId ?? string.Empty,
|
||||
["recipient_id"] = msg.RecipientId ?? string.Empty,
|
||||
["is_read"] = msg.IsRead,
|
||||
["message_id"] = msg.MessageId
|
||||
["message_id"] = msg.MessageId,
|
||||
["sender_job_icon_id"] = msg.SenderJobIconId?.Id ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -298,7 +301,8 @@ public sealed partial class MessengerServerSystem
|
|||
["group_id"] = message.GroupId ?? string.Empty,
|
||||
["recipient_id"] = message.RecipientId ?? string.Empty,
|
||||
["is_read"] = message.IsRead,
|
||||
["message_id"] = message.MessageId
|
||||
["message_id"] = message.MessageId,
|
||||
["sender_job_icon_id"] = message.SenderJobIconId?.Id ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ using Content.Shared.Access.Components;
|
|||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.CartridgeLoader;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Sunrise.Messenger;
|
||||
|
||||
|
|
@ -133,6 +135,7 @@ public sealed partial class MessengerServerSystem
|
|||
|
||||
string? jobTitle = null;
|
||||
string? departmentId = null;
|
||||
ProtoId<JobIconPrototype>? jobIconId = null;
|
||||
|
||||
if (pda.ContainedId != null && TryComp<IdCardComponent>(pda.ContainedId.Value, out var idCard))
|
||||
{
|
||||
|
|
@ -141,9 +144,10 @@ public sealed partial class MessengerServerSystem
|
|||
{
|
||||
departmentId = idCard.JobDepartments[0];
|
||||
}
|
||||
jobIconId = idCard.JobIcon;
|
||||
}
|
||||
|
||||
var user = new MessengerUser(userId, userName, jobTitle, departmentId);
|
||||
var user = new MessengerUser(userId, userName, jobTitle, departmentId, jobIconId);
|
||||
component.Users[userId] = user;
|
||||
|
||||
AddUserToAutoGroups(uid, component, userId, userName, departmentId);
|
||||
|
|
@ -171,7 +175,8 @@ public sealed partial class MessengerServerSystem
|
|||
["user_id"] = userId,
|
||||
["user_name"] = userName,
|
||||
["job_title"] = jobTitle ?? string.Empty,
|
||||
["department_id"] = departmentId ?? string.Empty
|
||||
["department_id"] = departmentId ?? string.Empty,
|
||||
["job_icon_id"] = jobIconId?.Id ?? string.Empty
|
||||
};
|
||||
|
||||
if (_deviceNetwork.IsAddressPresent(serverDevice.DeviceNetId, userId))
|
||||
|
|
@ -188,7 +193,8 @@ public sealed partial class MessengerServerSystem
|
|||
["user_id"] = u.UserId,
|
||||
["user_name"] = u.Name,
|
||||
["job_title"] = u.JobTitle ?? string.Empty,
|
||||
["department_id"] = u.DepartmentId ?? string.Empty
|
||||
["department_id"] = u.DepartmentId ?? string.Empty,
|
||||
["job_icon_id"] = u.JobIconId?.Id ?? string.Empty
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
|
|
@ -301,6 +307,7 @@ public sealed partial class MessengerServerSystem
|
|||
string? userName = null;
|
||||
string? jobTitle = null;
|
||||
string? departmentId = null;
|
||||
ProtoId<JobIconPrototype>? jobIconId = null;
|
||||
|
||||
if (pda.ContainedId != null && TryComp<IdCardComponent>(pda.ContainedId.Value, out var idCard))
|
||||
{
|
||||
|
|
@ -310,6 +317,7 @@ public sealed partial class MessengerServerSystem
|
|||
{
|
||||
departmentId = idCard.JobDepartments[0];
|
||||
}
|
||||
jobIconId = idCard.JobIcon;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(userName))
|
||||
|
|
@ -317,7 +325,7 @@ public sealed partial class MessengerServerSystem
|
|||
userName = pda.OwnerName ?? Loc.GetString("messenger-user-unknown");
|
||||
}
|
||||
|
||||
var user = new MessengerUser(userId, userName, jobTitle, departmentId);
|
||||
var user = new MessengerUser(userId, userName, jobTitle, departmentId, jobIconId);
|
||||
component.Users[userId] = user;
|
||||
|
||||
AddUserToAutoGroups(uid, component, userId, userName, departmentId);
|
||||
|
|
@ -345,7 +353,8 @@ public sealed partial class MessengerServerSystem
|
|||
["user_id"] = userId,
|
||||
["user_name"] = userName,
|
||||
["job_title"] = jobTitle ?? string.Empty,
|
||||
["department_id"] = departmentId ?? string.Empty
|
||||
["department_id"] = departmentId ?? string.Empty,
|
||||
["job_icon_id"] = jobIconId?.Id ?? string.Empty
|
||||
};
|
||||
|
||||
if (_deviceNetwork.IsAddressPresent(serverDevice.DeviceNetId, args.SenderAddress))
|
||||
|
|
@ -361,7 +370,8 @@ public sealed partial class MessengerServerSystem
|
|||
["user_id"] = u.UserId,
|
||||
["user_name"] = u.Name,
|
||||
["job_title"] = u.JobTitle ?? string.Empty,
|
||||
["department_id"] = u.DepartmentId ?? string.Empty
|
||||
["department_id"] = u.DepartmentId ?? string.Empty,
|
||||
["job_icon_id"] = u.JobIconId?.Id ?? string.Empty
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
|
|
@ -586,4 +596,89 @@ public sealed partial class MessengerServerSystem
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет информацию о пользователе из его PDA по адресу DeviceNetwork
|
||||
/// </summary>
|
||||
private void UpdateUserInfoFromPda(EntityUid uid, MessengerServerComponent component, string userId, MessengerUser user)
|
||||
{
|
||||
var pdaQuery = EntityQueryEnumerator<PdaComponent, DeviceNetworkComponent>();
|
||||
EntityUid? foundPda = null;
|
||||
|
||||
while (pdaQuery.MoveNext(out var pdaUid, out var pda, out var deviceNetwork))
|
||||
{
|
||||
if (deviceNetwork.Address == userId)
|
||||
{
|
||||
foundPda = pdaUid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPda == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<PdaComponent>(foundPda.Value, out var pdaComp))
|
||||
return;
|
||||
|
||||
string? jobTitle = null;
|
||||
string? departmentId = null;
|
||||
ProtoId<JobIconPrototype>? jobIconId = null;
|
||||
|
||||
if (pdaComp.ContainedId != null && TryComp<IdCardComponent>(pdaComp.ContainedId.Value, out var idCard))
|
||||
{
|
||||
jobTitle = idCard.LocalizedJobTitle;
|
||||
if (idCard.JobDepartments.Count > 0)
|
||||
{
|
||||
departmentId = idCard.JobDepartments[0];
|
||||
}
|
||||
jobIconId = idCard.JobIcon;
|
||||
}
|
||||
|
||||
var needsUpdate = user.JobTitle != jobTitle || user.DepartmentId != departmentId || user.JobIconId != jobIconId;
|
||||
|
||||
if (needsUpdate)
|
||||
{
|
||||
user.JobTitle = jobTitle;
|
||||
user.DepartmentId = departmentId;
|
||||
user.JobIconId = jobIconId;
|
||||
|
||||
// Отправляем обновленный список пользователей всем клиентам
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
var notifyPayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdUsersList,
|
||||
["users"] = component.Users.Values.Select(u => new Dictionary<string, object>
|
||||
{
|
||||
["user_id"] = u.UserId,
|
||||
["user_name"] = u.Name,
|
||||
["job_title"] = u.JobTitle ?? string.Empty,
|
||||
["department_id"] = u.DepartmentId ?? string.Empty,
|
||||
["job_icon_id"] = u.JobIconId?.Id ?? string.Empty
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
foreach (var existingUserId in component.Users.Keys)
|
||||
{
|
||||
if (_deviceNetwork.IsAddressPresent(serverDevice.DeviceNetId, existingUserId))
|
||||
{
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, existingUserId, notifyPayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, existingUserId, notifyPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,5 +59,6 @@ public enum MessengerUiAction
|
|||
AcceptInvite,
|
||||
DeclineInvite,
|
||||
LeaveGroup,
|
||||
DeleteMessage
|
||||
DeleteMessage,
|
||||
TogglePin
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@ public sealed class MessengerUiState : BoundUserInterfaceState
|
|||
/// </summary>
|
||||
public List<MessengerGroupInvite> ActiveInvites { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Закрепленные чаты (chatId)
|
||||
/// </summary>
|
||||
public HashSet<string> PinnedChats { get; }
|
||||
|
||||
public MessengerUiState(
|
||||
bool isRegistered,
|
||||
bool serverAvailable,
|
||||
|
|
@ -69,7 +74,8 @@ public sealed class MessengerUiState : BoundUserInterfaceState
|
|||
HashSet<string> mutedPersonalChats,
|
||||
HashSet<string> mutedGroupChats,
|
||||
Dictionary<string, int> unreadCounts,
|
||||
List<MessengerGroupInvite> activeInvites)
|
||||
List<MessengerGroupInvite> activeInvites,
|
||||
HashSet<string> pinnedChats)
|
||||
{
|
||||
IsRegistered = isRegistered;
|
||||
ServerAvailable = serverAvailable;
|
||||
|
|
@ -81,5 +87,6 @@ public sealed class MessengerUiState : BoundUserInterfaceState
|
|||
MutedGroupChats = mutedGroupChats;
|
||||
UnreadCounts = unreadCounts;
|
||||
ActiveInvites = activeInvites;
|
||||
PinnedChats = pinnedChats;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.Messenger;
|
||||
|
|
@ -48,7 +50,12 @@ public sealed class MessengerMessage
|
|||
/// </summary>
|
||||
public long MessageId { get; set; }
|
||||
|
||||
public MessengerMessage(string senderId, string senderName, string content, TimeSpan timestamp, string? groupId = null, string? recipientId = null, bool isRead = false, long messageId = 0)
|
||||
/// <summary>
|
||||
/// ID иконки роли отправителя (опционально)
|
||||
/// </summary>
|
||||
public ProtoId<JobIconPrototype>? SenderJobIconId { get; set; }
|
||||
|
||||
public MessengerMessage(string senderId, string senderName, string content, TimeSpan timestamp, string? groupId = null, string? recipientId = null, bool isRead = false, long messageId = 0, ProtoId<JobIconPrototype>? senderJobIconId = null)
|
||||
{
|
||||
SenderId = senderId;
|
||||
SenderName = senderName;
|
||||
|
|
@ -58,5 +65,6 @@ public sealed class MessengerMessage
|
|||
RecipientId = recipientId;
|
||||
IsRead = isRead;
|
||||
MessageId = messageId;
|
||||
SenderJobIconId = senderJobIconId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.Messenger;
|
||||
|
|
@ -28,11 +30,17 @@ public sealed class MessengerUser
|
|||
/// </summary>
|
||||
public string? DepartmentId { get; set; }
|
||||
|
||||
public MessengerUser(string userId, string name, string? jobTitle = null, string? departmentId = null)
|
||||
/// <summary>
|
||||
/// ID иконки роли пользователя (опционально)
|
||||
/// </summary>
|
||||
public ProtoId<JobIconPrototype>? JobIconId { get; set; }
|
||||
|
||||
public MessengerUser(string userId, string name, string? jobTitle = null, string? departmentId = null, ProtoId<JobIconPrototype>? jobIconId = null)
|
||||
{
|
||||
UserId = userId;
|
||||
Name = name;
|
||||
JobTitle = jobTitle;
|
||||
DepartmentId = departmentId;
|
||||
JobIconId = jobIconId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25300,5 +25300,27 @@
|
|||
\u0438\u044F \u0432 \u043B\u0438\u0447\u043D\u044B\u0445 \u0438 \u0433\u0440\
|
||||
\u0443\u043F\u043E\u0432\u044B\u0445 \u0447\u0430\u0442\u0430\u0445."
|
||||
type: Add
|
||||
id: 1640
|
||||
time: '2026-01-27T23:27:00.379706+00:00'
|
||||
- message: "\u0412 \u0447\u0430\u0442\u0430\u0445 \u0434\u0435\u043F\u0430\u0440\
|
||||
\u0442\u0430\u043C\u0435\u043D\u0442\u043E\u0432 \u0431\u043E\u043B\u044C\u0448\
|
||||
\u0435 \u043D\u0435 \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442 \u0441\u043E\
|
||||
\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043E\u0431 \u0434\u043E\u0431\u0430\
|
||||
\u0432\u043B\u0435\u043D\u0438\u0438 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\
|
||||
\u043A\u043E\u0432."
|
||||
type: Tweak
|
||||
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0430 \u0432\u043E\u0437\
|
||||
\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u0434\u043E\u0431\u0430\u0432\
|
||||
\u043B\u044F\u0442\u044C \u043B\u0438\u0447\u043D\u044B\u0435 \u0447\u0430\u0442\
|
||||
\u044B \u0438 \u0433\u0440\u0443\u043F\u043F\u044B \u0432 \u0438\u0437\u0431\
|
||||
\u0440\u0430\u043D\u043E\u0435 \u043F\u0440\u0438 \u043F\u043E\u043C\u043E\u0449\
|
||||
\u0438 \u041F\u041A\u041C."
|
||||
type: Tweak
|
||||
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u0432 \u0441\u043F\u0438\u0441\
|
||||
\u043A\u0435 \u043B\u0438\u0447\u043D\u044B\u0445 \u0447\u0430\u0442\u043E\u0432\
|
||||
, \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F\u0445 \u0438\
|
||||
\ \u0441\u043F\u0438\u0441\u043A\u0435 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\
|
||||
\u043A\u043E\u0432 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442\
|
||||
\u0441\u044F \u0438\u043A\u043E\u043D\u043A\u0430 \u0441 \u0434\u043E\u043B\u0436\
|
||||
\u043D\u043E\u0441\u0442\u044C\u044E."
|
||||
type: Tweak
|
||||
id: 1641
|
||||
time: '2026-01-28T01:51:45.486583+00:00'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue