summary refs log tree commit diff
path: root/extra/admin-api/Utilities/Spacebar.Client/Components
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2026-05-17 03:14:34 +0200
committerRory& <root@rory.gay>2026-05-17 03:14:34 +0200
commit8f76cc5edf8588c8bec36daf0bbdc7d699d38800 (patch)
treed84535fa9d66a674ed652d0b7d5e6cb4865ba7ac /extra/admin-api/Utilities/Spacebar.Client/Components
parentAdd WIP client (diff)
downloadserver-ts-8f76cc5edf8588c8bec36daf0bbdc7d699d38800.tar.xz
client: basic message parsing, gateway listening
Diffstat (limited to 'extra/admin-api/Utilities/Spacebar.Client/Components')
-rw-r--r--extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor158
-rw-r--r--extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.cs30
-rw-r--r--extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.css7
-rw-r--r--extra/admin-api/Utilities/Spacebar.Client/Components/ClientManager.razor64
4 files changed, 248 insertions, 11 deletions
diff --git a/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor b/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor
new file mode 100644

index 00000000..6ac95c62 --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor
@@ -0,0 +1,158 @@ +@using System.Collections.ObjectModel +@using System.Text.Json +@using System.Text.RegularExpressions +@using ArcaneLibs.Blazor.Components.Services +@using ArcaneLibs.Extensions +@using Spacebar.Client.Core +@using Spacebar.Models.Generic +@inject JsConsoleService jsConsole + +@foreach (var message in Messages) { + if (message.Type == 0 || message.Type == 19) { + if (message.Type == 19) { + <span>╭⎯⎯ <b>@message.ReferencedMessage?.Author.Username</b> @string.Join("", message.ReferencedMessage?.Content?.Split("\n")[0].Take(100) ?? [])</span> + <br/> + } + + <b class="@(string.Join(" ", GetMemberRoles(message.GuildId.Value, message.Author.Id).Select(x => $"role_{x}")))">@message.Author.Username</b> + <br/> + <span>@message.Content</span> + <br/> + <div style="background-color: #FFFF0033;"> + @GetMessageContent(message) + </div> + <br/> + <div style="background-color: #FF00FF33;"> + @GetMessageContentEnumerated(message) + </div> + <br/> + @if (message.Attachments.Any()) { + @foreach (var att in message.Attachments) { + @if (att.ContentType.StartsWith("image/")) { + <img src="@att.ProxyUrl" class="attachmentImage" alt="Attachment image"/> + } + else { + <span class="code">@att.ToJson()</span> + } + + <br/> + } + } + + <br/> + } + else { + <span class="code" style="background-color: #772222"> + Unknown message type @message.Type + <details> + <summary>View raw message data</summary> + @message.ToJson(indent: true) + </details> + </span> + <br/> + } +} + +@code { + + [Parameter] + public required ObservableCollection<Message> Messages { get; set; } + + public List<string> GetMemberRoles(long guildId, long memberId) { + // App.ClientManager.ClientState.Guilds[guildId]. + return []; + } + + private static string[] _partColors = [ + "#FFFF0033", + "#FF00FF33", + "#00FFFF33", + "#FF000033", + "#00FF0033", + "#0000FF33" + ]; + + private static bool _shouldRenderMarkdownZones = true; + private RenderFragment GetMessageContent(Message msg) => builder => { + var fullContent = msg.Content; + int i = 0, line = 0; + Regex[][] groupedRegexes = [[MarkdownBoldRegex, MarkdownCodeblockRegex], [MarkdownCodeRegex, MarkdownItalicRegex]]; + Regex[] regexes = groupedRegexes.SelectMany(x => x).ToArray(); + + var lines = fullContent.Split('\n'); + foreach (var lineContent in lines) { + var content = lineContent; + var elemType = "span"; + var shouldBr = true; + if (content.StartsWith("-#")) { + elemType = "sub"; + content = content[2..].TrimStart(); + } + else if (content.StartsWith("#")) { + var hdrLevel = content.TakeWhile(x => x == '#').Count(); + content = content[hdrLevel..]; + shouldBr = false; + elemType = "h" + hdrLevel; + } + else if (content.StartsWith("*")) { + + } + + var indicies = regexes.Select(r => new { + regex = r, + regexStr = r.ToString(), + matchIdx = r.Match(content).Index, + matchContent = r.Match(content).Value + }).Where(x => x.matchIdx != 0).ToList(); + + if (indicies.Any()) { + jsConsole.Info("Found indices: ", JsonSerializer.SerializeToElement(indicies, new JsonSerializerOptions() { + IncludeFields = true + })); + + builder.OpenElement(i++, elemType); + { + if (_shouldRenderMarkdownZones) builder.AddAttribute(i++, "style", $"background-color: {_partColors[i % _partColors.Length]}"); + builder.AddContent(i++, content![..indicies.Min(x => x.matchIdx)]); + content = content![..indicies.Min(x => x.matchIdx)]; + } + builder.CloseComponent(); + } + else { + builder.OpenElement(i++, elemType); + { + if (_shouldRenderMarkdownZones && elemType != "span") builder.AddAttribute(i++, "style", $"background-color: {_partColors[i % _partColors.Length]}"); + builder.AddContent(i++, content!); + } + builder.CloseComponent(); + // continue; + } + + if (line++ <= lines.Length && shouldBr) { + builder.AddMarkupContent(i++, "<br/>"); + } + } + }; + + private RenderFragment GetMessageContentEnumerated(Message msg) => builder => { + int i = 0; + builder.OpenElement(i++, "div"); + builder.AddAttribute(i++, "id", "msg"+msg.Id); + foreach (var comp in new MarkdownEnumerator().EnumerateMarkdownComponents(msg.Content)) { + if (comp is ContainerMarkdownNode) { + + } + else { + builder.OpenElement(i++, "span"); + builder.AddAttribute(i++, "class", "mdErrorBlinkBg"); + // jsConsole.Info("frames:", builder.GetFrames().Array[0].); + // builder.AddAttribute(i++, ); + builder.AddContent(i++, $"Unknown component type: {comp.GetType().FullName}"); + builder.CloseElement(); + } + } + + builder.CloseElement(); + }; + +} \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.cs b/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.cs new file mode 100644
index 00000000..c1121f9b --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.cs
@@ -0,0 +1,30 @@ +using System.Text.RegularExpressions; + +namespace Spacebar.Client.Components; + +public partial class ChannelMessageList { + [GeneratedRegex(@"\*\*(.*)\*\*")] + private static partial Regex MarkdownBoldRegex { get; } + + [GeneratedRegex(@"\*(.*)\*")] + private static partial Regex MarkdownItalicRegex { get; } + + [GeneratedRegex(@"```((?<lang>.*)\n)(?<content>.*)```")] + private static partial Regex MarkdownCodeblockRegex { get; } + + [GeneratedRegex(@"``?(.*)`?`")] + private static partial Regex MarkdownCodeRegex { get; } + + [GeneratedRegex(@"<#(\d*)>")] + private static partial Regex MarkdownChannelMentionRegex { get; } + + [GeneratedRegex(@"<@(\d*)>")] + private static partial Regex MarkdownUserMentionRegex { get; } + + [GeneratedRegex(@"<@&(\d*)>")] + private static partial Regex MarkdownRoleMentionRegex { get; } + + [GeneratedRegex(@"<:(?<name>[a-zA-Z0-9]*?):(?<emojiId>\d*>)")] + private static partial Regex MarkdownEmojiMentionRegex { get; } + +} \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.css b/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.css new file mode 100644
index 00000000..a3687701 --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.Client/Components/ChannelMessageList.razor.css
@@ -0,0 +1,7 @@ +.attachmentImage { + max-height: 300px; + max-width: 300px; + object-position: center; + object-fit: cover; +} + diff --git a/extra/admin-api/Utilities/Spacebar.Client/Components/ClientManager.razor b/extra/admin-api/Utilities/Spacebar.Client/Components/ClientManager.razor
index 71e82698..353d8637 100644 --- a/extra/admin-api/Utilities/Spacebar.Client/Components/ClientManager.razor +++ b/extra/admin-api/Utilities/Spacebar.Client/Components/ClientManager.razor
@@ -2,52 +2,95 @@ @using ArcaneLibs.Extensions @using Spacebar.Client.Core @using Spacebar.Client.WebCore +@using Spacebar.Client.WebCore.Client @using Spacebar.Models.Gateway @inject SessionStore sessionStore @inject SpacebarClientProviderService clientProvider @inject JsConsoleService jsConsole <DebugBanner Name="@GetType().Name" @ref="_dbgBanner"/> -<CascadingValue TValue="AuthenticatedSpacebarClient" Value="@_client"> +<CascadingValue TValue="AuthenticatedSpacebarClient" Value="@Client"> @ChildContent </CascadingValue> @code { private DebugBanner _dbgBanner = null!; - private AuthenticatedSpacebarClient? _client { get; set; } + private bool _readyReceived = false; + + public ClientManager() { + ClientAvailable = Task.Run(async () => { + while (Client is null) await Task.Delay(50); + ClientAvailable = null; + }); + ClientReady = Task.Run(async () => { + while (!_readyReceived) await Task.Delay(50); + ClientAvailable = null; + }); + } + + public AuthenticatedSpacebarClient? Client { get; set; } + + public ClientStateContainer ClientState { get; set; } = new(); [Parameter] public required RenderFragment ChildContent { get; set; } + public Task? ClientAvailable { get; set; } + public Task? ClientReady { get; set; } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; await _dbgBanner.SetStatus("Preparing for launch!"); await Task.Delay(125); var session = await sessionStore.GetCurrentSessionAsync(); if (session != null) { - _client = await clientProvider.GetAuthenticatedClientAsync(session.ServerName, session.AccessToken); + Client = await clientProvider.GetAuthenticatedClientAsync(session.ServerName, session.AccessToken); await _dbgBanner.SetStatus($"Got authenticated client for {session.ProfileCache.Username}#{session.ProfileCache.Discriminator} on {session.ServerName}! Connecting to gateway..."); - _client.Gateway.IdentifyData.ClientProperties = new IdentifyClientProperties() { + Client.Gateway.IdentifyData.ClientProperties = new IdentifyClientProperties() { HasClientMods = false, ApplicationArchitecture = "wasm" }.ToJsonNode().AsObject(); StateHasChanged(); - await _client.Gateway.Connect(); - _ = _client.Gateway.Start().ContinueWith(ct => { + await Client.Gateway.Connect(); + _ = Client.Gateway.Start().ContinueWith(ct => { jsConsole.Warn("[ClientManager] Heartbeat loop exited!"); if (ct.IsFaulted) { jsConsole.Error("Unhandled exception during gateway connection:", ct.Exception.ToString()); throw ct.Exception; } }); - _client.Gateway.OnceGatewayMessage.Add(async msg => { + Client.Gateway.OnceGatewayMessage.Add(async msg => { if (msg is { Opcode: GatewayOpcode.S2CDispatch, DispatchEventType: "READY" }) { - await _dbgBanner.SetStatus($"Got READY from gateway"); - await _dbgBanner.SetStatus(null, 1750); + await _dbgBanner.SetStatus($"Got READY from gateway, deserializing..."); var content = msg.GetData<ReadyResponse>(); - await jsConsole.Info("Parsed ready payload:", content); + await _dbgBanner.SetStatus($"Deserialized READY from gateway, handling..."); + // ClientState.Guilds.AddRange(content.Guilds.ToDictionary(x=>x.Id, x=>x)); + foreach (var guild in content.Guilds) { + ClientState.Guilds.Add(guild.Id, guild); + await _dbgBanner.SetStatus($"Deserialized READY from gateway, handling... guilds ({ClientState.Guilds.Count})"); + await Task.Delay(1); + } + + foreach (var guild in content.Relationships) { + // ClientState.Relationships.Add(guild.Id, guild); + await _dbgBanner.SetStatus($"Deserialized READY from gateway, handling... guilds ({ClientState.Guilds.Count}), relationships (0)"); + await Task.Delay(1); + } + + await jsConsole.Info("Parsed ready payload:", new { original = msg.EventData, parsed = content }); + await _dbgBanner.SetStatus($"Done handling ready!"); + _readyReceived = true; + _ = _dbgBanner.SetStatus(null, 1750); + return false; + } + + if (msg is { Opcode: GatewayOpcode.S2CDispatch, DispatchEventType: "READY_SUPPLEMENTAL" }) { + await _dbgBanner.SetStatus("Received READY_SUPPLEMENTAL..."); + await jsConsole.Info("Parsed ready_supplemental payload", new { original = msg.EventData }); + _ =_dbgBanner.SetStatus(null, 1750); return true; } + return false; }); } @@ -55,7 +98,6 @@ await _dbgBanner.SetStatus("No session marked as current... :("); await _dbgBanner.SetStatus(null, 1750); } - } } \ No newline at end of file