140 lines
3.6 KiB
C#
140 lines
3.6 KiB
C#
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 null)
|
|
{
|
|
return;
|
|
}
|
|
user.Color = color;
|
|
ctx.Db.User.Identity.Update(user);
|
|
}
|
|
|
|
private static string ValidateColor(string color)
|
|
{
|
|
if (color[0] == '#' && color.Length != 4 || color.Length != 7)
|
|
{
|
|
throw new Exception("Invalid Hex 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.");
|
|
}
|
|
}
|
|
}
|