UI/Screen/Chat/Chat.razor
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
@implements Component.INetworkListener
<root>
<div class="output" @ref="OutputPanel">
@foreach (var entry in Entries)
{
<div class="chat_entry" style="opacity: @GetEntryOpacity(entry)">
@if (entry.steamId.ValueUnsigned != 0)
{
<img class="avatar" src=@($"avatar:{entry.steamId}") />
}
else
{
<div class="avatar avatar--empty"></div>
}
<div class="author">@entry.author</div>
<div class="message">@entry.message</div>
</div>
}
</div>
<div class="input">
<TextEntry Placeholder="Enter to chat" @ref="InputBox" onsubmit="@ChatFinished"></TextEntry>
</div>
</root>
@code
{
[Property] public string MyStringValue { get; set; } = "Hello World!";
[Property] public SoundEvent OpenSound { get; set; } = ResourceLibrary.Get<SoundEvent>( "combo_finish" );
[Property] public SoundEvent CloseSound { get; set; } = ResourceLibrary.Get<SoundEvent>( "combo_finish" );
[Property] public SoundEvent SendSound { get; set; } = ResourceLibrary.Get<SoundEvent>( "combo_finish" );
TextEntry InputBox;
Panel OutputPanel;
public record Entry(SteamId steamId, string author, string message, RealTimeSince timeSinceAdded);
List<Entry> Entries = new();
private bool _wasOpen;
private RealTimeSince _timeSinceFadeUpdate;
/// <summary>
/// the hash determines if the system should be rebuilt. If it changes, it will be rebuilt
/// </summary>
protected override int BuildHash() => System.HashCode.Combine(MyStringValue);
protected override void OnUpdate()
{
if (!InputBox.IsValid())
return;
Panel.AcceptsFocus = false;
if (Input.Pressed("chat"))
{
InputBox.Focus();
}
var isOpen = InputBox.HasFocus;
if (isOpen != _wasOpen)
{
_wasOpen = isOpen;
_timeSinceFadeUpdate = 0f;
PlayUiSound( isOpen ? OpenSound : CloseSound );
OutputPanel?.TryScrollToBottom();
StateHasChanged();
}
else if (!isOpen && Entries.Count > 0 && _timeSinceFadeUpdate > 0.1f)
{
_timeSinceFadeUpdate = 0f;
StateHasChanged();
}
if (OutputPanel is not null)
OutputPanel.PreferScrollToBottom = true;
SetClass("open", isOpen);
}
void ChatFinished()
{
var text = InputBox.Text;
InputBox.Text = "";
if (string.IsNullOrWhiteSpace(text))
return;
PlayUiSound( SendSound );
AddText(Connection.Local.SteamId, Sandbox.Utility.Steam.PersonaName, text);
}
[Rpc.Broadcast]
public void AddText(SteamId steamId, string author, string message)
{
message = message.Truncate(300);
if (string.IsNullOrWhiteSpace(message))
return;
Log.Info($"{author}: {message}");
Entries.Add(new Entry(steamId, author, message, 0.0f));
OutputPanel?.TryScrollToBottom();
StateHasChanged();
}
void Component.INetworkListener.OnConnected(Connection channel)
{
if (IsProxy) return;
AddText(channel.SteamId, ">", $"{channel.DisplayName} has joined the game");
}
void Component.INetworkListener.OnDisconnected(Connection channel)
{
if (IsProxy) return;
AddText(channel.SteamId, "<", $"{channel.DisplayName} has left the game");
}
private string GetEntryOpacity(Entry entry)
{
if (InputBox?.HasFocus ?? false)
return "1";
const float fadeStart = 3f;
const float fadeEnd = 10f;
var t = (float)entry.timeSinceAdded;
if (t <= fadeStart)
return "1";
if (t >= fadeEnd)
return "0";
var alpha = 1f - (t - fadeStart) / (fadeEnd - fadeStart);
return alpha.ToString("0.###");
}
private static void PlayUiSound( SoundEvent sound )
{
if ( sound is null || !sound.IsValid() )
return;
Sound.Play( sound );
}
}