diff --git a/server/Lib.cs b/server/Lib.cs index 621d1f9..b5d9812 100644 --- a/server/Lib.cs +++ b/server/Lib.cs @@ -1,140 +1 @@ -using System.Text.RegularExpressions; -using SpacetimeDB; - -public static partial class Module -{ - [Table(Name = "User", Public = true)] - public partial class User - { - [PrimaryKey] - public Identity Identity; - public string? Name; - public string? Color; - public bool Online; - } - - [Table(Name = "Message", Public = true)] - public partial class Message - { - public Identity Sender; - public Timestamp Sent; - public string Text = ""; - } - - [Reducer] - public static void SetName(ReducerContext ctx, string name) - { - name = ValidateName(name); - - var user = ctx.Db.User.Identity.Find(ctx.Sender); - if (user is not null) - { - user.Name = name; - ctx.Db.User.Identity.Update(user); - } - } - - /// Takes a name and checks if it's acceptable as a user's name. - private static string ValidateName(string name) - { - if (string.IsNullOrEmpty(name)) - { - throw new Exception("Names must not be empty"); - } - 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) - { - text = ValidateMessage(text); - Log.Info(text); - ctx.Db.Message.Insert( - new Message - { - Sender = ctx.Sender, - Text = text, - Sent = ctx.Timestamp, - } - ); - } - - /// Takes a message's text and checks if it's acceptable to send. - private static string ValidateMessage(string text) - { - if (string.IsNullOrEmpty(text)) - { - throw new ArgumentException("Messages must not be empty"); - } - return text; - } - - [Reducer(ReducerKind.ClientConnected)] - public static void ClientConnected(ReducerContext ctx) - { - Log.Info($"Connect {ctx.Sender}"); - var user = ctx.Db.User.Identity.Find(ctx.Sender); - - if (user is not null) - { - // If this is a returning user, i.e., we already have a `User` with this `Identity`, - // set `Online: true`, but leave `Name` and `Identity` unchanged. - user.Online = true; - ctx.Db.User.Identity.Update(user); - } - else - { - // If this is a new user, create a `User` object for the `Identity`, - // which is online, but hasn't set a name. - ctx.Db.User.Insert( - new User - { - Name = null, - Identity = ctx.Sender, - Online = true, - } - ); - } - } - - [Reducer(ReducerKind.ClientDisconnected)] - public static void ClientDisconnected(ReducerContext ctx) - { - var user = ctx.Db.User.Identity.Find(ctx.Sender); - - if (user is not null) - { - // This user should exist, so set `Online: false`. - user.Online = false; - ctx.Db.User.Identity.Update(user); - } - else - { - // User does not exist, log warning - Log.Warn("Warning: No user found for disconnected client."); - } - } -} +public static partial class Module { } diff --git a/server/module/MessageTable.cs b/server/module/MessageTable.cs new file mode 100644 index 0000000..50794f2 --- /dev/null +++ b/server/module/MessageTable.cs @@ -0,0 +1,37 @@ +using SpacetimeDB; + +public static partial class Module +{ + [Table(Name = "Message", Public = true)] + public partial class Message + { + public Identity Sender; + public Timestamp Sent; + public string Text = ""; + } + + /// Takes a message's text and checks if it's acceptable to send. + private static string ValidateMessage(string text) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentException("Messages must not be empty"); + } + return text; + } + + [Reducer] + public static void SendMessage(ReducerContext ctx, string text) + { + text = ValidateMessage(text); + Log.Info(text); + ctx.Db.Message.Insert( + new Message + { + Sender = ctx.Sender, + Text = text, + Sent = ctx.Timestamp, + } + ); + } +} diff --git a/server/module/Reducers.cs b/server/module/Reducers.cs new file mode 100644 index 0000000..9cca5fd --- /dev/null +++ b/server/module/Reducers.cs @@ -0,0 +1,50 @@ +using SpacetimeDB; + +public static partial class Module +{ + [Reducer(ReducerKind.ClientConnected)] + public static void ClientConnected(ReducerContext ctx) + { + Log.Info($"Connect {ctx.Sender}"); + var user = ctx.Db.User.Identity.Find(ctx.Sender); + + if (user is not null) + { + // If this is a returning user, i.e., we already have a `User` with this `Identity`, + // set `Online: true`, but leave `Name` and `Identity` unchanged. + user.Online = true; + ctx.Db.User.Identity.Update(user); + } + else + { + // If this is a new user, create a `User` object for the `Identity`, + // which is online, but hasn't set a name. + ctx.Db.User.Insert( + new User + { + Name = null, + Identity = ctx.Sender, + Online = true, + } + ); + } + } + + [Reducer(ReducerKind.ClientDisconnected)] + public static void ClientDisconnected(ReducerContext ctx) + { + var user = ctx.Db.User.Identity.Find(ctx.Sender); + + if (user is not null) + { + // This user should exist, so set `Online: false`. + user.Online = false; + ctx.Db.User.Identity.Update(user); + } + else + { + // User does not exist, log warning + Log.Warn("Warning: No user found for disconnected client."); + } + } +} diff --git a/server/module/UserTable.cs b/server/module/UserTable.cs new file mode 100644 index 0000000..1137aa6 --- /dev/null +++ b/server/module/UserTable.cs @@ -0,0 +1,61 @@ +using System.Text.RegularExpressions; +using SpacetimeDB; + +public static partial class Module +{ + [Table(Name = "User", Public = true)] + public partial class User + { + [PrimaryKey] + public Identity Identity; + public string? Name; + public string? Color; + public bool Online; + } + + /// Takes a name and checks if it's acceptable as a user's name. + private static string ValidateName(string name) + { + if (string.IsNullOrEmpty(name)) + { + throw new Exception("Names must not be empty"); + } + return name; + } + + [Reducer] + public static void SetName(ReducerContext ctx, string name) + { + name = ValidateName(name); + + var user = ctx.Db.User.Identity.Find(ctx.Sender); + if (user is not null) + { + user.Name = name; + 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 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); + } + } +}