using Godot; using SpacetimeDB; using SpacetimeDB.Types; public partial class ChatWindow : VBoxContainer { const string SystemColor = "#747474"; private LineEdit _userNameInput; private ChatLog _log; private LineEdit _input; // Called when the node enters the scene tree for the first time. public override void _EnterTree() { _userNameInput = GetNode("Options/Username"); _log = GetNode("ChatLog"); _input = GetNode("ChatInput"); _log.Text = ""; } string UserNameOrIdentity(User user) { return user != null ? user.Name ?? user.Identity.ToString()[..8] : "unknown"; } // Called when the node enters the scene tree for the first time. public override void _Ready() { DbConnection conn = Spacetime.Instance.Connection; _userNameInput.TextSubmitted += OnUsernameInput; _userNameInput.FocusExited += ResetUsername; _input.TextSubmitted += OnMessageInput; conn.Db.User.OnInsert += User_OnInsert; conn.Db.User.OnUpdate += User_OnUpdate; conn.Db.Message.OnInsert += Message_OnInsert; conn.Reducers.OnSetName += Reducer_OnSetNameEvent; conn.Reducers.OnSendMessage += Reducer_OnSendMessageEvent; } void ResetUsername() { _userNameInput.Text = UserNameOrIdentity(Spacetime.Instance.Me); } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { } private void OnUsernameInput(string text) { Spacetime.Instance.Connection.Reducers.SetName(text); } private void OnMessageInput(string text) { Spacetime.Instance.Connection.Reducers.SendMessage(text); _input.Text = ""; } void User_OnInsert(EventContext ctx, User insertedValue) { if (ctx.Event is Event.SubscribeApplied) { if (insertedValue.Identity == Spacetime.Instance.Identity) { ResetUsername(); } return; } _log.PushMessage("System", $"{UserNameOrIdentity(insertedValue)} connected", SystemColor); } void User_OnUpdate(EventContext ctx, User oldValue, User newValue) { if (oldValue.Name != newValue.Name) { _log.PushMessage("System", $"{UserNameOrIdentity(oldValue)} renamed to {UserNameOrIdentity(newValue)}", SystemColor); } if (oldValue.Online != newValue.Online) { if (newValue.Online) { _log.PushMessage("System", $"{UserNameOrIdentity(newValue)} connected", SystemColor); } else { _log.PushMessage("System", $"{UserNameOrIdentity(newValue)} disconnected", SystemColor); } } } 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"; if (ctx.Event is Event.SubscribeApplied) { _log.PushMessage(UserNameOrIdentity(sender), insertedValue.Text, color); return; } _log.PushMessage(UserNameOrIdentity(sender), insertedValue.Text, color); } /// Our `OnSetNameEvent` callback: print a warning if the reducer failed. void Reducer_OnSetNameEvent(ReducerEventContext ctx, string name) { 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 name to {name}: {error}"); return; } } /// Our `OnSendMessageEvent` callback: print a warning if the reducer failed. void Reducer_OnSendMessageEvent(ReducerEventContext ctx, string text) { var e = ctx.Event; if (e.CallerIdentity == Spacetime.Instance.Identity && e.Status is Status.Failed(var error)) { GD.PrintErr($"Failed to send message {text}: {error}"); } } }