Editor/MessageWidget.cs
using Editor;
using Sandbox;
using System.Collections.Generic;
namespace EditorAllChat;

public class MessageWidget : Widget
{
	public MessageObject Message;

	Friend Friend;
	TextureWidget AvatarImage;
	Label UsernameLabel;

	bool _hasInit = false;
	int _lastHash = 0;

	public MessageWidget( MessageObject message ) : base( null )
	{
		HorizontalSizeMode = SizeMode.Flexible;
		Layout = Layout.Row();
		Layout.Spacing = 4;

		Message = message;
		Friend = new Friend( message.SteamId );

		var timestampLabel = Layout.Add( new Label( message.Timestamp.ToShortTimeString() ) );
		timestampLabel.SetStyles( "color: gray; font-size: 8px;" );
		timestampLabel.FixedWidth = 38;
		timestampLabel.Alignment = TextFlag.RightCenter;

		AvatarImage = Layout.Add( new TextureWidget() );
		AvatarImage.Texture = Sandbox.Texture.Load( $"avatar:{message.SteamId}" );
		AvatarImage.FixedSize = 18;

		UsernameLabel = Layout.Add( new Label( Friend.Name ) );
		UsernameLabel.MaximumWidth = 128;
		UsernameLabel.SetStyles( "font-weight: bold;" );

		var label = Layout.Add( new Label( message.Message ) );
		label.HorizontalSizeMode = SizeMode.Flexible;
		label.WordWrap = true;

		_hasInit = true;
	}

	[EditorEvent.Frame]
	void OnFrame()
	{
		if ( !_hasInit ) return;
		var currentGame = Friend.GetRichPresence( "gametitle" ) ?? "";
		var hash = System.HashCode.Combine( Friend.Id, Friend.Name, Friend.IsOnline, Friend.IsPlayingThisGame, currentGame );
		if ( hash != _lastHash )
		{
			_lastHash = hash;
			UsernameLabel.Text = Friend.Name;
			UsernameLabel.Color = Friend.IsPlayingThisGame ? Theme.Green : Friend.IsOnline ? Color.Blue : Color.Gray;
			UsernameLabel.ToolTip = string.IsNullOrEmpty( currentGame ) ? "" : $"In Project: {Friend.GetRichPresence( "gametitle" )}";
			AvatarImage.Texture = Sandbox.Texture.Load( $"avatar:{Friend.Id}" );
			Update();
		}
	}

	protected override void OnContextMenu( ContextMenuEvent e )
	{
		base.OnContextMenu( e );

		if ( !string.IsNullOrWhiteSpace( AllChatWindow.ADMIN_PASSWORD ) )
		{
			var menu = new ContextMenu( this );
			menu.AddOption( "Delete Message", "delete", () =>
			{
				var url = AllChatWindow.API_URL + "/admin/message/" + Message.IdString;
				Http.RequestAsync( url, "DELETE", Http.CreateJsonContent( new Dictionary<string, string>
				{
					{ "secret", AllChatWindow.ADMIN_PASSWORD }
				} ) );
			} );

			menu.OpenAtCursor();
		}
	}
}