[frontend/pages] Separate remote emoji management into its own page

This commit is contained in:
pancakes 2025-03-07 13:34:48 +10:00 committed by Laura Hausmann
parent 5eec67fe70
commit 01d49ca011
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
5 changed files with 139 additions and 4 deletions

View file

@ -11,8 +11,15 @@ internal class EmojiControllerModel(ApiClient api)
public Task<List<EmojiResponse>> GetAllEmojiAsync() => public Task<List<EmojiResponse>> GetAllEmojiAsync() =>
api.CallAsync<List<EmojiResponse>>(HttpMethod.Get, "/emoji"); api.CallAsync<List<EmojiResponse>>(HttpMethod.Get, "/emoji");
public Task<PaginationWrapper<List<EmojiResponse>>> GetRemoteEmojiAsync(PaginationQuery pq) => public Task<PaginationWrapper<List<EmojiResponse>>> GetRemoteEmojiAsync(string? name, string? host, PaginationQuery pq) =>
api.CallAsync<PaginationWrapper<List<EmojiResponse>>>(HttpMethod.Get, "/emoji/remote", pq); api.CallAsync<PaginationWrapper<List<EmojiResponse>>>(HttpMethod.Get, "/emoji/remote",
(name != null
? QueryString.Create("name", name)
: QueryString.Empty)
+ (host != null
? QueryString.Create("host", host)
: QueryString.Empty)
+ pq);
public Task<PaginationWrapper<List<EmojiResponse>>> GetRemoteEmojiAsync(string instance, PaginationQuery pq) => public Task<PaginationWrapper<List<EmojiResponse>>> GetRemoteEmojiAsync(string instance, PaginationQuery pq) =>
api.CallAsync<PaginationWrapper<List<EmojiResponse>>>(HttpMethod.Get, $"/emoji/remote/{instance}", pq); api.CallAsync<PaginationWrapper<List<EmojiResponse>>>(HttpMethod.Get, $"/emoji/remote/{instance}", pq);

View file

@ -70,6 +70,7 @@
.top-bar { .top-bar {
.action { .action {
margin-left: auto; margin-left: auto;
color: var(--notice-color);
} }
} }

View file

@ -30,6 +30,12 @@
<Icon Name="Icons.FileArrowUp"/> <Icon Name="Icons.FileArrowUp"/>
</span> </span>
} }
@if (State is not State.Loading)
{
<a class="action btn" href="/mod/emojis/remote" title="@Loc["Remote emojis"]">
<Icon Name="Icons.ArrowSquareOut"/>
</a>
}
</SectionContent> </SectionContent>
<div class="body"> <div class="body">
@ -175,7 +181,7 @@
} }
else else
{ {
var res = await Api.Emoji.GetRemoteEmojiAsync(new PaginationQuery()); var res = await Api.Emoji.GetRemoteEmojiAsync(null, null, new PaginationQuery());
PaginationData = res.Links; PaginationData = res.Links;
StoredRemoteEmojis = res.Data; StoredRemoteEmojis = res.Data;
} }
@ -213,7 +219,7 @@
{ {
if (PaginationData?.Next == null) return; if (PaginationData?.Next == null) return;
var pq = new PaginationQuery { MaxId = PaginationData.Next?.Split('=')[1], Limit = PaginationData.Limit }; var pq = new PaginationQuery { MaxId = PaginationData.Next?.Split('=')[1], Limit = PaginationData.Limit };
var res = await Api.Emoji.GetRemoteEmojiAsync(pq); var res = await Api.Emoji.GetRemoteEmojiAsync(null, null, pq);
PaginationData = res.Links; PaginationData = res.Links;
StoredRemoteEmojis.AddRange(res.Data); StoredRemoteEmojis.AddRange(res.Data);
if (EmojiFilter.Length == 0) if (EmojiFilter.Length == 0)

View file

@ -0,0 +1,99 @@
@page "/mod/emojis/remote"
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Extensions.Localization
@using Iceshrimp.Frontend.Components
@using Microsoft.AspNetCore.Components.Sections
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Shared.Schemas.Web
@attribute [Authorize(Roles = "moderator")]
@layout ModerationLayout
@inject ApiService Api;
@inject GlobalComponentSvc Global;
@inject IJSRuntime Js;
@inject IStringLocalizer<Localization> Loc;
@inject ILogger<CustomEmojis> Logger;
<HeadTitle Text="@Loc["Remote Emojis"]"/>
<SectionContent SectionName="top-bar">
<Icon Name="Icons.Smiley"></Icon>
@Loc["Remote Emojis"]
@if (State is not State.Loading)
{
<a class="action btn" href="/mod/emojis" title="@Loc["Local emojis"]">
<Icon Name="Icons.ArrowSquareIn"/>
</a>
}
</SectionContent>
<div class="body">
@if (State is State.Empty or State.Error or State.Loaded)
{
<div class="emoji-search">
<input @bind="EmojiFilter" class="search" type="text" placeholder="@Loc["Name"]" aria-label="search name"/>
<input @bind="HostFilter" class="search" type="text" placeholder="@Loc["Host"]" aria-label="search host"/>
<button @onclick="Search" class="button">@Loc["Search"]</button>
</div>
}
@if (State is State.Loaded)
{
<div class="emoji-list">
@foreach (var emoji in Emojis)
{
<EmojiManagementEntry Emoji="@emoji" Source="remote" GetEmojis="Search"/>
}
</div>
@if (PaginationData is { Next: not null })
{
<ScrollEnd ManualLoad="FetchMore" IntersectionChange="FetchMore"></ScrollEnd>
}
}
@if (State is State.Empty)
{
<i>This instance hasn't received remote emojis</i>
}
@if (State is State.Loading)
{
<div class="loading">
<LoadingSpinner Scale="2"/>
</div>
}
</div>
@code {
private List<EmojiResponse> Emojis { get; set; } = [];
private string EmojiFilter { get; set; } = "";
private string HostFilter { get; set; } = "";
private State State { get; set; }
private PaginationData? PaginationData { get; set; }
private async Task Search()
{
State = State.Loading;
var res = await Api.Emoji.GetRemoteEmojiAsync(string.IsNullOrWhiteSpace(EmojiFilter) ? null : EmojiFilter.ToLower().Replace(" ", ""), string.IsNullOrWhiteSpace(HostFilter) ? null : HostFilter.ToLower().Replace(" ", ""), new PaginationQuery());
PaginationData = res.Links;
Emojis = res.Data;
State = State.Loaded;
StateHasChanged();
}
private async Task FetchMore()
{
if (PaginationData?.Next == null) return;
var pq = new PaginationQuery { MaxId = PaginationData.Next?.Split('=')[1], Limit = PaginationData.Limit };
var res = await Api.Emoji.GetRemoteEmojiAsync(string.IsNullOrWhiteSpace(EmojiFilter) ? null : EmojiFilter.ToLower().Replace(" ", ""), string.IsNullOrWhiteSpace(HostFilter) ? null : HostFilter.ToLower().Replace(" ", ""), pq);
PaginationData = res.Links;
Emojis.AddRange(res.Data);
StateHasChanged();
}
protected override async Task OnInitializedAsync()
{
await Search();
}
}

View file

@ -0,0 +1,22 @@
.emoji-search {
display: flex;
margin-top: 1rem;
}
.search {
display: inline-block;
width: 100%;
}
.emoji-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
width: 100%;
}
.loading {
display: flex;
justify-content: center;
margin-top: 5rem;
}