Skip to content

Commit

Permalink
NanoChat QoL And Bug Fixes (#1460)
Browse files Browse the repository at this point in the history
# Description

Fixes notifications so that they always trigger when the chat is not
open, fixes a possible exploit where a user can send a message that is
longer than the max message length, and improves the UX of NanoChat in a
few areas.

Original message from
Goob-Station/Goob-Station#1313:
> Fixed NanoChat message notifications not showing up in situations
where you'd definitely expect them to occur.
> 
> Now, for a chat to not send a notification upon a new message, three
conditions need to be fulfilled:
> - You don't have the chat muted
> - Your PDA UI is open
> - You have NanoChat as your open program
> - Your currently-selected chat is the aforementioned chat

More changes:
- Validate NanoChat message length & chat name/job title length on the
server
Goob-Station/Goob-Station@c063001.
  -If they are too long, they will be truncated to the maximum length. 
- Improve the UI for editing a chat:
- Added a proper icon (used to be the letter E) for the Edit Chat
button.

![image](https://github.com/user-attachments/assets/ce868829-a9f5-494b-9d15-2cbc4a7e2a86)
- The Edit Chat popup now has correct text: the title is now "Edit a
contact", not "Add a new chat" and the Create button has been renamed to
the Confirm button.

![image](https://github.com/user-attachments/assets/aabd84b3-0f3c-410a-a74e-06420eb336dc)
  - Editing a chat no longer deselects the current chat.
- This is done by adding a new NanoChatUiMessageType entry, `EditChat`
and a new server-side handler function just for chat edits. The previous
edit chat method simply *deleted* the chat from the client and created a
new one.
- Form validation is now more robust: you can now only submit the edit
if you've made changes to the name or job title.
- Fixed NanoChat not appearing in PDAs with more than 5 pre-installed
programs by increasing the PDA disk space (max program count) from 5 to
8, the current disk space in Wizden.

![image](https://github.com/user-attachments/assets/e214fa21-5365-4f8a-8919-c6019d3b94bb)
- The job title now appears on the notification title.

![image](https://github.com/user-attachments/assets/486f5b2a-c555-4d1a-97ef-4dab3d8e1102)
  - The title will be truncated if it's too long.

![image](https://github.com/user-attachments/assets/ef99805c-b004-456a-9b59-e48f4abbe4ab)
- Improved form validation on the New Chat popup by only allowing users
to submit if the Number field is 4 numbers long. Previously, inputs like
"0" "12" "289" were valid which is ultimately incorrect as phone numbers
function more like strings that just happen to be digit-only and less
like actual numbers.

![image](https://github.com/user-attachments/assets/e3a767ff-71cb-4c61-b3e9-be13af66fd60)
- The left chat list will now truncate names and job titles if they're
too long.

![image](https://github.com/user-attachments/assets/76a8fbfa-e5e5-495c-ad13-fc9444e6ce9a)

## Changelog
:cl: Skubman
- add: NanoChat message notifications will now show you the job title of
the sender alongside their name.
- add: The NanoChat edit button now has a proper icon.
- tweak: The disk space (max amount of installable programs) on PDAs has
been increased from 5 to 8. This also fixes a bug where NanoChat was not
being installed on PDAs with more than 5 pre-installed cartridges.
- tweak: The character limit for names and job titles in NanoChat has
been increased to their actual limit on ID cards (30 characters).
- tweak: NanoChat chat list names/titles and notification titles that
are too long will be shortened, indicated with "..." at the end.
- tweak: The NanoChat edit popup now displays the correct text, instead
of appearing identical to the new chat popup.
- fix: NanoChat will now send a new message notification no matter what
if you don't have the chat open.
- fix: Editing a chat in NanoChat no longer deselects that chat.
- fix: Fixed a issue where users could potentially send NanoChat
messages longer than the maximum allowed message length.
- fix: Fixed being able to create a new chat when you haven't typed in 4
digits in the number field.
  • Loading branch information
angelofallars authored Jan 8, 2025
1 parent f026528 commit f11d2e7
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc nano-chat-new-title}"
Title="{Loc nano-chat-edit-title}"
MinSize="300 200">
<PanelContainer StyleClasses="AngleRect">
<BoxContainer Orientation="Vertical" Margin="4">
Expand Down Expand Up @@ -42,8 +42,8 @@
Text="{Loc nano-chat-cancel}"
StyleClasses="OpenRight"
MinSize="80 0" />
<Button Name="CreateButton"
Text="{Loc nano-chat-create}"
<Button Name="ConfirmButton"
Text="{Loc nano-chat-confirm}"
StyleClasses="OpenLeft"
MinSize="80 0"
Disabled="True" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Linq;
using Content.Client.DeltaV.NanoChat;
using Content.Shared.Access.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
Expand All @@ -9,9 +9,14 @@ namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class EditChatPopup : DefaultWindow
{
private const int MaxInputLength = 16;
private const int MaxNumberLength = 4;

// Used to see if the user input is different from the original data
// to check if the user can submit
private string _originalNumber = "";
private string _originalName = "";
private string _originalJob = "";

public event Action<uint, string, string?>? OnContactEdited;

public EditChatPopup()
Expand All @@ -23,22 +28,23 @@ public EditChatPopup()

// Button handlers
CancelButton.OnPressed += _ => Close();
CreateButton.OnPressed += _ => EditChat();
ConfirmButton.OnPressed += _ => EditChat();

// Input validation
NameInput.OnTextChanged += _ => ValidateInputs();

NameInput.OnTextChanged += args =>
{
if (args.Text.Length > MaxInputLength)
NameInput.Text = args.Text[..MaxInputLength];
if (args.Text.Length > IdCardConsoleComponent.MaxFullNameLength)
NameInput.Text = args.Text[..IdCardConsoleComponent.MaxFullNameLength];
ValidateInputs();
};

JobInput.OnTextChanged += args =>
{
if (args.Text.Length > MaxInputLength)
JobInput.Text = args.Text[..MaxInputLength];
if (args.Text.Length > IdCardConsoleComponent.MaxJobTitleLength)
JobInput.Text = args.Text[..IdCardConsoleComponent.MaxJobTitleLength];
ValidateInputs();
};

NumberInput.OnTextChanged += args =>
Expand All @@ -58,10 +64,15 @@ public EditChatPopup()
private void ValidateInputs()
{
var isValid = !string.IsNullOrWhiteSpace(NumberInput.Text) &&
!string.IsNullOrWhiteSpace(NameInput.Text) &&
uint.TryParse(NumberInput.Text, out _);

CreateButton.Disabled = !isValid;
!string.IsNullOrWhiteSpace(NameInput.Text) &&
NumberInput.Text.Length == MaxNumberLength &&
uint.TryParse(NumberInput.Text, out _) &&
// Only valid if there are any changes
(NumberInput.Text != _originalNumber ||
NameInput.Text != _originalName ||
JobInput.Text != _originalJob);

ConfirmButton.Disabled = !isValid;
}

private void EditChat()
Expand All @@ -83,7 +94,21 @@ public void ClearInputs()
ValidateInputs();
}

public void SetNumberInput(string newNumber) => NumberInput.Text = newNumber;
public void SetNameInput(string newName) => NameInput.Text = newName;
public void SetJobInput(string newJob) => JobInput.Text = newJob;
public void SetNumberInput(string newNumber)
{
NumberInput.Text = newNumber.PadLeft(MaxNumberLength, '0');
_originalNumber = newNumber;
}

public void SetNameInput(string newName)
{
NameInput.Text = newName;
_originalName = newName;
}

public void SetJobInput(string newJob)
{
JobInput.Text = newJob;
_originalJob = newJob;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class NanoChatEntry : BoxContainer
{
private const int MaxNameLength = 14;
private const int MaxJobLength = 20;

public event Action<uint>? OnPressed;
private uint _number;
private Action<EventArgs>? _pressHandler;
Expand All @@ -29,11 +32,19 @@ public void SetRecipient(NanoChatRecipient recipient, uint number, bool isSelect
_pressHandler = _ => OnPressed?.Invoke(_number);
ChatButton.OnPressed += _pressHandler;

NameLabel.Text = recipient.Name;
JobLabel.Text = recipient.JobTitle ?? "";
NameLabel.Text = Truncate(recipient.Name, MaxNameLength);
JobLabel.Text = Truncate(recipient.JobTitle ?? "", MaxJobLength);
JobLabel.Visible = !string.IsNullOrEmpty(recipient.JobTitle);
UnreadIndicator.Visible = recipient.HasUnread;

ChatButton.ModulateSelfOverride = isSelected ? NanoChatMessageBubble.OwnMessageColor : null;
}

/// <summary>
/// Truncates a string to a maximum length.
/// </summary>
private static string Truncate(string text, int maxLength) =>
text.Length <= maxLength
? text
: text[..(maxLength - 3)] + "...";
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@
VerticalAlignment="Center"
Margin="0 0 8 0" />
<Button Name="EditChatButton"
Text="E"
MaxSize="32 32"
Visible="False"
StyleClasses="OpenBoth"
Margin="0 0 4 0"
ToolTip="{Loc nano-chat-edit}">
<TextureRect StyleClasses="ButtonSquare"
TexturePath="/Textures/Interface/VerbIcons/edit.svg.png"
Stretch="KeepAspectCentered"
MinSize="18 18" />
</Button>
<Button Name="DeleteChatButton"
MaxSize="32 32"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ public sealed partial class NanoChatUiFragment : BoxContainer
{
[Dependency] private readonly IGameTiming _timing = default!;

private const int MaxMessageLength = 256;

private readonly NewChatPopup _newChatPopup;
private readonly EditChatPopup _editChatPopup;
private uint? _currentChat;
Expand Down Expand Up @@ -50,8 +48,7 @@ private void SetupEventHandlers()

_editChatPopup.OnContactEdited += (number, name, job) =>
{
DeleteCurrentChat();
ActionSendUiMessage?.Invoke(NanoChatUiMessageType.NewChat, number, name, job);
ActionSendUiMessage?.Invoke(NanoChatUiMessageType.EditChat, number, name, job);
};

NewChatButton.OnPressed += _ =>
Expand All @@ -71,18 +68,18 @@ private void SetupEventHandlers()
{
var length = args.Text.Length;
var isValid = !string.IsNullOrWhiteSpace(args.Text) &&
length <= MaxMessageLength &&
length <= NanoChatMessage.MaxContentLength &&
(_currentChat != null || _pendingChat != null);

SendButton.Disabled = !isValid;

// Show character count when over limit
CharacterCount.Visible = length > MaxMessageLength;
if (length > MaxMessageLength)
CharacterCount.Visible = length > NanoChatMessage.MaxContentLength;
if (length > NanoChatMessage.MaxContentLength)
{
CharacterCount.Text = Loc.GetString("nano-chat-message-too-long",
("current", length),
("max", MaxMessageLength));
("max", NanoChatMessage.MaxContentLength));
CharacterCount.StyleClasses.Add("LabelDanger");
}
};
Expand All @@ -100,6 +97,12 @@ private void SendMessage()
return;

var messageContent = MessageInput.Text;
if (!string.IsNullOrWhiteSpace(messageContent))
{
messageContent = messageContent.Trim();
if (messageContent.Length > NanoChatMessage.MaxContentLength)
messageContent = messageContent[..NanoChatMessage.MaxContentLength];
}

// Add predicted message
var predictedMessage = new NanoChatMessage(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Content.Shared.Access.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
Expand All @@ -8,7 +9,6 @@ namespace Content.Client.DeltaV.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class NewChatPopup : DefaultWindow
{
private const int MaxInputLength = 16;
private const int MaxNumberLength = 4; // i hardcoded it to be 4 so suffer

public event Action<uint, string, string?>? OnChatCreated;
Expand Down Expand Up @@ -44,22 +44,23 @@ public NewChatPopup()

NameInput.OnTextChanged += args =>
{
if (args.Text.Length > MaxInputLength)
NameInput.Text = args.Text[..MaxInputLength];
if (args.Text.Length > IdCardConsoleComponent.MaxFullNameLength)
NameInput.Text = args.Text[..IdCardConsoleComponent.MaxFullNameLength];
ValidateInputs();
};

JobInput.OnTextChanged += args =>
{
if (args.Text.Length > MaxInputLength)
JobInput.Text = args.Text[..MaxInputLength];
if (args.Text.Length > IdCardConsoleComponent.MaxJobTitleLength)
JobInput.Text = args.Text[..IdCardConsoleComponent.MaxJobTitleLength];
};
}

private void ValidateInputs()
{
var isValid = !string.IsNullOrWhiteSpace(NumberInput.Text) &&
!string.IsNullOrWhiteSpace(NameInput.Text) &&
NumberInput.Text.Length == MaxNumberLength &&
uint.TryParse(NumberInput.Text, out _);

CreateButton.Disabled = !isValid;
Expand Down
Loading

0 comments on commit f11d2e7

Please sign in to comment.