[frontend/components] Add menu and actions to emoji management entries

This commit is contained in:
pancakes 2025-01-28 01:02:12 +10:00 committed by Laura Hausmann
parent a9378fbdae
commit 0951728f0a
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 155 additions and 6 deletions

View file

@ -2,9 +2,13 @@
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services
@inject ApiService Api;
@inject IJSRuntime Js;
@inject IStringLocalizer<Localization> Loc;
<div class="emoji-entry">
<div @ref="EmojiButton" class="emoji-entry" @onclick="SelectEmoji" @onclick:stopPropagation="true">
<InlineEmoji Name="@Emoji.Name" Url="@Emoji.PublicUrl" Size="3rem"/>
<div class="emoji-details">
<span class="emoji-name">@Emoji.Name</span>
@ -29,9 +33,148 @@
}
</span>
</div>
<Menu @ref="EmojiMenu">
@if (Source == "remote")
{
<MenuElement Icon="Icons.Copy" OnSelect="Clone">
<Text>@Loc["Clone"]</Text>
</MenuElement>
}
else
{
<MenuElement Icon="Icons.CursorText" OnSelect="Rename">
<Text>@Loc["Rename"]</Text>
</MenuElement>
@if (Emoji.Sensitive)
{
<MenuElement Icon="Icons.Eye" OnSelect="MarkNotSensitive">
<Text>@Loc["Mark as not sensitive"]</Text>
</MenuElement>
}
else
{
<MenuElement Icon="Icons.EyeSlash" OnSelect="MarkAsSensitive">
<Text>@Loc["Mark as sensitive"]</Text>
</MenuElement>
}
<MenuElement Icon="Icons.TextAa" OnSelect="SetAliases">
<Text>@Loc["Set aliases"]</Text>
</MenuElement>
<MenuElement Icon="Icons.Folder" OnSelect="SetCategory">
<Text>@Loc["Set category"]</Text>
</MenuElement>
<MenuElement Icon="Icons.Article" OnSelect="SetLicense">
<Text>@Loc["Set license"]</Text>
</MenuElement>
<MenuElement Icon="Icons.Trash" OnSelect="Delete">
<Text>@Loc["Delete"]</Text>
</MenuElement>
}
<ClosingBackdrop OnClose="EmojiMenu.Close"></ClosingBackdrop>
</Menu>
</div>
@code {
[Parameter, EditorRequired] public required EmojiResponse Emoji { get; set; }
[Parameter, EditorRequired] public required string Source { get; set; }
[Parameter, EditorRequired] public required EventCallback GetEmojis { get; set; }
private ElementReference EmojiButton { get; set; }
private Menu EmojiMenu { get; set; } = null!;
private void SelectEmoji() => EmojiMenu.Toggle(EmojiButton);
private async Task Clone()
{
if (Source == "local" || Emoji.Category == null) return;
try
{
// Remote emojis endpoint sets EmojiResponse.Category to the emoji's host
var res = await Api.Emoji.CloneEmojiAsync(Emoji.Name, Emoji.Category);
if (res != null) await Js.InvokeVoidAsync("alert", $"Cloned {Emoji.Name}");
}
catch (ApiException e)
{
await Js.InvokeVoidAsync("alert", e.Response.Message);
}
}
private async Task Rename()
{
var name = await Js.InvokeAsync<string?>("prompt", "Rename emoji", Emoji.Name);
if (string.IsNullOrWhiteSpace(name)) return;
try
{
var res = await Api.Emoji.UpdateEmojiAsync(Emoji.Id, new UpdateEmojiRequest { Name = name.Trim() });
if (res != null)
{
Emoji.Name = res.Name;
StateHasChanged();
}
}
catch (ApiException e)
{
await Js.InvokeVoidAsync("alert", e.Response.Message);
}
}
private async Task MarkSensitive(bool sensitive)
{
var res = await Api.Emoji.UpdateEmojiAsync(Emoji.Id, new UpdateEmojiRequest { Sensitive = sensitive });
if (res != null)
{
Emoji.Sensitive = res.Sensitive;
StateHasChanged();
}
}
private async Task MarkNotSensitive() => await MarkSensitive(false);
private async Task MarkAsSensitive() => await MarkSensitive(true);
private async Task SetAliases()
{
var aliases = await Js.InvokeAsync<string?>("prompt", "Set aliases (separated by comma , )", Emoji.License ?? "");
if (aliases == null) return;
var res = await Api.Emoji.UpdateEmojiAsync(Emoji.Id, new UpdateEmojiRequest { Aliases = string.IsNullOrWhiteSpace(aliases) ? null : aliases.Replace(" ", "").Split(",").ToList() });
if (res != null)
{
Emoji.Aliases = res.Aliases;
StateHasChanged();
}
}
private async Task SetCategory()
{
var category = await Js.InvokeAsync<string?>("prompt", "Set category", Emoji.Category ?? "");
if (category == null) return;
var res = await Api.Emoji.UpdateEmojiAsync(Emoji.Id, new UpdateEmojiRequest { Category = category.Trim() });
if (res != null)
{
Emoji.Category = res.Category;
await GetEmojis.InvokeAsync();
}
}
private async Task SetLicense()
{
var license = await Js.InvokeAsync<string?>("prompt", "Set license", Emoji.License ?? "");
if (license == null) return;
var res = await Api.Emoji.UpdateEmojiAsync(Emoji.Id, new UpdateEmojiRequest { License = string.IsNullOrWhiteSpace(license) ? null : license.Trim() });
if (res != null)
{
Emoji.License = res.License;
StateHasChanged();
}
}
private async Task Delete()
{
var res = await Api.Emoji.DeleteEmojiAsync(Emoji.Id);
if (res) await GetEmojis.InvokeAsync();
}
}

View file

@ -15,8 +15,14 @@ internal class EmojiControllerModel(ApiClient api)
public Task<EmojiResponse> UploadEmojiAsync(IBrowserFile file) =>
api.CallAsync<EmojiResponse>(HttpMethod.Post, "/emoji", data: file);
public Task<EmojiResponse?> UpdateEmojiAsync(string id, EmojiResponse emoji) =>
api.CallNullableAsync<EmojiResponse>(HttpMethod.Patch, $"/emoji/{id}", data: emoji);
public Task<EmojiResponse?> CloneEmojiAsync(string name, string host) =>
api.CallNullableAsync<EmojiResponse>(HttpMethod.Post, $"/emoji/clone/{name}@{host}");
public Task<EmojiResponse?> UpdateEmojiAsync(string id, UpdateEmojiRequest request) =>
api.CallNullableAsync<EmojiResponse>(HttpMethod.Patch, $"/emoji/{id}", data: request);
public Task<bool> DeleteEmojiAsync(string id) =>
api.CallNullableAsync(HttpMethod.Delete, $"/emoji/{id}");
public Task<EmojiResponse?> GetEmojiAsync(string id) =>
api.CallNullableAsync<EmojiResponse>(HttpMethod.Get, $"/emoji/{id}");

View file

@ -38,7 +38,7 @@
<div class="emoji-list">
@foreach (var emoji in category.Value)
{
<EmojiManagementEntry Emoji="@emoji" Source="@Source"/>
<EmojiManagementEntry Emoji="@emoji" Source="@Source" GetEmojis="GetEmojis"/>
}
</div>
}