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 000000000..6ac95c629
--- /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 000000000..c1121f9b7
--- /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 000000000..a36877011
--- /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 71e82698d..353d86370 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
|