Доработка мессенжера
This commit is contained in:
parent
d4989a6645
commit
1fabbbb504
19 changed files with 1526 additions and 84 deletions
|
|
@ -1,5 +1,6 @@
|
|||
using Content.Client._Sunrise.Messenger;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared._Sunrise.Messenger;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
|
|
@ -21,6 +22,11 @@ public sealed partial class MessagePanel : PanelContainer
|
|||
private ClientEmojiSystem? EmojiSystem => _entitySystemManager.GetEntitySystemOrNull<ClientEmojiSystem>();
|
||||
private SpriteSystem GetSpriteSystem() => _entitySystemManager.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
/// <summary>
|
||||
/// ID сообщения для идентификации при обновлении
|
||||
/// </summary>
|
||||
public long MessageId { get; private set; }
|
||||
|
||||
private static readonly Type[] MessageTagsAllowed =
|
||||
[
|
||||
typeof(BoldItalicTag),
|
||||
|
|
@ -38,14 +44,38 @@ public sealed partial class MessagePanel : PanelContainer
|
|||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public event Action<long>? OnDeleteMessage;
|
||||
private Button? _deleteButton;
|
||||
|
||||
public void UpdateMessage(MessengerMessage message, bool isOwnMessage, bool isPersonalChat, string? currentUserId)
|
||||
{
|
||||
MessageId = message.MessageId;
|
||||
SenderLabel.Text = message.SenderName;
|
||||
TimeLabel.Text = message.Timestamp.ToString(@"hh\:mm");
|
||||
|
||||
var parsedContent = EmojiSystem?.ParseEmojis(message.Content) ?? message.Content;
|
||||
ContentLabel.SetMessage(FormattedMessage.FromMarkupPermissive(parsedContent), MessageTagsAllowed);
|
||||
|
||||
if (isOwnMessage && _deleteButton == null)
|
||||
{
|
||||
_deleteButton = new Button
|
||||
{
|
||||
Text = "✖",
|
||||
SetWidth = 24,
|
||||
SetHeight = 24,
|
||||
ToolTip = Loc.GetString("messenger-delete-message"),
|
||||
Margin = new Thickness(2, 0)
|
||||
};
|
||||
_deleteButton.AddStyleClass(StyleClass.ButtonSmall);
|
||||
_deleteButton.OnPressed += _ => OnDeleteMessage?.Invoke(MessageId);
|
||||
RightContainer.AddChild(_deleteButton);
|
||||
}
|
||||
else if (!isOwnMessage && _deleteButton != null)
|
||||
{
|
||||
RightContainer.RemoveChild(_deleteButton);
|
||||
_deleteButton = null;
|
||||
}
|
||||
|
||||
var roundedButtonTex = _resourceCache.GetTexture("/Textures/Interface/Nano/rounded_button_bordered.svg.96dpi.png");
|
||||
var roundedStyleBox = new StyleBoxTexture
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ public sealed partial class MessengerUi : UIFragment
|
|||
SendMessengerMessage(MessengerUiAction.RequestMessages, userInterface, chatId: chatId);
|
||||
_fragment.OnToggleMute += (chatId, isMuted) =>
|
||||
SendMessengerMessage(MessengerUiAction.ToggleMute, userInterface, chatId: chatId, isMuted: isMuted);
|
||||
_fragment.OnAcceptInvite += (groupId) =>
|
||||
SendMessengerMessage(MessengerUiAction.AcceptInvite, userInterface, groupId: groupId);
|
||||
_fragment.OnDeclineInvite += (groupId) =>
|
||||
SendMessengerMessage(MessengerUiAction.DeclineInvite, userInterface, groupId: groupId);
|
||||
_fragment.OnLeaveGroup += (groupId) =>
|
||||
SendMessengerMessage(MessengerUiAction.LeaveGroup, userInterface, groupId: groupId);
|
||||
_fragment.OnDeleteMessage += (chatId, messageId) =>
|
||||
SendMessengerMessage(MessengerUiAction.DeleteMessage, userInterface, chatId: chatId, messageId: messageId);
|
||||
}
|
||||
|
||||
public override void UpdateState(BoundUserInterfaceState state)
|
||||
|
|
@ -48,9 +56,10 @@ public sealed partial class MessengerUi : UIFragment
|
|||
string? groupName = null,
|
||||
string? userId = null,
|
||||
string? chatId = null,
|
||||
bool? isMuted = null)
|
||||
bool? isMuted = null,
|
||||
long? messageId = null)
|
||||
{
|
||||
var messengerMessage = new MessengerUiMessageEvent(action, recipientId, groupId, content, groupName, userId, chatId, isMuted);
|
||||
var messengerMessage = new MessengerUiMessageEvent(action, recipientId, groupId, content, groupName, userId, chatId, isMuted, messageId);
|
||||
var message = new CartridgeUiMessage(messengerMessage);
|
||||
userInterface.SendMessage(message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Orientation="Vertical"
|
||||
SetWidth="200"
|
||||
SetWidth="220"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
HorizontalExpand="True"
|
||||
|
|
@ -30,16 +30,27 @@
|
|||
PlaceHolder="{Loc 'messenger-search-placeholder'}" />
|
||||
<BoxContainer
|
||||
HorizontalExpand="True"
|
||||
Orientation="Horizontal"
|
||||
Orientation="Vertical"
|
||||
Margin="4,2">
|
||||
<Button
|
||||
Name="PersonalChatsTab"
|
||||
Text="{Loc 'messenger-tab-personal'}"
|
||||
<BoxContainer
|
||||
HorizontalExpand="True"
|
||||
ToggleMode="True" />
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Name="PersonalChatsTab"
|
||||
Text="{Loc 'messenger-tab-personal'}"
|
||||
HorizontalExpand="True"
|
||||
SizeFlagsStretchRatio="1"
|
||||
ToggleMode="True" />
|
||||
<Button
|
||||
Name="GroupChatsTab"
|
||||
Text="{Loc 'messenger-tab-groups'}"
|
||||
HorizontalExpand="True"
|
||||
SizeFlagsStretchRatio="1"
|
||||
ToggleMode="True" />
|
||||
</BoxContainer>
|
||||
<Button
|
||||
Name="GroupChatsTab"
|
||||
Text="{Loc 'messenger-tab-groups'}"
|
||||
Name="InvitesTab"
|
||||
Text="{Loc 'messenger-tab-invites'}"
|
||||
HorizontalExpand="True"
|
||||
ToggleMode="True" />
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
public event Action<string, string>? OnRemoveFromGroup;
|
||||
public event Action<string>? OnRequestMessages;
|
||||
public event Action<string, bool>? OnToggleMute;
|
||||
public event Action<string>? OnAcceptInvite;
|
||||
public event Action<string>? OnDeclineInvite;
|
||||
public event Action<string>? OnLeaveGroup;
|
||||
public event Action<string, long>? OnDeleteMessage;
|
||||
|
||||
private string? _currentChatId;
|
||||
private MessengerUiState? _currentState;
|
||||
|
|
@ -40,6 +44,11 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
private int _lastMessageCount;
|
||||
private bool _isScrolledToBottom = true;
|
||||
|
||||
/// <summary>
|
||||
/// Максимальное количество сообщений, отображаемых в UI (для оптимизации производительности)
|
||||
/// </summary>
|
||||
private const int MaxDisplayedMessages = 100;
|
||||
|
||||
private Dictionary<string, int>? _lastUnreadCounts;
|
||||
private HashSet<string>? _lastUserIds;
|
||||
private HashSet<string>? _lastGroupIds;
|
||||
|
|
@ -88,6 +97,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
SearchInput.OnTextChanged += OnSearchTextChanged;
|
||||
PersonalChatsTab.OnPressed += _ => SwitchTab(0);
|
||||
GroupChatsTab.OnPressed += _ => SwitchTab(1);
|
||||
InvitesTab.OnPressed += _ => SwitchTab(2);
|
||||
MuteCheckBox.OnToggled += OnMuteToggled;
|
||||
|
||||
PersonalChatsTab.Pressed = true;
|
||||
|
|
@ -207,6 +217,11 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
.ThenBy(m => m.Content)
|
||||
.ToList();
|
||||
|
||||
if (messages.Count > MaxDisplayedMessages)
|
||||
{
|
||||
messages = messages.Skip(messages.Count - MaxDisplayedMessages).ToList();
|
||||
}
|
||||
|
||||
var wasChatChanged = savedChatId != _currentChatId;
|
||||
|
||||
if (wasChatChanged)
|
||||
|
|
@ -288,6 +303,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
}
|
||||
|
||||
UpdateChatsList(state);
|
||||
UpdateTabButtons(state);
|
||||
|
||||
if (_showMembersList && _currentGroupId != null)
|
||||
{
|
||||
|
|
@ -295,15 +311,158 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateTabButtons(MessengerUiState state)
|
||||
{
|
||||
int personalUnreadChats = 0;
|
||||
int groupsUnreadChats = 0;
|
||||
int invitesCount = 0;
|
||||
|
||||
if (state.CurrentUserId != null)
|
||||
{
|
||||
foreach (var user in state.Users)
|
||||
{
|
||||
if (user.UserId == state.CurrentUserId)
|
||||
continue;
|
||||
|
||||
var chatId = GetPersonalChatId(user.UserId);
|
||||
if (state.UnreadCounts.TryGetValue(chatId, out var count) && count > 0)
|
||||
{
|
||||
personalUnreadChats++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var group in state.Groups)
|
||||
{
|
||||
if (state.UnreadCounts.TryGetValue(group.GroupId, out var count) && count > 0)
|
||||
{
|
||||
groupsUnreadChats++;
|
||||
}
|
||||
}
|
||||
|
||||
if (state.ActiveInvites != null)
|
||||
{
|
||||
invitesCount = state.ActiveInvites.Count;
|
||||
}
|
||||
|
||||
UpdateTabButton(PersonalChatsTab, personalUnreadChats);
|
||||
UpdateTabButton(GroupChatsTab, groupsUnreadChats);
|
||||
UpdateTabButton(InvitesTab, invitesCount);
|
||||
}
|
||||
|
||||
private void UpdateTabButton(Button button, int count)
|
||||
{
|
||||
BoxContainer? textContainer;
|
||||
Label? nameLabel;
|
||||
var isInvitesTab = button == InvitesTab;
|
||||
|
||||
if (button.ChildCount == 0)
|
||||
{
|
||||
textContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
nameLabel = new Label
|
||||
{
|
||||
Text = button.Text ?? "",
|
||||
HorizontalExpand = false,
|
||||
Align = isInvitesTab ? Label.AlignMode.Center : Label.AlignMode.Left
|
||||
};
|
||||
textContainer.AddChild(nameLabel);
|
||||
button.AddChild(textContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
textContainer = button.Children.FirstOrDefault() as BoxContainer;
|
||||
if (textContainer == null)
|
||||
{
|
||||
textContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
nameLabel = new Label
|
||||
{
|
||||
Text = button.Text ?? "",
|
||||
HorizontalExpand = false,
|
||||
Align = isInvitesTab ? Label.AlignMode.Center : Label.AlignMode.Left
|
||||
};
|
||||
textContainer.AddChild(nameLabel);
|
||||
button.RemoveAllChildren();
|
||||
button.AddChild(textContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
nameLabel = textContainer.Children.OfType<Label>().FirstOrDefault(l => !l.StyleClasses.Contains("Bold"));
|
||||
if (nameLabel == null)
|
||||
{
|
||||
nameLabel = new Label
|
||||
{
|
||||
Text = button.Text ?? "",
|
||||
HorizontalExpand = false,
|
||||
Align = isInvitesTab ? Label.AlignMode.Center : Label.AlignMode.Left
|
||||
};
|
||||
textContainer.AddChild(nameLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
nameLabel.HorizontalExpand = false;
|
||||
nameLabel.Align = isInvitesTab ? Label.AlignMode.Center : Label.AlignMode.Left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textContainer == null || nameLabel == null)
|
||||
return;
|
||||
|
||||
var existingCounter = textContainer.Children.OfType<Label>()
|
||||
.FirstOrDefault(l => l != nameLabel && l.StyleClasses.Contains("Bold"));
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
button.ModulateSelfOverride = Color.Red;
|
||||
|
||||
if (existingCounter == null)
|
||||
{
|
||||
var counterLabel = new Label
|
||||
{
|
||||
Text = $"({count})",
|
||||
Align = Label.AlignMode.Left,
|
||||
StyleClasses = { "Bold" },
|
||||
HorizontalExpand = false
|
||||
};
|
||||
counterLabel.ModulateSelfOverride = Color.White;
|
||||
textContainer.AddChild(counterLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingCounter.Text = $"({count})";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
button.ModulateSelfOverride = null;
|
||||
|
||||
if (existingCounter != null)
|
||||
{
|
||||
textContainer.RemoveChild(existingCounter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SwitchTab(int tabIndex)
|
||||
{
|
||||
_activeTab = tabIndex;
|
||||
PersonalChatsTab.Pressed = tabIndex == 0;
|
||||
GroupChatsTab.Pressed = tabIndex == 1;
|
||||
InvitesTab.Pressed = tabIndex == 2;
|
||||
CreateGroupButton.Visible = tabIndex == 1;
|
||||
if (_currentState != null)
|
||||
{
|
||||
UpdateChatsList(_currentState);
|
||||
UpdateTabButtons(_currentState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -444,7 +603,7 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
ChatsContainer.AddChild(buttonContainer);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (_activeTab == 1)
|
||||
{
|
||||
foreach (var group in state.Groups)
|
||||
{
|
||||
|
|
@ -510,6 +669,94 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
ChatsContainer.AddChild(groupContainer);
|
||||
}
|
||||
}
|
||||
else if (_activeTab == 2)
|
||||
{
|
||||
if (state.ActiveInvites.Count > 0)
|
||||
{
|
||||
foreach (var invite in state.ActiveInvites)
|
||||
{
|
||||
var invitePanel = new PanelContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(2, 2)
|
||||
};
|
||||
invitePanel.PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = Color.FromHex("#25252A"),
|
||||
BorderColor = Color.FromHex("#3D4059"),
|
||||
BorderThickness = new Thickness(1),
|
||||
ContentMarginLeftOverride = 6,
|
||||
ContentMarginRightOverride = 6,
|
||||
ContentMarginTopOverride = 4,
|
||||
ContentMarginBottomOverride = 4
|
||||
};
|
||||
|
||||
var inviteContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
var groupNameLabel = new Label
|
||||
{
|
||||
Text = invite.GroupName,
|
||||
StyleClasses = { "Bold" },
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 0, 0, 4),
|
||||
ClipText = true
|
||||
};
|
||||
inviteContainer.AddChild(groupNameLabel);
|
||||
|
||||
var inviteInfo = new RichTextLabel
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 0, 0, 8)
|
||||
};
|
||||
inviteInfo.SetMessage(Loc.GetString("messenger-invite-info", ("inviter", invite.InviterName)), tagsAllowed: null);
|
||||
inviteInfo.StyleClasses.Add("LabelSubText");
|
||||
inviteContainer.AddChild(inviteInfo);
|
||||
|
||||
var buttonsContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
|
||||
var acceptButton = new Button
|
||||
{
|
||||
Text = "✓",
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(2, 0)
|
||||
};
|
||||
acceptButton.OnPressed += _ => OnAcceptInvite?.Invoke(invite.GroupId);
|
||||
buttonsContainer.AddChild(acceptButton);
|
||||
|
||||
var declineButton = new Button
|
||||
{
|
||||
Text = "✖",
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(2, 0)
|
||||
};
|
||||
declineButton.OnPressed += _ => OnDeclineInvite?.Invoke(invite.GroupId);
|
||||
buttonsContainer.AddChild(declineButton);
|
||||
|
||||
inviteContainer.AddChild(buttonsContainer);
|
||||
invitePanel.AddChild(inviteContainer);
|
||||
ChatsContainer.AddChild(invitePanel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var noInvitesLabel = new Label
|
||||
{
|
||||
Text = Loc.GetString("messenger-no-invites"),
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Margin = new Thickness(4)
|
||||
};
|
||||
ChatsContainer.AddChild(noInvitesLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool AreUnreadCountsEqual(Dictionary<string, int> counts1, Dictionary<string, int> counts2)
|
||||
|
|
@ -789,31 +1036,86 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
MembersList.AddChild(noMembersLabel);
|
||||
}
|
||||
|
||||
if (isUserGroup && isOwner && _currentState != null && _currentState.ServerAvailable && _currentState.IsRegistered)
|
||||
if (isUserGroup && _currentState != null && _currentState.ServerAvailable && _currentState.IsRegistered)
|
||||
{
|
||||
AddMemberButton.Disabled = false;
|
||||
AddMemberButton.Visible = true;
|
||||
AddMemberButton.OnPressed -= OnAddMemberButtonPressed;
|
||||
AddMemberButton.OnPressed += OnAddMemberButtonPressed;
|
||||
var buttonsContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(2, 4)
|
||||
};
|
||||
|
||||
if (group.Type == MessengerGroupType.UserCreated && isOwner)
|
||||
{
|
||||
if (AddMemberButton.Parent != null)
|
||||
{
|
||||
AddMemberButton.Parent.RemoveChild(AddMemberButton);
|
||||
}
|
||||
AddMemberButton.Disabled = false;
|
||||
AddMemberButton.Visible = true;
|
||||
AddMemberButton.Text = Loc.GetString("messenger-invite-member-button");
|
||||
AddMemberButton.HorizontalExpand = true;
|
||||
AddMemberButton.Margin = new Thickness(2, 0);
|
||||
AddMemberButton.OnPressed -= OnAddMemberButtonPressed;
|
||||
AddMemberButton.OnPressed += OnAddMemberButtonPressed;
|
||||
buttonsContainer.AddChild(AddMemberButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMemberButton.Disabled = true;
|
||||
AddMemberButton.Visible = false;
|
||||
}
|
||||
|
||||
var leaveButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("messenger-leave-group"),
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(2, 0)
|
||||
};
|
||||
leaveButton.OnPressed += _ =>
|
||||
{
|
||||
if (_currentGroupId != null)
|
||||
{
|
||||
OnLeaveGroup?.Invoke(_currentGroupId);
|
||||
}
|
||||
};
|
||||
buttonsContainer.AddChild(leaveButton);
|
||||
|
||||
MembersList.AddChild(buttonsContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMemberButton.Disabled = true;
|
||||
AddMemberButton.Visible = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateMessages(List<MessengerMessage> messages)
|
||||
{
|
||||
if (messages.Count > MaxDisplayedMessages)
|
||||
{
|
||||
messages = messages.Skip(messages.Count - MaxDisplayedMessages).ToList();
|
||||
}
|
||||
|
||||
var hasNewMessages = messages.Count > _lastMessageCount;
|
||||
|
||||
UpdateScrollBottomState();
|
||||
var wasAtBottom = _isScrolledToBottom;
|
||||
|
||||
|
||||
if (_lastMessageCount == 0)
|
||||
if (_lastMessageCount == 0 || messages.Count < _lastMessageCount)
|
||||
{
|
||||
MessagesList.RemoveAllChildren();
|
||||
_lastMessageCount = 0;
|
||||
}
|
||||
else if (messages.Count > 0 && _lastMessageCount > 0)
|
||||
{
|
||||
var firstDisplayedId = GetFirstDisplayedMessageId();
|
||||
if (firstDisplayedId.HasValue && messages[0].MessageId != firstDisplayedId.Value)
|
||||
{
|
||||
MessagesList.RemoveAllChildren();
|
||||
_lastMessageCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var hasStatusChange = false;
|
||||
|
|
@ -858,17 +1160,53 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
_lastMessageCount = 0;
|
||||
}
|
||||
|
||||
var existingMessageIds = new HashSet<long>();
|
||||
foreach (var child in MessagesList.Children)
|
||||
{
|
||||
if (child is MessagePanel panel)
|
||||
{
|
||||
existingMessageIds.Add(panel.MessageId);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = _lastMessageCount; i < messages.Count; i++)
|
||||
{
|
||||
var message = messages[i];
|
||||
if (existingMessageIds.Contains(message.MessageId))
|
||||
continue;
|
||||
|
||||
var isOwnMessage = _currentState?.CurrentUserId == message.SenderId;
|
||||
var isPersonalChat = _currentChatId != null && _currentChatId.StartsWith("personal_");
|
||||
|
||||
var messagePanel = new MessagePanel();
|
||||
messagePanel.UpdateMessage(message, isOwnMessage, isPersonalChat, _currentState?.CurrentUserId);
|
||||
if (isOwnMessage)
|
||||
{
|
||||
messagePanel.OnDeleteMessage += (messageId) =>
|
||||
{
|
||||
if (_currentChatId != null)
|
||||
{
|
||||
OnDeleteMessage?.Invoke(_currentChatId, messageId);
|
||||
}
|
||||
};
|
||||
}
|
||||
MessagesList.AddChild(messagePanel);
|
||||
}
|
||||
|
||||
var messageIds = new HashSet<long>(messages.Select(m => m.MessageId));
|
||||
var toRemove = new List<Control>();
|
||||
foreach (var child in MessagesList.Children)
|
||||
{
|
||||
if (child is MessagePanel panel && !messageIds.Contains(panel.MessageId))
|
||||
{
|
||||
toRemove.Add(child);
|
||||
}
|
||||
}
|
||||
foreach (var child in toRemove)
|
||||
{
|
||||
MessagesList.RemoveChild(child);
|
||||
}
|
||||
|
||||
var previousMessageCount = _lastMessageCount;
|
||||
_lastMessageCount = messages.Count;
|
||||
|
||||
|
|
@ -895,6 +1233,21 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
UpdateScrollBottomState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает ID первого отображаемого сообщения для проверки необходимости полной перерисовки
|
||||
/// </summary>
|
||||
private long? GetFirstDisplayedMessageId()
|
||||
{
|
||||
if (MessagesList.ChildCount == 0)
|
||||
return null;
|
||||
|
||||
var firstChild = MessagesList.Children.FirstOrDefault();
|
||||
if (firstChild is not MessagePanel firstPanel)
|
||||
return null;
|
||||
|
||||
return firstPanel.MessageId;
|
||||
}
|
||||
|
||||
private void SendMessage()
|
||||
{
|
||||
var messageText = MessageInput.Text;
|
||||
|
|
@ -1023,7 +1376,8 @@ public sealed partial class MessengerUiFragment : BoxContainer
|
|||
}
|
||||
|
||||
var dialog = new AddUserDialog();
|
||||
dialog.SetTitle(Loc.GetString("messenger-add-user-to-group-title", ("groupName", groupName)));
|
||||
var dialogTitle = Loc.GetString("messenger-invite-user-to-group-title", ("groupName", groupName));
|
||||
dialog.SetTitle(dialogTitle);
|
||||
_addUserDialog = dialog;
|
||||
|
||||
dialog.SetAvailableUsers(availableUsers);
|
||||
|
|
|
|||
|
|
@ -97,4 +97,10 @@ public sealed partial class MessengerCartridgeComponent : Component
|
|||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Dictionary<string, int> ServerUnreadCounts = new();
|
||||
|
||||
/// <summary>
|
||||
/// Активные приглашения в группы
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public List<MessengerGroupInvite> ActiveInvites = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,22 @@ public sealed partial class MessengerCartridgeSystem
|
|||
if (message.ChatId != null && message.IsMuted.HasValue)
|
||||
ToggleMute(uid, component, message.ChatId, message.IsMuted.Value);
|
||||
break;
|
||||
case MessengerUiAction.AcceptInvite:
|
||||
if (message.GroupId != null)
|
||||
AcceptInvite(uid, component, loaderUid, deviceNetwork, message.GroupId);
|
||||
break;
|
||||
case MessengerUiAction.DeclineInvite:
|
||||
if (message.GroupId != null)
|
||||
DeclineInvite(uid, component, loaderUid, deviceNetwork, message.GroupId);
|
||||
break;
|
||||
case MessengerUiAction.LeaveGroup:
|
||||
if (message.GroupId != null)
|
||||
LeaveGroup(uid, component, loaderUid, deviceNetwork, message.GroupId);
|
||||
break;
|
||||
case MessengerUiAction.DeleteMessage:
|
||||
if (message.ChatId != null && message.MessageId.HasValue)
|
||||
DeleteMessage(uid, component, loaderUid, deviceNetwork, message.ChatId, message.MessageId.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,4 +323,149 @@ public sealed partial class MessengerCartridgeSystem
|
|||
_deviceNetwork.QueuePacket(loaderUid, component.ServerAddress, payload, frequency: messengerFreq, network: pdaDevice.DeviceNetId);
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
}
|
||||
|
||||
private void AcceptInvite(EntityUid uid, MessengerCartridgeComponent component, EntityUid loaderUid, DeviceNetworkComponent deviceNetwork, string groupId)
|
||||
{
|
||||
if (component.ServerAddress == null || !component.IsRegistered)
|
||||
return;
|
||||
|
||||
var messengerFreq = GetMessengerFrequency();
|
||||
if (!messengerFreq.HasValue)
|
||||
return;
|
||||
|
||||
SetMessengerFrequency(loaderUid, deviceNetwork, out var originalFreq);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(loaderUid, out var pdaDevice))
|
||||
{
|
||||
RestoreFrequency(loaderUid, deviceNetwork, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_deviceNetwork.IsAddressPresent(pdaDevice.DeviceNetId, component.ServerAddress))
|
||||
{
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdAcceptInvite,
|
||||
[MessengerCommands.CmdAcceptInvite] = new NetworkPayload
|
||||
{
|
||||
["group_id"] = groupId
|
||||
}
|
||||
};
|
||||
|
||||
_deviceNetwork.QueuePacket(loaderUid, component.ServerAddress, payload, frequency: messengerFreq, network: pdaDevice.DeviceNetId);
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
}
|
||||
|
||||
private void DeclineInvite(EntityUid uid, MessengerCartridgeComponent component, EntityUid loaderUid, DeviceNetworkComponent deviceNetwork, string groupId)
|
||||
{
|
||||
if (component.ServerAddress == null || !component.IsRegistered)
|
||||
return;
|
||||
|
||||
var messengerFreq = GetMessengerFrequency();
|
||||
if (!messengerFreq.HasValue)
|
||||
return;
|
||||
|
||||
SetMessengerFrequency(loaderUid, deviceNetwork, out var originalFreq);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(loaderUid, out var pdaDevice))
|
||||
{
|
||||
RestoreFrequency(loaderUid, deviceNetwork, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_deviceNetwork.IsAddressPresent(pdaDevice.DeviceNetId, component.ServerAddress))
|
||||
{
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdDeclineInvite,
|
||||
[MessengerCommands.CmdDeclineInvite] = new NetworkPayload
|
||||
{
|
||||
["group_id"] = groupId
|
||||
}
|
||||
};
|
||||
|
||||
_deviceNetwork.QueuePacket(loaderUid, component.ServerAddress, payload, frequency: messengerFreq, network: pdaDevice.DeviceNetId);
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
}
|
||||
|
||||
private void LeaveGroup(EntityUid uid, MessengerCartridgeComponent component, EntityUid loaderUid, DeviceNetworkComponent deviceNetwork, string groupId)
|
||||
{
|
||||
if (component.ServerAddress == null || !component.IsRegistered)
|
||||
return;
|
||||
|
||||
var messengerFreq = GetMessengerFrequency();
|
||||
if (!messengerFreq.HasValue)
|
||||
return;
|
||||
|
||||
SetMessengerFrequency(loaderUid, deviceNetwork, out var originalFreq);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(loaderUid, out var pdaDevice))
|
||||
{
|
||||
RestoreFrequency(loaderUid, deviceNetwork, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_deviceNetwork.IsAddressPresent(pdaDevice.DeviceNetId, component.ServerAddress))
|
||||
{
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdLeaveGroup,
|
||||
[MessengerCommands.CmdLeaveGroup] = new NetworkPayload
|
||||
{
|
||||
["group_id"] = groupId
|
||||
}
|
||||
};
|
||||
|
||||
_deviceNetwork.QueuePacket(loaderUid, component.ServerAddress, payload, frequency: messengerFreq, network: pdaDevice.DeviceNetId);
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
}
|
||||
|
||||
private void DeleteMessage(EntityUid uid, MessengerCartridgeComponent component, EntityUid loaderUid, DeviceNetworkComponent deviceNetwork, string chatId, long messageId)
|
||||
{
|
||||
if (component.ServerAddress == null || !component.IsRegistered)
|
||||
return;
|
||||
|
||||
var messengerFreq = GetMessengerFrequency();
|
||||
if (!messengerFreq.HasValue)
|
||||
return;
|
||||
|
||||
SetMessengerFrequency(loaderUid, deviceNetwork, out var originalFreq);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(loaderUid, out var pdaDevice))
|
||||
{
|
||||
RestoreFrequency(loaderUid, deviceNetwork, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_deviceNetwork.IsAddressPresent(pdaDevice.DeviceNetId, component.ServerAddress))
|
||||
{
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdDeleteMessage,
|
||||
[MessengerCommands.CmdDeleteMessage] = new NetworkPayload
|
||||
{
|
||||
["message_id"] = messageId,
|
||||
["chat_id"] = chatId
|
||||
}
|
||||
};
|
||||
|
||||
_deviceNetwork.QueuePacket(loaderUid, component.ServerAddress, payload, frequency: messengerFreq, network: pdaDevice.DeviceNetId);
|
||||
RestoreFrequency(loaderUid, pdaDevice, originalFreq);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,21 @@ public sealed partial class MessengerCartridgeSystem
|
|||
case MessengerCommands.CmdUserAddedToGroup:
|
||||
HandleUserAddedToGroup(uid, component, packet, loaderUid);
|
||||
break;
|
||||
case MessengerCommands.CmdInviteReceived:
|
||||
HandleInviteReceived(uid, component, packet, loaderUid);
|
||||
break;
|
||||
case MessengerCommands.CmdInvitesList:
|
||||
HandleInvitesList(uid, component, packet, loaderUid);
|
||||
break;
|
||||
case MessengerCommands.CmdUserLeftGroup:
|
||||
HandleUserLeftGroup(uid, component, packet, loaderUid);
|
||||
break;
|
||||
case MessengerCommands.CmdGroupDeleted:
|
||||
HandleGroupDeleted(uid, component, packet, loaderUid);
|
||||
break;
|
||||
case MessengerCommands.CmdMessageDeleted:
|
||||
HandleMessageDeleted(uid, component, packet, loaderUid);
|
||||
break;
|
||||
default:
|
||||
Sawmill.Warning($"Unknown command received: {command}");
|
||||
break;
|
||||
|
|
@ -181,6 +196,17 @@ public sealed partial class MessengerCartridgeSystem
|
|||
|
||||
component.Groups = groups;
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
if (group.Type == MessengerGroupType.Automatic && group.AutoGroupPrototypeId != null)
|
||||
{
|
||||
if (!component.MutedGroupChats.Contains(group.GroupId))
|
||||
{
|
||||
component.MutedGroupChats.Add(group.GroupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverUnreadCounts != null)
|
||||
{
|
||||
foreach (var (chatId, count) in serverUnreadCounts)
|
||||
|
|
@ -494,6 +520,82 @@ public sealed partial class MessengerCartridgeSystem
|
|||
}
|
||||
}
|
||||
|
||||
private void HandleInviteReceived(EntityUid uid, MessengerCartridgeComponent component, DeviceNetworkPacketEvent packet, EntityUid loaderUid)
|
||||
{
|
||||
if (!packet.Data.TryGetValue("group_id", out object? groupIdObj) ||
|
||||
!packet.Data.TryGetValue("group_name", out object? groupNameObj) ||
|
||||
!packet.Data.TryGetValue("inviter_id", out object? inviterIdObj) ||
|
||||
!packet.Data.TryGetValue("inviter_name", out object? inviterNameObj) ||
|
||||
!packet.Data.TryGetValue("created_at", out object? createdAtObj))
|
||||
return;
|
||||
|
||||
var groupId = groupIdObj?.ToString();
|
||||
var groupName = groupNameObj?.ToString();
|
||||
var inviterId = inviterIdObj?.ToString();
|
||||
var inviterName = inviterNameObj?.ToString();
|
||||
|
||||
if (groupId == null || groupName == null || inviterId == null || inviterName == null)
|
||||
return;
|
||||
|
||||
if (!double.TryParse(createdAtObj?.ToString(), out var createdAtSeconds))
|
||||
return;
|
||||
|
||||
var createdAt = TimeSpan.FromSeconds(createdAtSeconds);
|
||||
|
||||
var invite = new MessengerGroupInvite(groupId, groupName, inviterId, inviterName, component.UserId ?? string.Empty, createdAt);
|
||||
|
||||
var isNewInvite = !component.ActiveInvites.Any(inv => inv.GroupId == groupId && inv.InviterId == inviterId);
|
||||
|
||||
if (isNewInvite)
|
||||
{
|
||||
component.ActiveInvites.Add(invite);
|
||||
|
||||
// Воспроизводим рингтон при получении нового инвайта
|
||||
if (TryComp<RingerComponent>(loaderUid, out var ringer))
|
||||
{
|
||||
_ringer.RingerPlayRingtone(loaderUid);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateUiState(uid, loaderUid, component);
|
||||
}
|
||||
|
||||
private void HandleInvitesList(EntityUid uid, MessengerCartridgeComponent component, DeviceNetworkPacketEvent packet, EntityUid loaderUid)
|
||||
{
|
||||
if (!packet.Data.TryGetValue("invites", out List<Dictionary<string, object>>? invitesData))
|
||||
return;
|
||||
|
||||
component.ActiveInvites.Clear();
|
||||
|
||||
foreach (var inviteData in invitesData)
|
||||
{
|
||||
if (!inviteData.TryGetValue("group_id", out object? groupIdObj) ||
|
||||
!inviteData.TryGetValue("group_name", out object? groupNameObj) ||
|
||||
!inviteData.TryGetValue("inviter_id", out object? inviterIdObj) ||
|
||||
!inviteData.TryGetValue("inviter_name", out object? inviterNameObj) ||
|
||||
!inviteData.TryGetValue("created_at", out object? createdAtObj))
|
||||
continue;
|
||||
|
||||
var groupId = groupIdObj?.ToString();
|
||||
var groupName = groupNameObj?.ToString();
|
||||
var inviterId = inviterIdObj?.ToString();
|
||||
var inviterName = inviterNameObj?.ToString();
|
||||
|
||||
if (groupId == null || groupName == null || inviterId == null || inviterName == null)
|
||||
continue;
|
||||
|
||||
if (!double.TryParse(createdAtObj?.ToString(), out var createdAtSeconds))
|
||||
continue;
|
||||
|
||||
var createdAt = TimeSpan.FromSeconds(createdAtSeconds);
|
||||
|
||||
var invite = new MessengerGroupInvite(groupId, groupName, inviterId, inviterName, component.UserId ?? string.Empty, createdAt);
|
||||
component.ActiveInvites.Add(invite);
|
||||
}
|
||||
|
||||
UpdateUiState(uid, loaderUid, component);
|
||||
}
|
||||
|
||||
private void HandleGroupCreated(EntityUid uid, MessengerCartridgeComponent component, DeviceNetworkPacketEvent packet, EntityUid loaderUid)
|
||||
{
|
||||
if (TryComp<DeviceNetworkComponent>(loaderUid, out var deviceNetwork))
|
||||
|
|
@ -520,4 +622,83 @@ public sealed partial class MessengerCartridgeSystem
|
|||
if (TryComp<DeviceNetworkComponent>(loaderUid, out var deviceNetwork))
|
||||
RequestGroups(uid, component, loaderUid, deviceNetwork);
|
||||
}
|
||||
|
||||
private void HandleUserLeftGroup(EntityUid uid, MessengerCartridgeComponent component, DeviceNetworkPacketEvent packet, EntityUid loaderUid)
|
||||
{
|
||||
if (!packet.Data.TryGetValue("group_id", out object? groupIdObj) ||
|
||||
!packet.Data.TryGetValue("user_id", out object? userIdObj))
|
||||
return;
|
||||
|
||||
var groupId = groupIdObj?.ToString();
|
||||
var userId = userIdObj?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(groupId))
|
||||
return;
|
||||
|
||||
if (userId == component.UserId)
|
||||
{
|
||||
component.Groups.RemoveAll(g => g.GroupId == groupId);
|
||||
component.MessageHistory.Remove(groupId);
|
||||
component.MutedGroupChats.Remove(groupId);
|
||||
component.ServerUnreadCounts.Remove(groupId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var group = component.Groups.FirstOrDefault(g => g.GroupId == groupId);
|
||||
if (group != null && !string.IsNullOrEmpty(userId))
|
||||
{
|
||||
group.Members.Remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<DeviceNetworkComponent>(loaderUid, out var deviceNetwork))
|
||||
RequestGroups(uid, component, loaderUid, deviceNetwork);
|
||||
|
||||
UpdateUiState(uid, loaderUid, component);
|
||||
}
|
||||
|
||||
private void HandleGroupDeleted(EntityUid uid, MessengerCartridgeComponent component, DeviceNetworkPacketEvent packet, EntityUid loaderUid)
|
||||
{
|
||||
if (!packet.Data.TryGetValue("group_id", out object? groupIdObj))
|
||||
return;
|
||||
|
||||
var groupId = groupIdObj?.ToString();
|
||||
if (string.IsNullOrEmpty(groupId))
|
||||
return;
|
||||
|
||||
component.Groups.RemoveAll(g => g.GroupId == groupId);
|
||||
component.MessageHistory.Remove(groupId);
|
||||
component.MutedGroupChats.Remove(groupId);
|
||||
component.ServerUnreadCounts.Remove(groupId);
|
||||
|
||||
component.ActiveInvites.RemoveAll(inv => inv.GroupId == groupId);
|
||||
|
||||
if (TryComp<DeviceNetworkComponent>(loaderUid, out var deviceNetwork))
|
||||
RequestGroups(uid, component, loaderUid, deviceNetwork);
|
||||
|
||||
UpdateUiState(uid, loaderUid, component);
|
||||
}
|
||||
|
||||
private void HandleMessageDeleted(EntityUid uid, MessengerCartridgeComponent component, DeviceNetworkPacketEvent packet, EntityUid loaderUid)
|
||||
{
|
||||
if (!packet.Data.TryGetValue("message_id", out object? messageIdObj) ||
|
||||
!packet.Data.TryGetValue("chat_id", out object? chatIdObj))
|
||||
return;
|
||||
|
||||
if (!long.TryParse(messageIdObj?.ToString(), out var messageId))
|
||||
return;
|
||||
|
||||
var chatId = chatIdObj?.ToString();
|
||||
if (string.IsNullOrEmpty(chatId))
|
||||
return;
|
||||
|
||||
if (component.MessageHistory.TryGetValue(chatId, out var history))
|
||||
{
|
||||
history.RemoveAll(m => m.MessageId == messageId);
|
||||
if (history.Count == 0)
|
||||
component.MessageHistory.Remove(chatId);
|
||||
}
|
||||
|
||||
UpdateUiState(uid, loaderUid, component);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ public sealed partial class MessengerCartridgeSystem
|
|||
component.MessageHistory,
|
||||
component.MutedPersonalChats,
|
||||
component.MutedGroupChats,
|
||||
unreadCounts
|
||||
unreadCounts,
|
||||
component.ActiveInvites
|
||||
);
|
||||
|
||||
_cartridgeLoader.UpdateCartridgeUiState(loaderUid, state);
|
||||
|
|
|
|||
|
|
@ -23,4 +23,25 @@ public static class MessengerCommands
|
|||
public const string CmdMessageReceived = "messenger_message_received";
|
||||
public const string CmdGroupCreated = "messenger_group_created";
|
||||
public const string CmdUserAddedToGroup = "messenger_user_added_to_group";
|
||||
public const string CmdInviteReceived = "messenger_invite_received";
|
||||
public const string CmdInviteAccepted = "messenger_invite_accepted";
|
||||
public const string CmdInviteDeclined = "messenger_invite_declined";
|
||||
public const string CmdInvitesList = "messenger_invites_list";
|
||||
|
||||
// Входящие команды для инвайтов
|
||||
public const string CmdAcceptInvite = "messenger_accept_invite";
|
||||
public const string CmdDeclineInvite = "messenger_decline_invite";
|
||||
|
||||
// Входящие команды для выхода из группы
|
||||
public const string CmdLeaveGroup = "messenger_leave_group";
|
||||
|
||||
// Исходящие команды для выхода из группы
|
||||
public const string CmdUserLeftGroup = "messenger_user_left_group";
|
||||
public const string CmdGroupDeleted = "messenger_group_deleted";
|
||||
|
||||
// Входящие команды для удаления сообщений
|
||||
public const string CmdDeleteMessage = "messenger_delete_message";
|
||||
|
||||
// Исходящие команды для удаления сообщений
|
||||
public const string CmdMessageDeleted = "messenger_message_deleted";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public sealed partial class MessengerServerComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MaxMessageHistory = 5000;
|
||||
public int MaxMessageHistory = 2000;
|
||||
|
||||
/// <summary>
|
||||
/// Счетчик для генерации уникальных ID групп
|
||||
|
|
@ -66,4 +66,10 @@ public sealed partial class MessengerServerComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<DeviceFrequencyPrototype> PdaFrequencyId = "PDA";
|
||||
|
||||
/// <summary>
|
||||
/// Активные приглашения в группы. Ключ - ID получателя, значение - список приглашений
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public readonly Dictionary<string, List<MessengerGroupInvite>> ActiveInvites = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,25 +78,94 @@ public sealed partial class MessengerServerSystem
|
|||
if (!component.Groups.TryGetValue(groupId, out var group))
|
||||
return;
|
||||
|
||||
if (group.Type != MessengerGroupType.UserCreated)
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
if (group.AutoGroupPrototypeId != null)
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
if (group.Type == MessengerGroupType.UserCreated)
|
||||
{
|
||||
if (!component.Users.TryGetValue(args.SenderAddress, out var inviter))
|
||||
return;
|
||||
|
||||
if (group.OwnerId != inviter.UserId)
|
||||
return;
|
||||
|
||||
if (!component.Users.TryGetValue(userId, out var invitee))
|
||||
return;
|
||||
|
||||
if (group.Members.Contains(userId))
|
||||
return;
|
||||
|
||||
// Проверяем, нет ли уже активного инвайта
|
||||
if (component.ActiveInvites.TryGetValue(userId, out var existingInvites))
|
||||
{
|
||||
if (_prototypeManager.TryIndex<MessengerAutoGroupPrototype>(group.AutoGroupPrototypeId, out var autoGroupProto))
|
||||
{
|
||||
if (!autoGroupProto.AllowManualMemberManagement)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (existingInvites.Any(inv => inv.GroupId == groupId))
|
||||
return;
|
||||
}
|
||||
|
||||
// Создаем инвайт
|
||||
var invite = new MessengerGroupInvite(
|
||||
groupId,
|
||||
group.Name,
|
||||
inviter.UserId,
|
||||
inviter.Name,
|
||||
userId,
|
||||
GetStationTime()
|
||||
);
|
||||
|
||||
if (!component.ActiveInvites.TryGetValue(userId, out var invites))
|
||||
{
|
||||
invites = new List<MessengerGroupInvite>();
|
||||
component.ActiveInvites[userId] = invites;
|
||||
}
|
||||
invites.Add(invite);
|
||||
|
||||
var invitePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdInviteReceived,
|
||||
["group_id"] = groupId,
|
||||
["group_name"] = group.Name,
|
||||
["inviter_id"] = inviter.UserId,
|
||||
["inviter_name"] = inviter.Name,
|
||||
["created_at"] = invite.CreatedAt.TotalSeconds
|
||||
};
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, userId, invitePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, userId, invitePayload);
|
||||
}
|
||||
|
||||
// Отправляем обновленный список инвайтов
|
||||
SendInvitesList(uid, component, userId, serverDevice, pdaFrequency);
|
||||
return;
|
||||
}
|
||||
|
||||
// Для автоматических групп сохраняем старую логику (если разрешено)
|
||||
if (group.AutoGroupPrototypeId != null)
|
||||
{
|
||||
if (_prototypeManager.TryIndex<MessengerAutoGroupPrototype>(group.AutoGroupPrototypeId, out var autoGroupProto))
|
||||
{
|
||||
if (!autoGroupProto.AllowManualMemberManagement)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component.Users.TryGetValue(args.SenderAddress, out var adder))
|
||||
return;
|
||||
|
|
@ -128,15 +197,6 @@ public sealed partial class MessengerServerSystem
|
|||
history.Add(systemMessage);
|
||||
TrimMessageHistory(history, component.MaxMessageHistory);
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
var messagePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessageReceived,
|
||||
|
|
@ -332,4 +392,411 @@ public sealed partial class MessengerServerSystem
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает принятие приглашения в группу
|
||||
/// </summary>
|
||||
private void HandleAcceptInvite(EntityUid uid, MessengerServerComponent component, DeviceNetworkPacketEvent args)
|
||||
{
|
||||
if (!args.Data.TryGetValue(MessengerCommands.CmdAcceptInvite, out NetworkPayload? acceptData))
|
||||
return;
|
||||
|
||||
if (!acceptData.TryGetValue("group_id", out string? groupId))
|
||||
return;
|
||||
|
||||
if (!component.Users.TryGetValue(args.SenderAddress, out var user))
|
||||
return;
|
||||
|
||||
if (!component.ActiveInvites.TryGetValue(user.UserId, out var invites))
|
||||
return;
|
||||
|
||||
var invite = invites.FirstOrDefault(inv => inv.GroupId == groupId);
|
||||
if (invite == null)
|
||||
return;
|
||||
|
||||
if (!component.Groups.TryGetValue(groupId, out var group))
|
||||
return;
|
||||
|
||||
if (group.Members.Contains(user.UserId))
|
||||
{
|
||||
invites.Remove(invite);
|
||||
if (invites.Count == 0)
|
||||
component.ActiveInvites.Remove(user.UserId);
|
||||
return;
|
||||
}
|
||||
|
||||
group.Members.Add(user.UserId);
|
||||
|
||||
invites.Remove(invite);
|
||||
if (invites.Count == 0)
|
||||
component.ActiveInvites.Remove(user.UserId);
|
||||
|
||||
var timestamp = GetStationTime();
|
||||
var messageText = Loc.GetString("messenger-system-user-joined", ("userName", user.Name));
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var systemMessage = new MessengerMessage("system", Loc.GetString("messenger-system-name"), messageText, timestamp, groupId, null, false, messageId);
|
||||
|
||||
if (!component.MessageHistory.TryGetValue(groupId, out var history))
|
||||
{
|
||||
history = new List<MessengerMessage>();
|
||||
component.MessageHistory[groupId] = history;
|
||||
}
|
||||
history.Add(systemMessage);
|
||||
TrimMessageHistory(history, component.MaxMessageHistory);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
var messagePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessageReceived,
|
||||
["sender_id"] = "system",
|
||||
["sender_name"] = Loc.GetString("messenger-system-name"),
|
||||
["content"] = messageText,
|
||||
["timestamp"] = timestamp.TotalSeconds,
|
||||
["group_id"] = groupId,
|
||||
["recipient_id"] = string.Empty,
|
||||
["is_read"] = false,
|
||||
["message_id"] = systemMessage.MessageId
|
||||
};
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
{
|
||||
var isMemberChatOpen = component.OpenChats.TryGetValue(memberId, out var memberOpenChatId) && memberOpenChatId == groupId;
|
||||
|
||||
if (!isMemberChatOpen)
|
||||
{
|
||||
if (!component.UnreadCounts.TryGetValue(memberId, out var memberUnreads))
|
||||
{
|
||||
memberUnreads = new Dictionary<string, int>();
|
||||
component.UnreadCounts[memberId] = memberUnreads;
|
||||
}
|
||||
memberUnreads.TryGetValue(groupId, out var currentCount);
|
||||
memberUnreads[groupId] = currentCount + 1;
|
||||
}
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload);
|
||||
}
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdUserAddedToGroup,
|
||||
["group_id"] = groupId,
|
||||
["user_id"] = user.UserId
|
||||
};
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
{
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, payload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (component.MessageHistory.TryGetValue(groupId, out var groupHistory) && groupHistory.Count > 0)
|
||||
{
|
||||
var sortedMessages = groupHistory.OrderBy(m => m.Timestamp)
|
||||
.ThenBy(m => m.MessageId)
|
||||
.ThenBy(m => m.SenderId)
|
||||
.ToList();
|
||||
|
||||
var messagesData = new List<Dictionary<string, object>>();
|
||||
foreach (var msg in sortedMessages)
|
||||
{
|
||||
messagesData.Add(new Dictionary<string, object>
|
||||
{
|
||||
["sender_id"] = msg.SenderId,
|
||||
["sender_name"] = msg.SenderName,
|
||||
["content"] = msg.Content,
|
||||
["timestamp"] = msg.Timestamp.TotalSeconds,
|
||||
["group_id"] = msg.GroupId ?? string.Empty,
|
||||
["recipient_id"] = msg.RecipientId ?? string.Empty,
|
||||
["is_read"] = msg.IsRead,
|
||||
["message_id"] = msg.MessageId
|
||||
});
|
||||
}
|
||||
|
||||
var historyPayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessagesList,
|
||||
["messages"] = messagesData,
|
||||
["chat_id"] = groupId
|
||||
};
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, user.UserId, historyPayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, user.UserId, historyPayload);
|
||||
}
|
||||
}
|
||||
|
||||
SendInvitesList(uid, component, user.UserId, serverDevice, pdaFrequency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает отклонение приглашения в группу
|
||||
/// </summary>
|
||||
private void HandleDeclineInvite(EntityUid uid, MessengerServerComponent component, DeviceNetworkPacketEvent args)
|
||||
{
|
||||
if (!args.Data.TryGetValue(MessengerCommands.CmdDeclineInvite, out NetworkPayload? declineData))
|
||||
return;
|
||||
|
||||
if (!declineData.TryGetValue("group_id", out string? groupId))
|
||||
return;
|
||||
|
||||
if (!component.Users.TryGetValue(args.SenderAddress, out var user))
|
||||
return;
|
||||
|
||||
if (!component.ActiveInvites.TryGetValue(user.UserId, out var invites))
|
||||
return;
|
||||
|
||||
var invite = invites.FirstOrDefault(inv => inv.GroupId == groupId);
|
||||
if (invite == null)
|
||||
return;
|
||||
|
||||
invites.Remove(invite);
|
||||
if (invites.Count == 0)
|
||||
component.ActiveInvites.Remove(user.UserId);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
SendInvitesList(uid, component, user.UserId, serverDevice, pdaFrequency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет список активных инвайтов пользователю
|
||||
/// </summary>
|
||||
private void SendInvitesList(EntityUid uid, MessengerServerComponent component, string userId, DeviceNetworkComponent serverDevice, uint? pdaFrequency)
|
||||
{
|
||||
var invitesList = new List<Dictionary<string, object>>();
|
||||
|
||||
if (component.ActiveInvites.TryGetValue(userId, out var invites))
|
||||
{
|
||||
foreach (var invite in invites)
|
||||
{
|
||||
invitesList.Add(new Dictionary<string, object>
|
||||
{
|
||||
["group_id"] = invite.GroupId,
|
||||
["group_name"] = invite.GroupName,
|
||||
["inviter_id"] = invite.InviterId,
|
||||
["inviter_name"] = invite.InviterName,
|
||||
["created_at"] = invite.CreatedAt.TotalSeconds
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdInvitesList,
|
||||
["invites"] = invitesList
|
||||
};
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, userId, payload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, userId, payload);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает выход пользователя из группы
|
||||
/// </summary>
|
||||
private void HandleLeaveGroup(EntityUid uid, MessengerServerComponent component, DeviceNetworkPacketEvent args)
|
||||
{
|
||||
if (!args.Data.TryGetValue(MessengerCommands.CmdLeaveGroup, out NetworkPayload? leaveData))
|
||||
return;
|
||||
|
||||
if (!leaveData.TryGetValue("group_id", out string? groupId))
|
||||
return;
|
||||
|
||||
if (!component.Users.TryGetValue(args.SenderAddress, out var user))
|
||||
return;
|
||||
|
||||
if (!component.Groups.TryGetValue(groupId, out var group))
|
||||
return;
|
||||
|
||||
if (!group.Members.Contains(user.UserId))
|
||||
return;
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
if (group.OwnerId == user.UserId && group.Type == MessengerGroupType.UserCreated)
|
||||
{
|
||||
var membersToNotify = new List<string>(group.Members);
|
||||
component.Groups.Remove(groupId);
|
||||
component.MessageHistory.Remove(groupId);
|
||||
|
||||
foreach (var memberId in membersToNotify)
|
||||
{
|
||||
if (component.UnreadCounts.TryGetValue(memberId, out var memberUnreads))
|
||||
{
|
||||
memberUnreads.Remove(groupId);
|
||||
if (memberUnreads.Count == 0)
|
||||
component.UnreadCounts.Remove(memberId);
|
||||
}
|
||||
|
||||
if (component.OpenChats.TryGetValue(memberId, out var openChatId) && openChatId == groupId)
|
||||
{
|
||||
component.OpenChats[memberId] = null;
|
||||
}
|
||||
|
||||
var deletePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdGroupDeleted,
|
||||
["group_id"] = groupId
|
||||
};
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, deletePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, deletePayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
group.Members.Remove(user.UserId);
|
||||
|
||||
if (group.Type == MessengerGroupType.UserCreated)
|
||||
{
|
||||
var timestamp = GetStationTime();
|
||||
var messageText = Loc.GetString("messenger-system-user-left", ("userName", user.Name));
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var systemMessage = new MessengerMessage("system", Loc.GetString("messenger-system-name"), messageText, timestamp, groupId, null, false, messageId);
|
||||
|
||||
if (!component.MessageHistory.TryGetValue(groupId, out var history))
|
||||
{
|
||||
history = new List<MessengerMessage>();
|
||||
component.MessageHistory[groupId] = history;
|
||||
}
|
||||
history.Add(systemMessage);
|
||||
TrimMessageHistory(history, component.MaxMessageHistory);
|
||||
|
||||
var messagePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessageReceived,
|
||||
["sender_id"] = "system",
|
||||
["sender_name"] = Loc.GetString("messenger-system-name"),
|
||||
["content"] = messageText,
|
||||
["timestamp"] = timestamp.TotalSeconds,
|
||||
["group_id"] = groupId,
|
||||
["recipient_id"] = string.Empty,
|
||||
["is_read"] = false,
|
||||
["message_id"] = systemMessage.MessageId
|
||||
};
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
{
|
||||
var isMemberChatOpen = component.OpenChats.TryGetValue(memberId, out var memberOpenChatId) && memberOpenChatId == groupId;
|
||||
|
||||
if (!isMemberChatOpen)
|
||||
{
|
||||
if (!component.UnreadCounts.TryGetValue(memberId, out var memberUnreads))
|
||||
{
|
||||
memberUnreads = new Dictionary<string, int>();
|
||||
component.UnreadCounts[memberId] = memberUnreads;
|
||||
}
|
||||
memberUnreads.TryGetValue(groupId, out var currentCount);
|
||||
memberUnreads[groupId] = currentCount + 1;
|
||||
}
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var payload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdUserLeftGroup,
|
||||
["group_id"] = groupId,
|
||||
["user_id"] = user.UserId
|
||||
};
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
{
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, payload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, payload);
|
||||
}
|
||||
}
|
||||
|
||||
var leftPayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdUserLeftGroup,
|
||||
["group_id"] = groupId,
|
||||
["user_id"] = user.UserId
|
||||
};
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, user.UserId, leftPayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, user.UserId, leftPayload);
|
||||
}
|
||||
|
||||
if (component.UnreadCounts.TryGetValue(user.UserId, out var userUnreads))
|
||||
{
|
||||
userUnreads.Remove(groupId);
|
||||
if (userUnreads.Count == 0)
|
||||
component.UnreadCounts.Remove(user.UserId);
|
||||
}
|
||||
|
||||
if (component.OpenChats.TryGetValue(user.UserId, out var userOpenChatId) && userOpenChatId == groupId)
|
||||
{
|
||||
component.OpenChats[user.UserId] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.DeviceNetwork;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
|
|
@ -199,4 +200,85 @@ public sealed partial class MessengerServerSystem
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает удаление сообщения пользователем
|
||||
/// </summary>
|
||||
private void HandleDeleteMessage(EntityUid uid, MessengerServerComponent component, DeviceNetworkPacketEvent args)
|
||||
{
|
||||
if (!args.Data.TryGetValue(MessengerCommands.CmdDeleteMessage, out NetworkPayload? deleteData))
|
||||
return;
|
||||
|
||||
if (!deleteData.TryGetValue("message_id", out object? messageIdObj) ||
|
||||
!deleteData.TryGetValue("chat_id", out string? chatId))
|
||||
return;
|
||||
|
||||
if (!long.TryParse(messageIdObj?.ToString(), out var messageId))
|
||||
return;
|
||||
|
||||
if (!component.Users.TryGetValue(args.SenderAddress, out var user))
|
||||
return;
|
||||
|
||||
if (!component.MessageHistory.TryGetValue(chatId, out var history))
|
||||
return;
|
||||
|
||||
var message = history.FirstOrDefault(m => m.MessageId == messageId);
|
||||
if (message == null)
|
||||
return;
|
||||
|
||||
if (message.SenderId != user.UserId)
|
||||
return;
|
||||
|
||||
history.Remove(message);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
return;
|
||||
|
||||
uint? pdaFrequency = null;
|
||||
if (_prototypeManager.TryIndex(component.PdaFrequencyId, out var pdaFreq))
|
||||
{
|
||||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
var recipients = new HashSet<string>();
|
||||
|
||||
if (chatId.StartsWith("personal_"))
|
||||
{
|
||||
var parts = chatId.Split('_');
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
recipients.Add(parts[1]);
|
||||
recipients.Add(parts[2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (component.Groups.TryGetValue(chatId, out var group))
|
||||
{
|
||||
foreach (var memberId in group.Members)
|
||||
{
|
||||
recipients.Add(memberId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var deletePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessageDeleted,
|
||||
["message_id"] = messageId,
|
||||
["chat_id"] = chatId
|
||||
};
|
||||
|
||||
foreach (var recipientId in recipients)
|
||||
{
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, recipientId, deletePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, recipientId, deletePayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -449,19 +449,6 @@ public sealed partial class MessengerServerSystem
|
|||
{
|
||||
group.Members.Add(userId);
|
||||
|
||||
var timestamp = GetStationTime();
|
||||
var messageText = Loc.GetString("messenger-system-user-added", ("userName", userName));
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var systemMessage = new MessengerMessage("system", Loc.GetString("messenger-system-name"), messageText, timestamp, autoGroupProto.GroupId, null, false, messageId);
|
||||
|
||||
if (!component.MessageHistory.TryGetValue(autoGroupProto.GroupId, out var history))
|
||||
{
|
||||
history = new List<MessengerMessage>();
|
||||
component.MessageHistory[autoGroupProto.GroupId] = history;
|
||||
}
|
||||
history.Add(systemMessage);
|
||||
TrimMessageHistory(history, component.MaxMessageHistory);
|
||||
|
||||
if (!TryComp<DeviceNetworkComponent>(uid, out var serverDevice))
|
||||
continue;
|
||||
|
||||
|
|
@ -471,41 +458,67 @@ public sealed partial class MessengerServerSystem
|
|||
pdaFrequency = pdaFreq.Frequency;
|
||||
}
|
||||
|
||||
var messagePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessageReceived,
|
||||
["sender_id"] = "system",
|
||||
["sender_name"] = Loc.GetString("messenger-system-name"),
|
||||
["content"] = messageText,
|
||||
["timestamp"] = timestamp.TotalSeconds,
|
||||
["group_id"] = autoGroupProto.GroupId,
|
||||
["recipient_id"] = string.Empty,
|
||||
["is_read"] = false,
|
||||
["message_id"] = systemMessage.MessageId
|
||||
};
|
||||
var isDepartmentChat = group.Type == MessengerGroupType.Automatic && group.AutoGroupPrototypeId != null;
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
if (!isDepartmentChat)
|
||||
{
|
||||
var isMemberChatOpen = component.OpenChats.TryGetValue(memberId, out var memberOpenChatId) && memberOpenChatId == autoGroupProto.GroupId;
|
||||
var timestamp = GetStationTime();
|
||||
var messageText = Loc.GetString("messenger-system-user-added", ("userName", userName));
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var systemMessage = new MessengerMessage("system", Loc.GetString("messenger-system-name"), messageText, timestamp, autoGroupProto.GroupId, null, false, messageId);
|
||||
|
||||
if (!isMemberChatOpen)
|
||||
if (!component.MessageHistory.TryGetValue(autoGroupProto.GroupId, out var history))
|
||||
{
|
||||
if (!component.UnreadCounts.TryGetValue(memberId, out var memberUnreads))
|
||||
history = new List<MessengerMessage>();
|
||||
component.MessageHistory[autoGroupProto.GroupId] = history;
|
||||
}
|
||||
history.Add(systemMessage);
|
||||
TrimMessageHistory(history, component.MaxMessageHistory);
|
||||
}
|
||||
|
||||
if (!isDepartmentChat)
|
||||
{
|
||||
var timestamp = GetStationTime();
|
||||
var messageText = Loc.GetString("messenger-system-user-added", ("userName", userName));
|
||||
var messageId = GetNextMessageId(uid, component);
|
||||
var systemMessage = new MessengerMessage("system", Loc.GetString("messenger-system-name"), messageText, timestamp, autoGroupProto.GroupId, null, false, messageId);
|
||||
|
||||
var messagePayload = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.Command] = MessengerCommands.CmdMessageReceived,
|
||||
["sender_id"] = "system",
|
||||
["sender_name"] = Loc.GetString("messenger-system-name"),
|
||||
["content"] = messageText,
|
||||
["timestamp"] = timestamp.TotalSeconds,
|
||||
["group_id"] = autoGroupProto.GroupId,
|
||||
["recipient_id"] = string.Empty,
|
||||
["is_read"] = false,
|
||||
["message_id"] = systemMessage.MessageId
|
||||
};
|
||||
|
||||
foreach (var memberId in group.Members)
|
||||
{
|
||||
var isMemberChatOpen = component.OpenChats.TryGetValue(memberId, out var memberOpenChatId) && memberOpenChatId == autoGroupProto.GroupId;
|
||||
|
||||
if (!isMemberChatOpen)
|
||||
{
|
||||
memberUnreads = new Dictionary<string, int>();
|
||||
component.UnreadCounts[memberId] = memberUnreads;
|
||||
if (!component.UnreadCounts.TryGetValue(memberId, out var memberUnreads))
|
||||
{
|
||||
memberUnreads = new Dictionary<string, int>();
|
||||
component.UnreadCounts[memberId] = memberUnreads;
|
||||
}
|
||||
memberUnreads.TryGetValue(autoGroupProto.GroupId, out var currentCount);
|
||||
memberUnreads[autoGroupProto.GroupId] = currentCount + 1;
|
||||
}
|
||||
memberUnreads.TryGetValue(autoGroupProto.GroupId, out var currentCount);
|
||||
memberUnreads[autoGroupProto.GroupId] = currentCount + 1;
|
||||
}
|
||||
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload);
|
||||
if (pdaFrequency.HasValue)
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload, frequency: pdaFrequency, network: serverDevice.DeviceNetId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deviceNetwork.QueuePacket(uid, memberId, messagePayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,18 @@ public sealed partial class MessengerServerSystem : EntitySystem
|
|||
case MessengerCommands.CmdGetMessages:
|
||||
HandleGetMessages(uid, component, args);
|
||||
break;
|
||||
case MessengerCommands.CmdAcceptInvite:
|
||||
HandleAcceptInvite(uid, component, args);
|
||||
break;
|
||||
case MessengerCommands.CmdDeclineInvite:
|
||||
HandleDeclineInvite(uid, component, args);
|
||||
break;
|
||||
case MessengerCommands.CmdLeaveGroup:
|
||||
HandleLeaveGroup(uid, component, args);
|
||||
break;
|
||||
case MessengerCommands.CmdDeleteMessage:
|
||||
HandleDeleteMessage(uid, component, args);
|
||||
break;
|
||||
default:
|
||||
Sawmill.Warning($"Unknown command received: {command} from {args.SenderAddress}");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public sealed class MessengerUiMessageEvent : CartridgeMessageEvent
|
|||
public readonly string? UserId;
|
||||
public readonly string? ChatId;
|
||||
public readonly bool? IsMuted;
|
||||
public readonly long? MessageId;
|
||||
|
||||
public MessengerUiMessageEvent(
|
||||
MessengerUiAction action,
|
||||
|
|
@ -26,7 +27,8 @@ public sealed class MessengerUiMessageEvent : CartridgeMessageEvent
|
|||
string? groupName = null,
|
||||
string? userId = null,
|
||||
string? chatId = null,
|
||||
bool? isMuted = null)
|
||||
bool? isMuted = null,
|
||||
long? messageId = null)
|
||||
{
|
||||
Action = action;
|
||||
RecipientId = recipientId;
|
||||
|
|
@ -36,6 +38,7 @@ public sealed class MessengerUiMessageEvent : CartridgeMessageEvent
|
|||
UserId = userId;
|
||||
ChatId = chatId;
|
||||
IsMuted = isMuted;
|
||||
MessageId = messageId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,5 +55,9 @@ public enum MessengerUiAction
|
|||
RequestUsers,
|
||||
RequestGroups,
|
||||
RequestMessages,
|
||||
ToggleMute
|
||||
ToggleMute,
|
||||
AcceptInvite,
|
||||
DeclineInvite,
|
||||
LeaveGroup,
|
||||
DeleteMessage
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ public sealed class MessengerUiState : BoundUserInterfaceState
|
|||
/// </summary>
|
||||
public Dictionary<string, int> UnreadCounts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Активные приглашения в группы
|
||||
/// </summary>
|
||||
public List<MessengerGroupInvite> ActiveInvites { get; }
|
||||
|
||||
public MessengerUiState(
|
||||
bool isRegistered,
|
||||
bool serverAvailable,
|
||||
|
|
@ -63,7 +68,8 @@ public sealed class MessengerUiState : BoundUserInterfaceState
|
|||
Dictionary<string, List<MessengerMessage>> messageHistory,
|
||||
HashSet<string> mutedPersonalChats,
|
||||
HashSet<string> mutedGroupChats,
|
||||
Dictionary<string, int> unreadCounts)
|
||||
Dictionary<string, int> unreadCounts,
|
||||
List<MessengerGroupInvite> activeInvites)
|
||||
{
|
||||
IsRegistered = isRegistered;
|
||||
ServerAvailable = serverAvailable;
|
||||
|
|
@ -74,5 +80,6 @@ public sealed class MessengerUiState : BoundUserInterfaceState
|
|||
MutedPersonalChats = mutedPersonalChats;
|
||||
MutedGroupChats = mutedGroupChats;
|
||||
UnreadCounts = unreadCounts;
|
||||
ActiveInvites = activeInvites;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
50
Content.Shared/_Sunrise/Messenger/MessengerGroupInvite.cs
Normal file
50
Content.Shared/_Sunrise/Messenger/MessengerGroupInvite.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.Messenger;
|
||||
|
||||
/// <summary>
|
||||
/// Приглашение в группу мессенджера
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class MessengerGroupInvite
|
||||
{
|
||||
/// <summary>
|
||||
/// ID группы, в которую приглашают
|
||||
/// </summary>
|
||||
public string GroupId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Название группы
|
||||
/// </summary>
|
||||
public string GroupName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID пользователя, который отправил приглашение
|
||||
/// </summary>
|
||||
public string InviterId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Имя пользователя, который отправил приглашение
|
||||
/// </summary>
|
||||
public string InviterName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ID пользователя, которому отправлено приглашение
|
||||
/// </summary>
|
||||
public string InviteeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Время создания приглашения
|
||||
/// </summary>
|
||||
public TimeSpan CreatedAt { get; set; }
|
||||
|
||||
public MessengerGroupInvite(string groupId, string groupName, string inviterId, string inviterName, string inviteeId, TimeSpan createdAt)
|
||||
{
|
||||
GroupId = groupId;
|
||||
GroupName = groupName;
|
||||
InviterId = inviterId;
|
||||
InviterName = inviterName;
|
||||
InviteeId = inviteeId;
|
||||
CreatedAt = createdAt;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ messenger-status-connected = Connected
|
|||
messenger-search-placeholder = Search...
|
||||
messenger-tab-personal = Personal
|
||||
messenger-tab-groups = Groups
|
||||
messenger-tab-invites = Invites
|
||||
messenger-no-invites = No invites
|
||||
messenger-create-group-button = Create group
|
||||
messenger-chat-select = Select chat
|
||||
messenger-mute-tooltip = Mute
|
||||
|
|
@ -46,3 +48,13 @@ messenger-emoji-favorite-hint = ПКМ по общему списку для д
|
|||
messenger-emoji-all-title = All emojis
|
||||
messenger-user-unknown = Unknown
|
||||
messenger-system-name = System
|
||||
messenger-leave-group = Leave group
|
||||
messenger-invite-member-button = Invite
|
||||
messenger-invites-header = Invites
|
||||
messenger-invite-info = { $inviter } invites you
|
||||
messenger-invite-accept = Accept
|
||||
messenger-invite-decline = Decline
|
||||
messenger-invite-user-to-group-title = Invite to { $groupName }
|
||||
messenger-delete-message = Delete message
|
||||
messenger-system-user-joined = { $userName } joined the group
|
||||
messenger-system-user-left = { $userName } left the group
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ messenger-status-connected = Подключено
|
|||
messenger-search-placeholder = Поиск...
|
||||
messenger-tab-personal = Личные
|
||||
messenger-tab-groups = Группы
|
||||
messenger-tab-invites = Приглашения
|
||||
messenger-no-invites = Нет приглашений
|
||||
messenger-create-group-button = Создать группу
|
||||
messenger-chat-select = Выберите чат
|
||||
messenger-mute-tooltip = Отключить звук
|
||||
|
|
@ -47,3 +49,13 @@ messenger-emoji-favorite-hint = ПКМ по общему списку для д
|
|||
messenger-emoji-all-title = Все смайлики
|
||||
messenger-user-unknown = Неизвестно
|
||||
messenger-system-name = Система
|
||||
messenger-leave-group = Выйти из группы
|
||||
messenger-invite-member-button = Пригласить
|
||||
messenger-invites-header = Приглашения
|
||||
messenger-invite-info = { $inviter } приглашает вас
|
||||
messenger-invite-accept = Принять
|
||||
messenger-invite-decline = Отклонить
|
||||
messenger-invite-user-to-group-title = Пригласить в { $groupName }
|
||||
messenger-delete-message = Удалить сообщение
|
||||
messenger-system-user-joined = { $userName } присоединился(ась) к группе
|
||||
messenger-system-user-left = { $userName } покинул(а) группу
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue