Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Content.Client/CartridgeLoader/Cartridges/LogProbeUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public override void UpdateState(BoundUserInterfaceState state)
if (state is not LogProbeUiState cast)
return;

_fragment?.UpdateState(cast.EntityName, cast.PulledLogs);
_fragment?.UpdateState(cast); // Triad: pass the whole state so the fragment can render TriTalk scans too
}
}
22 changes: 18 additions & 4 deletions Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,25 @@
BorderColor="#5a5a5a"
BorderThickness="0 0 0 1"/>
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Horizontal" Margin="4 8">
<Label Align="Right" SetWidth="26" ClipText="True" Text="{Loc 'log-probe-label-number'}"/>
<Label Align="Center" SetWidth="100" ClipText="True" Text="{Loc 'log-probe-label-time'}"/>
<Label Align="Left" SetWidth="390" ClipText="True" Text="{Loc 'log-probe-label-accessor'}"/>
<!-- Triad begin: header + card-number row so the probe can switch between access logs and TriTalk -->
<BoxContainer Orientation="Vertical" Margin="4 8">
<Label Name="TitleLabel"
Text="{Loc 'log-probe-header-access'}"
StyleClasses="LabelHeading"
HorizontalAlignment="Center"
Margin="0 0 0 8"/>
<Label Name="CardNumberLabel"
StyleClasses="LabelSubText"
HorizontalAlignment="Center"
Margin="0 0 0 8"
Visible="False"/>
<BoxContainer Orientation="Horizontal">
<Label Align="Right" SetWidth="26" ClipText="True" Text="{Loc 'log-probe-label-number'}"/>
<Label Align="Center" SetWidth="100" ClipText="True" Text="{Loc 'log-probe-label-time'}"/>
<Label Name="ContentLabel" Align="Left" SetWidth="390" ClipText="True" Text="{Loc 'log-probe-label-accessor'}"/>
</BoxContainer>
</BoxContainer>
<!-- Triad end -->
</PanelContainer>
<ScrollContainer VerticalExpand="True" HScrollEnabled="True">
<BoxContainer Orientation="Vertical" Name="ProbedDeviceContainer"/>
Expand Down
107 changes: 102 additions & 5 deletions Content.Client/CartridgeLoader/Cartridges/LogProbeUiFragment.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
锘縰sing Content.Shared.CartridgeLoader.Cartridges;
锘縰sing System.Linq; // Triad
using Content.Client._DV.CartridgeLoader.Cartridges; // Triad
using Content.Shared._DV.CartridgeLoader.Cartridges; // Triad
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
Expand All @@ -20,20 +23,114 @@ public LogProbeUiFragment()
PrintButton.OnPressed += _ => OnPrintPressed?.Invoke();
}

public void UpdateState(string name, List<PulledAccessLog> logs)
// Triad begin: the probe now renders either access logs or a scanned TriTalk card
public void UpdateState(LogProbeUiState state)
{
EntityName.Text = name;
PrintButton.Disabled = string.IsNullOrEmpty(name);
EntityName.Text = state.EntityName;
PrintButton.Disabled = string.IsNullOrEmpty(state.EntityName);

ProbedDeviceContainer.RemoveAllChildren();

var count = 1;
if (state.NanoChatData != null)
{
SetupNanoChatView(state.NanoChatData.Value);
DisplayNanoChatData(state.NanoChatData.Value);
}
else
{
SetupAccessLogView();
DisplayAccessLogs(state.PulledLogs);
}
}

private void SetupNanoChatView(NanoChatData data)
{
TitleLabel.Text = Loc.GetString("log-probe-header-nanochat");
ContentLabel.Text = Loc.GetString("log-probe-label-message");

// Show card info if available
var cardInfo = new List<string>();
if (data.CardNumber != null)
cardInfo.Add(Loc.GetString("log-probe-card-number", ("number", $"#{data.CardNumber:D4}")));

cardInfo.Add(Loc.GetString("log-probe-recipients", ("count", data.Recipients.Count)));

CardNumberLabel.Text = string.Join(" | ", cardInfo);
CardNumberLabel.Visible = true;
}

