[frontend/pages] Add upload and import actions to emoji management page
This commit is contained in:
parent
fd5ced1c59
commit
e0e8fe8b72
4 changed files with 96 additions and 2 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
using Iceshrimp.Frontend.Core.Miscellaneous;
|
||||||
using Iceshrimp.Frontend.Core.Services;
|
using Iceshrimp.Frontend.Core.Services;
|
||||||
using Iceshrimp.Shared.Schemas.Web;
|
using Iceshrimp.Shared.Schemas.Web;
|
||||||
using Microsoft.AspNetCore.Components.Forms;
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
@ -12,12 +13,16 @@ internal class EmojiControllerModel(ApiClient api)
|
||||||
public Task<List<EmojiResponse>> GetRemoteEmojiAsync() =>
|
public Task<List<EmojiResponse>> GetRemoteEmojiAsync() =>
|
||||||
api.CallAsync<List<EmojiResponse>>(HttpMethod.Get, "/emoji/remote");
|
api.CallAsync<List<EmojiResponse>>(HttpMethod.Get, "/emoji/remote");
|
||||||
|
|
||||||
public Task<EmojiResponse> UploadEmojiAsync(IBrowserFile file) =>
|
public Task<EmojiResponse> UploadEmojiAsync(IBrowserFile file, string? name) =>
|
||||||
api.CallAsync<EmojiResponse>(HttpMethod.Post, "/emoji", data: file);
|
api.CallAsync<EmojiResponse>(HttpMethod.Post, "/emoji",
|
||||||
|
name != null ? QueryString.Create("name", name) : QueryString.Empty, file);
|
||||||
|
|
||||||
public Task<EmojiResponse?> CloneEmojiAsync(string name, string host) =>
|
public Task<EmojiResponse?> CloneEmojiAsync(string name, string host) =>
|
||||||
api.CallNullableAsync<EmojiResponse>(HttpMethod.Post, $"/emoji/clone/{name}@{host}");
|
api.CallNullableAsync<EmojiResponse>(HttpMethod.Post, $"/emoji/clone/{name}@{host}");
|
||||||
|
|
||||||
|
public Task<bool> ImportEmojiAsync(IBrowserFile file) =>
|
||||||
|
api.CallNullableAsync(HttpMethod.Post, "/emoji/import", data: file);
|
||||||
|
|
||||||
public Task<EmojiResponse?> UpdateEmojiAsync(string id, UpdateEmojiRequest request) =>
|
public Task<EmojiResponse?> UpdateEmojiAsync(string id, UpdateEmojiRequest request) =>
|
||||||
api.CallNullableAsync<EmojiResponse>(HttpMethod.Patch, $"/emoji/{id}", data: request);
|
api.CallNullableAsync<EmojiResponse>(HttpMethod.Patch, $"/emoji/{id}", data: request);
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,22 @@
|
||||||
@attribute [Authorize(Roles = "moderator")]
|
@attribute [Authorize(Roles = "moderator")]
|
||||||
@layout ModerationLayout
|
@layout ModerationLayout
|
||||||
@inject ApiService Api;
|
@inject ApiService Api;
|
||||||
|
@inject GlobalComponentSvc Global;
|
||||||
|
@inject IJSRuntime Js;
|
||||||
@inject IStringLocalizer<Localization> Loc;
|
@inject IStringLocalizer<Localization> Loc;
|
||||||
|
|
||||||
<SectionContent SectionName="top-bar">
|
<SectionContent SectionName="top-bar">
|
||||||
<Icon Name="Icons.Smiley"></Icon>
|
<Icon Name="Icons.Smiley"></Icon>
|
||||||
@Loc["Custom Emojis"]
|
@Loc["Custom Emojis"]
|
||||||
|
@if (State is State.Empty or State.Loaded && Source == "local")
|
||||||
|
{
|
||||||
|
<span class="action btn" @onclick="OpenUpload" title="@Loc["Upload emoji"]">
|
||||||
|
<Icon Name="Icons.Upload"/>
|
||||||
|
</span>
|
||||||
|
<span class="action btn" @onclick="OpenImport" title="@Loc["Import emoji pack"]">
|
||||||
|
<Icon Name="Icons.FileArrowUp"/>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
|
|
||||||
@if (State is State.Loaded)
|
@if (State is State.Loaded)
|
||||||
|
@ -59,6 +70,11 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="file-input">
|
||||||
|
<InputFile @ref="UploadInput" OnChange="Upload" accept="image/*">Upload</InputFile>
|
||||||
|
<InputFile @ref="ImportInput" OnChange="Import" accept=".zip">Import</InputFile>
|
||||||
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<EmojiResponse> Emojis { get; set; } = [];
|
private List<EmojiResponse> Emojis { get; set; } = [];
|
||||||
private Dictionary<string, List<EmojiResponse>> Categories { get; set; } = new();
|
private Dictionary<string, List<EmojiResponse>> Categories { get; set; } = new();
|
||||||
|
@ -66,6 +82,12 @@
|
||||||
private string HostFilter { get; set; } = "";
|
private string HostFilter { get; set; } = "";
|
||||||
private string Source { get; set; } = "local";
|
private string Source { get; set; } = "local";
|
||||||
private State State { get; set; }
|
private State State { get; set; }
|
||||||
|
private InputFile UploadInput { get; set; } = null!;
|
||||||
|
private IBrowserFile UploadFile { get; set; } = null!;
|
||||||
|
private string UploadName { get; set; } = "";
|
||||||
|
private InputFile ImportInput { get; set; } = null!;
|
||||||
|
private IBrowserFile ImportFile { get; set; } = null!;
|
||||||
|
private IJSObjectReference _module = null!;
|
||||||
|
|
||||||
private void FilterEmojis()
|
private void FilterEmojis()
|
||||||
{
|
{
|
||||||
|
@ -96,8 +118,68 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The <InputFile> Component is hidden, and triggered by a sepperate button.
|
||||||
|
// That way we get it's functionality, without the styling limitations of the InputFile component
|
||||||
|
private async Task OpenUpload()
|
||||||
|
{
|
||||||
|
await _module.InvokeVoidAsync("openUpload", UploadInput.Element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenImport()
|
||||||
|
{
|
||||||
|
await _module.InvokeVoidAsync("openUpload", ImportInput.Element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Upload(InputFileChangeEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.File.ContentType.StartsWith("image/")) return;
|
||||||
|
UploadFile = e.File;
|
||||||
|
await Global.PromptDialog?.Prompt(new EventCallback<string?>(this, UploadCallback), Loc["Set emoji name"], "", e.File.Name.Split(".")[0], buttonText: Loc["Upload"])!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UploadCallback(string? name)
|
||||||
|
{
|
||||||
|
if (name == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Api.Emoji.UploadEmojiAsync(UploadFile, name);
|
||||||
|
await Global.NoticeDialog?.Display(Loc["Successfully uploaded {0}", name])!;
|
||||||
|
await GetEmojis();
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
await Global.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Import(InputFileChangeEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.File.ContentType != "application/zip") return;
|
||||||
|
ImportFile = e.File;
|
||||||
|
await Global.ConfirmDialog?.Confirm(new EventCallback<bool>(this, ImportCallback), Loc["Import {0}?", e.File.Name], Icons.FileArrowUp, Loc["Import"])!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ImportCallback(bool import)
|
||||||
|
{
|
||||||
|
if (!import) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Api.Emoji.ImportEmojiAsync(ImportFile);
|
||||||
|
await Global.NoticeDialog?.Display(Loc["Successfully imported emoji pack"])!;
|
||||||
|
await GetEmojis();
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
await Global.NoticeDialog?.Display(e.Response.Message ?? Loc["An unknown error occurred"], NoticeDialog.NoticeType.Error)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
_module = await Js.InvokeAsync<IJSObjectReference>("import",
|
||||||
|
"./Pages/Moderation/CustomEmojis.razor.js");
|
||||||
await GetEmojis();
|
await GetEmojis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
margin-top: 5rem;
|
margin-top: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
@media (max-width: 1000px) {
|
||||||
.emoji-list {
|
.emoji-list {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function openUpload(element) {
|
||||||
|
element.click();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue