Guild discovery admin ui

This commit is contained in:
Rory&
2026-02-21 20:03:02 +01:00
parent d92b53ed8f
commit 28db92c3be
4 changed files with 147 additions and 4 deletions

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;
}