private void SetupAccessLogView()
{
TitleLabel.Text = Loc.GetString("log-probe-header-access");
ContentLabel.Text = Loc.GetString("log-probe-label-accessor");
CardNumberLabel.Visible = false;
}

private void DisplayNanoChatData(NanoChatData data)
{
// First add a recipient list entry
var recipientsList = Loc.GetString("log-probe-recipient-list") + "\n" + string.Join("\n",
data.Recipients.Values
.OrderBy(r => r.Name)
.Select(r => $" {r.Name}" +
(string.IsNullOrEmpty(r.JobTitle) ? "" : $" ({r.JobTitle})") +
$" | #{r.Number:D4}"));

var recipientsEntry = new LogProbeUiEntry(0, "---", recipientsList);
ProbedDeviceContainer.AddChild(recipientsEntry);

var count = 1;
foreach (var (partnerId, messages) in data.Messages)
{
// Show only successfully delivered incoming messages
var incomingMessages = messages
.Where(msg => msg.SenderId == partnerId && !msg.DeliveryFailed)
.OrderByDescending(msg => msg.Timestamp);

foreach (var msg in incomingMessages)
{
AddNanoChatEntry(count, msg, $"#{msg.SenderId:D4}", $"#{data.CardNumber:D4}");
count++;
}

// Show only successfully delivered outgoing messages
var outgoingMessages = messages
.Where(msg => msg.SenderId == data.CardNumber && !msg.DeliveryFailed)
.OrderByDescending(msg => msg.Timestamp);

foreach (var msg in outgoingMessages)
{
AddNanoChatEntry(count, msg, $"#{msg.SenderId:D4}", $"#{partnerId:D4}");
count++;
}
}
}

private void AddNanoChatEntry(int count, NanoChatMessage msg, string sender, string recipient)
{
var messageText = Loc.GetString("log-probe-message-format",
("sender", sender),
("recipient", recipient),
("content", msg.Content));

var entry = new NanoChatLogEntry(
count,
TimeSpan.FromSeconds(Math.Truncate(msg.Timestamp.TotalSeconds)).ToString(),
messageText);

ProbedDeviceContainer.AddChild(entry);
}

private void DisplayAccessLogs(List<PulledAccessLog> logs)
{
var count = 1;
foreach (var log in logs)
{
AddAccessLog(log, count);
count++;
}
}
// Triad end

private void AddAccessLog(PulledAccessLog log, int numberLabelText)
{
Expand Down
8 changes: 8 additions & 0 deletions Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ void AddCheckBox(string checkBoxName, bool currentState, Action<BaseButton.Butto
AddButton(ContentKeyFunctions.TargetRightFoot);
// Shitmed Change End

// Triad: TriTalk keybinds
AddHeader("ui-options-header-nano-chat");
AddButton(ContentKeyFunctions.NanoChatNavigateUp);
AddButton(ContentKeyFunctions.NanoChatNavigateDown);
AddButton(ContentKeyFunctions.NanoChatNavigateUpUnread);
AddButton(ContentKeyFunctions.NanoChatNavigateDownUnread);
// End Triad

AddHeader("ui-options-header-misc");
AddButton(ContentKeyFunctions.TakeScreenshot);
AddButton(ContentKeyFunctions.TakeScreenshotNoUI);
Expand Down
53 changes: 53 additions & 0 deletions Content.Client/_DV/CartridgeLoader/Cartridges/EditChatPopup.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc nano-chat-edit-title}"
MinSize="300 200">
<PanelContainer StyleClasses="AngleRect">
<BoxContainer Orientation="Vertical" Margin="4">
<!-- Number input -->
<BoxContainer Orientation="Vertical" Margin="0 4">
<Label Text="{Loc nano-chat-number-label}"
StyleClasses="LabelHeading" />
<PanelContainer StyleClasses="ButtonSquare">
<LineEdit Name="NumberInput"
PlaceHolder="{Loc nano-chat-number-placeholder}"
Editable="False" />
</PanelContainer>
</BoxContainer>

<!-- Name input -->
<BoxContainer Orientation="Vertical" Margin="0 4">
<Label Text="{Loc nano-chat-name-label}"
StyleClasses="LabelHeading" />
<PanelContainer StyleClasses="ButtonSquare">
<LineEdit Name="NameInput"
PlaceHolder="{Loc nano-chat-name-placeholder}" />
</PanelContainer>
</BoxContainer>

<!-- Job input (optional) -->
<BoxContainer Orientation="Vertical" Margin="0 4">
<Label Text="{Loc nano-chat-job-label}"
StyleClasses="LabelHeading" />
<PanelContainer StyleClasses="ButtonSquare">
<LineEdit Name="JobInput"
PlaceHolder="{Loc nano-chat-job-placeholder}" />
</PanelContainer>
</BoxContainer>

<!-- Action buttons -->
<BoxContainer Orientation="Horizontal"
HorizontalAlignment="Right"
Margin="0 8 0 0">
<Button Name="CancelButton"
Text="{Loc nano-chat-cancel}"
StyleClasses="OpenRight"
MinSize="80 0" />
<Button Name="ConfirmButton"
Text="{Loc nano-chat-confirm}"
StyleClasses="OpenLeft"
MinSize="80 0"
Disabled="True" />
</BoxContainer>
</BoxContainer>
</PanelContainer>
</DefaultWindow>
109 changes: 109 additions & 0 deletions Content.Client/_DV/CartridgeLoader/Cartridges/EditChatPopup.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using Content.Shared.Access.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client._DV.CartridgeLoader.Cartridges;

[GenerateTypedNameReferences]
public sealed partial class EditChatPopup : DefaultWindow
{
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 = "";
private int _maxNameLength;
private int _maxIdJobLength;

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

public EditChatPopup(int maxNameLength, int maxIdJobLength)
{
RobustXamlLoader.Load(this);

_maxNameLength = maxNameLength;
_maxIdJobLength = maxIdJobLength;

// margins trolling
ContentsContainer.Margin = new Thickness(3);

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

NameInput.OnTabComplete += _ => JobInput.GrabKeyboardFocus();
NameInput.OnTextEntered += _ => EditChat();

JobInput.OnTabComplete += _ => NameInput.GrabKeyboardFocus();
JobInput.OnTextEntered += _ => EditChat();

// Input validation
NameInput.OnTextChanged += args =>
{
if (args.Text.Length > _maxNameLength)
NameInput.Text = args.Text[.._maxNameLength];
ValidateInputs();
};

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

private void ValidateInputs()
{
var isValid = !string.IsNullOrWhiteSpace(NumberInput.Text) &&
!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()
{
if (ConfirmButton.Disabled || !uint.TryParse(NumberInput.Text, out var number))
return;

var name = NameInput.Text.Trim();
var job = string.IsNullOrWhiteSpace(JobInput.Text) ? null : JobInput.Text.Trim();

OnContactEdited?.Invoke(number, name, job);
Close();
}

public void ClearInputs()
{
NameInput.Text = string.Empty;
JobInput.Text = string.Empty;
ValidateInputs();
}

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;
}
}
48 changes: 48 additions & 0 deletions Content.Client/_DV/CartridgeLoader/Cartridges/NanoChatEntry.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<BoxContainer
xmlns="https://spacestation14.io"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
HorizontalExpand="True">
<Button Name="ChatButton"
StyleClasses="ButtonSquare"
HorizontalExpand="True"
MaxSize="137 64"
Margin="0 1">
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
VerticalExpand="True"
MinWidth="132"
Margin="6 4"
VerticalAlignment="Center">
<!-- Unread indicator dot -->
<PanelContainer Name="UnreadIndicator"
MinSize="8 8"
MaxSize="8 8"
VerticalAlignment="Center"
Margin="0 0 6 0">
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat
BackgroundColor="#17c622"
BorderColor="#0f7a15" />
</PanelContainer.PanelOverride>
</PanelContainer>

<!-- Text container -->
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
VerticalAlignment="Center">
<RichTextLabel Name="NameLabel"
StyleClasses="LabelHeading"
HorizontalExpand="True"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0 -2 0 0" />
<Label Name="JobLabel"
StyleClasses="LabelSubText"
HorizontalExpand="True"
ClipText="False"
HorizontalAlignment="Center" />
</BoxContainer>
</BoxContainer>
</Button>
</BoxContainer>
Loading
Loading