From 4f8fd9dc18148606b4a193802b2fefa4c689b92c Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Thu, 22 May 2025 14:19:37 -0400 Subject: [PATCH] add color to server --- client/UserUtils.cs | 17 +++++++++++ client/UserUtils.cs.uid | 1 + client/chat/ChatLog.cs | 37 +++++++++++++---------- client/chat/ChatOptions.cs | 61 +++++++++++++++++++++++++++++++++----- server/Lib.cs | 26 +++++++++++++++- 5 files changed, 118 insertions(+), 24 deletions(-) create mode 100644 client/UserUtils.cs create mode 100644 client/UserUtils.cs.uid diff --git a/client/UserUtils.cs b/client/UserUtils.cs new file mode 100644 index 0000000..c37936f --- /dev/null +++ b/client/UserUtils.cs @@ -0,0 +1,17 @@ +using Godot; +using SpacetimeDB.Types; + +public static class UserUtils +{ + public static Color ParseColor(User user) + { + return user.Color != null && Color.HtmlIsValid(user.Color) + ? Color.FromHtml(user.Color) + : Colors.AliceBlue; + } + + public static string UserNameOrIdentity(User user) + { + return user != null ? user.Name ?? user.Identity.ToString()[..8] : "unknown"; + } +} diff --git a/client/UserUtils.cs.uid b/client/UserUtils.cs.uid new file mode 100644 index 0000000..2ced1f1 --- /dev/null +++ b/client/UserUtils.cs.uid @@ -0,0 +1 @@ +uid://dnaxnse8ipje0 diff --git a/client/chat/ChatLog.cs b/client/chat/ChatLog.cs index 4f55792..aeffd33 100644 --- a/client/chat/ChatLog.cs +++ b/client/chat/ChatLog.cs @@ -4,7 +4,7 @@ using SpacetimeDB.Types; public partial class ChatLog : RichTextLabel { - const string SystemColor = "#747474"; + private static readonly Color SystemColor = Colors.Gray; public override void _EnterTree() { @@ -17,14 +17,9 @@ public partial class ChatLog : RichTextLabel RegisterSubscriptions(conn); } - string UserNameOrIdentity(User user) + void PushMessage(string name, string message, Color color) { - return user != null ? user.Name ?? user.Identity.ToString()[..8] : "unknown"; - } - - void PushMessage(string name, string message, string color) - { - string entry = $"[color={color}]{name}:[/color] {message}"; + string entry = $"[color={color.ToHtml()}]{name}:[/color] {message}"; this.Text += $"{entry}\n"; } @@ -47,7 +42,11 @@ public partial class ChatLog : RichTextLabel { return; } - PushMessage("System", $"{UserNameOrIdentity(insertedValue)} connected", SystemColor); + PushMessage( + "System", + $"{UserUtils.UserNameOrIdentity(insertedValue)} connected", + SystemColor + ); } void User_OnUpdate(EventContext ctx, User oldValue, User newValue) @@ -56,7 +55,7 @@ public partial class ChatLog : RichTextLabel { PushMessage( "System", - $"{UserNameOrIdentity(oldValue)} renamed to {UserNameOrIdentity(newValue)}", + $"{UserUtils.UserNameOrIdentity(oldValue)} renamed to {UserUtils.UserNameOrIdentity(newValue)}", SystemColor ); } @@ -64,11 +63,19 @@ public partial class ChatLog : RichTextLabel { if (newValue.Online) { - PushMessage("System", $"{UserNameOrIdentity(newValue)} connected", SystemColor); + PushMessage( + "System", + $"{UserUtils.UserNameOrIdentity(newValue)} connected", + SystemColor + ); } else { - PushMessage("System", $"{UserNameOrIdentity(newValue)} disconnected", SystemColor); + PushMessage( + "System", + $"{UserUtils.UserNameOrIdentity(newValue)} disconnected", + SystemColor + ); } } } @@ -76,12 +83,12 @@ public partial class ChatLog : RichTextLabel void Message_OnInsert(EventContext ctx, Message insertedValue) { User sender = ctx.Db.User.Identity.Find(insertedValue.Sender); - string color = sender.Identity.Equals(Spacetime.Instance.Identity) ? "blue" : "red"; + Color color = ctx.Identity == sender.Identity ? UserUtils.ParseColor(sender) : Colors.Red; if (ctx.Event is Event.SubscribeApplied) { - PushMessage(UserNameOrIdentity(sender), insertedValue.Text, color); + PushMessage(UserUtils.UserNameOrIdentity(sender), insertedValue.Text, color); return; } - PushMessage(UserNameOrIdentity(sender), insertedValue.Text, color); + PushMessage(UserUtils.UserNameOrIdentity(sender), insertedValue.Text, color); } } diff --git a/client/chat/ChatOptions.cs b/client/chat/ChatOptions.cs index 041dd75..16c4873 100644 --- a/client/chat/ChatOptions.cs +++ b/client/chat/ChatOptions.cs @@ -12,8 +12,10 @@ public partial class ChatOptions : HBoxContainer Username = GetNode("Username"); ColorPicker = GetNode("ColorPicker"); - Username.TextSubmitted += OnUsernameInput; + Username.TextSubmitted += OnUsernameChanged; Username.FocusExited += ResetUsername; + + ColorPicker.ColorChanged += OnColorChange; } public override void _Ready() @@ -22,24 +24,52 @@ public partial class ChatOptions : HBoxContainer RegisterSubscriptions(conn); } - string UserNameOrIdentity(User user) - { - return user != null ? user.Name ?? user.Identity.ToString()[..8] : "unknown"; - } - void ResetUsername() { - Username.Text = UserNameOrIdentity(Spacetime.Instance.Me); + Username.Text = UserUtils.UserNameOrIdentity(Spacetime.Instance.Me); } - void OnUsernameInput(string text) + void OnUsernameChanged(string text) { Spacetime.Instance.Connection.Reducers.SetName(text); } + void OnColorChange(Color color) + { + Spacetime.Instance.Connection.Reducers.SetColor(color.ToHtml(false)); + } + void RegisterSubscriptions(DbConnection conn) { + conn.Db.User.OnInsert += User_OnInsert; + conn.Db.User.OnUpdate += User_OnUpdate; conn.Reducers.OnSetName += Reducer_OnSetNameEvent; + conn.Reducers.OnSetColor += Reducer_OnSetColorEvent; + } + + void User_OnInsert(EventContext ctx, User insertedValue) + { + // It's me + if (ctx.Identity == insertedValue.Identity) + { + Username.Text = insertedValue.Name; + ColorPicker.Color = + insertedValue.Color != null && Color.HtmlIsValid(insertedValue.Color) + ? Color.FromHtml(insertedValue.Color) + : Colors.AliceBlue; + } + } + + void User_OnUpdate(EventContext ctx, User oldValue, User newValue) + { + if (ctx.Identity == oldValue.Identity && ctx.Identity == newValue.Identity) + { + Username.Text = newValue.Name; + ColorPicker.Color = + newValue.Color != null && Color.HtmlIsValid(newValue.Color) + ? Color.FromHtml(newValue.Color) + : Colors.AliceBlue; + } } /// Our `OnSetNameEvent` callback: print a warning if the reducer failed. @@ -57,4 +87,19 @@ public partial class ChatOptions : HBoxContainer return; } } + + void Reducer_OnSetColorEvent(ReducerEventContext ctx, string color) + { + var e = ctx.Event; + if (e.CallerIdentity != Spacetime.Instance.Identity) + { + // Not me + return; + } + if (e.Status is Status.Failed(var error)) + { + GD.PrintErr($"Failed to change color to {color}: {error}"); + return; + } + } } diff --git a/server/Lib.cs b/server/Lib.cs index 044257e..621d1f9 100644 --- a/server/Lib.cs +++ b/server/Lib.cs @@ -1,3 +1,4 @@ +using System.Text.RegularExpressions; using SpacetimeDB; public static partial class Module @@ -8,6 +9,7 @@ public static partial class Module [PrimaryKey] public Identity Identity; public string? Name; + public string? Color; public bool Online; } @@ -42,6 +44,29 @@ public static partial class Module return name; } + [Reducer] + public static void SetColor(ReducerContext ctx, string color) + { + color = ValidateColor(color); + + var user = ctx.Db.User.Identity.Find(ctx.Sender); + if (user is not null) + { + user.Color = color; + ctx.Db.User.Identity.Update(user); + } + } + + private static string ValidateColor(string color) + { + var regex = new Regex("^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"); + if (!regex.IsMatch(color)) + { + throw new Exception("Invalid color code"); + } + return color; + } + [Reducer] public static void SendMessage(ReducerContext ctx, string text) { @@ -113,4 +138,3 @@ public static partial class Module } } } -