summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2026-02-21 20:03:02 +0100
committerRory& <root@rory.gay>2026-02-21 20:03:02 +0100
commit28db92c3be6b7ade01513f356137c5d6d7af4c9e (patch)
tree44381656f7ee1794a04d197df21e85200e5405ad
parentfix uapi deps (diff)
downloadserver-ts-28db92c3be6b7ade01513f356137c5d6d7af4c9e.tar.xz
Guild discovery admin ui
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs6
-rw-r--r--extra/admin-api/Spacebar.AdminApi/Program.cs2
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor84
-rw-r--r--extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css59
4 files changed, 147 insertions, 4 deletions
diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs

index 3f38b3a8..b914abef 100644 --- a/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs
@@ -10,9 +10,9 @@ using Spacebar.Models.Db.Models; namespace Spacebar.AdminApi.Controllers; [ApiController] -[Route("/discovery")] -public class DiscoveryController( - ILogger<DiscoveryController> logger, +[Route("/discovery/guilds")] +public class GuildDiscoveryController( + ILogger<GuildDiscoveryController> logger, SpacebarDbContext db, IServiceProvider sp, SpacebarAspNetAuthenticationService auth, diff --git a/extra/admin-api/Spacebar.AdminApi/Program.cs b/extra/admin-api/Spacebar.AdminApi/Program.cs
index 072fba4b..3b3a9ef1 100644 --- a/extra/admin-api/Spacebar.AdminApi/Program.cs +++ b/extra/admin-api/Spacebar.AdminApi/Program.cs
@@ -60,7 +60,7 @@ builder.Services.AddRequestTimeouts(x => { var app = builder.Build(); app.Use((context, next) => { context.Response.Headers["Access-Control-Allow-Origin"] = "*"; - context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"; + context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS, PATCH"; context.Response.Headers["Access-Control-Allow-Headers"] = "*, Authorization"; if (context.Request.Method == "OPTIONS") { context.Response.StatusCode = 200; diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor new file mode 100644
index 00000000..7e388fbc --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor
@@ -0,0 +1,84 @@ +@page "/GuildDiscovery" +@using System.Net.Http.Headers +@using System.Text.Json.Nodes +@using ArcaneLibs +@using ArcaneLibs.Blazor.Components +@using Spacebar.AdminApi.TestClient.Services +@using Spacebar.Models.AdminApi +@inject Config Config +<h3>GuildDiscovery</h3> +<LinkButton OnClickAsync="@SaveChangesAsync">Save</LinkButton> + +<div class="guild-list"> + @foreach (var guild in _guilds) { + if (guild == null) continue; + + <div class="@("guild-card " + (guild.DiscoveryExcluded ? "excluded" : ""))"> + <img class="guild-icon" src="@(guild.Icon is null ? $"{Config.CdnUrl}/embed/avatars/0.png" : $"{Config.CdnUrl}/icons/{guild.Id}/{guild.Icon}")" alt="@guild.Name"/> + @if (guild.Banner is not null) { + <img class="guild-banner" src="@($"{Config.CdnUrl}/banners/{guild.Id}/{guild.Banner}")" + alt="@guild.Name"/> + } + <h4>@guild.Name</h4> + <p>@guild.Description</p> + + <div class="guild-details"> + <div class="guild-stats"> + <span>@guild.PresenceCount Online</span> + <span>@guild.MemberCount Members</span> + </div> + <label>Exclude</label> + <InputCheckbox @bind-Value="@guild.DiscoveryExcluded"/> + <label>Weight</label> + <InputNumber @bind-Value="@guild.DiscoveryWeight" style="width: 100px;"/> + </div> + </div> + } +</div> + +@code { + + private List<DiscoverableGuildModel?> _guilds = []; + + protected override async Task OnInitializedAsync() { + var hc = new StreamingHttpClient(); + hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken); + var response = await hc.GetAsync(Config.AdminUrl + $"/_spacebar/admin/discovery/guilds?includeExcluded=true"); + if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync()); + var content = response.Content.ReadFromJsonAsAsyncEnumerable<DiscoverableGuildModel>(); + await foreach (var guild in content) { + _guilds.Add(guild); + // var d = Task.Delay(25); + await StateHasChangedAsync(); + // await d; + } + } + + private async Task StateHasChangedAsync() { + StateHasChanged(); + await Task.Delay(1); + } + + protected async Task SaveChangesAsync() { + var hc = new StreamingHttpClient(); + hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken); + var response = await hc.GetAsync(Config.AdminUrl + $"/_spacebar/admin/discovery/guilds?includeExcluded=true"); + if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync()); + var content = response.Content.ReadFromJsonAsAsyncEnumerable<DiscoverableGuildModel>(); + await foreach (var guild in content) { + var matchingGuild = _guilds.FirstOrDefault(g => g!.Id == guild!.Id); + if (matchingGuild == null) continue; + JsonObject update = new(); + + if (matchingGuild.DiscoveryExcluded != guild.DiscoveryExcluded) + update["discovery_excluded"] = matchingGuild.DiscoveryExcluded; + + if (matchingGuild.DiscoveryWeight != guild.DiscoveryWeight) + update["discovery_weight"] = matchingGuild.DiscoveryWeight; + + if (update.Count == 0) continue; + await hc.PatchAsJsonAsync(Config.AdminUrl + $"/_spacebar/admin/discovery/guilds/{guild.Id}", update); + } + } + +} \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css new file mode 100644
index 00000000..b1873d10 --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css
@@ -0,0 +1,59 @@ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { transform: translateY(0); } +} + +.guild-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 3rem 1rem; + padding: 2rem 1rem 1rem; +} + +.guild-card { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem; + padding-top: 40px; + border: 1px solid #ccc; + border-radius: 8px; + width: 100%; + height: 360px; + background-color: var(--bs-card-bg); + margin-top: 32px; + animation: fadeIn 0.5s ease-out forwards; +} + +.guild-icon { + position: absolute; + top: -32px; + width: 64px; + height: 64px; + border-radius: 50%; + margin-bottom: 0; + border: 4px solid var(--bs-body-bg); + z-index: 10; +} + +.guild-banner { + width: 100%; + height: 150px; + object-fit: cover; + margin-bottom: 0.5rem; + position: absolute; + top: 0; + left: 0; + z-index: -1; + mask-image: linear-gradient(to bottom, black 0%, transparent 100%); +} + +.guild-details { + margin-top: auto; + text-align: center; +} + +.guild-card.excluded { + opacity: 0.2; +} \ No newline at end of file