[frontend/pages] Allow setting alt text for new or existing avatar and banner

This commit is contained in:
pancakes 2024-12-20 16:00:44 +10:00 committed by Laura Hausmann
parent d4efbfd2ce
commit b976bd9489
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 53 additions and 9 deletions

View file

@ -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;
@ -9,14 +10,19 @@ internal class ProfileControllerModel(ApiClient api)
public Task<UserProfileEntity> GetProfileAsync() => public Task<UserProfileEntity> GetProfileAsync() =>
api.CallAsync<UserProfileEntity>(HttpMethod.Get, "/profile"); api.CallAsync<UserProfileEntity>(HttpMethod.Get, "/profile");
public Task UpdateProfileAsync(UserProfileEntity request) => public Task UpdateProfileAsync(UserProfileEntity request, string? newAvatarAlt, string? newBannerAlt) =>
api.CallAsync(HttpMethod.Put, "/profile", data: request); api.CallAsync(HttpMethod.Put, "/profile",
QueryString.Create(new Dictionary<string, string?>
{
{ "newAvatarAlt", newAvatarAlt }, { "newBannerAlt", newBannerAlt }
}), request);
public Task<DriveFileResponse> GetAvatarAsync() => public Task<DriveFileResponse> GetAvatarAsync() =>
api.CallAsync<DriveFileResponse>(HttpMethod.Get, "/profile/avatar"); api.CallAsync<DriveFileResponse>(HttpMethod.Get, "/profile/avatar");
public Task UpdateAvatarAsync(IBrowserFile file) => public Task UpdateAvatarAsync(IBrowserFile file, string? altText) =>
api.CallAsync(HttpMethod.Post, "/profile/avatar", data: file); api.CallAsync(HttpMethod.Post, "/profile/avatar",
altText != null ? QueryString.Create("altText", altText) : QueryString.Empty, file);
public Task DeleteAvatarAsync() => public Task DeleteAvatarAsync() =>
api.CallAsync(HttpMethod.Delete, "/profile/avatar"); api.CallAsync(HttpMethod.Delete, "/profile/avatar");
@ -24,8 +30,9 @@ internal class ProfileControllerModel(ApiClient api)
public Task<DriveFileResponse> GetBannerAsync() => public Task<DriveFileResponse> GetBannerAsync() =>
api.CallAsync<DriveFileResponse>(HttpMethod.Get, "/profile/banner"); api.CallAsync<DriveFileResponse>(HttpMethod.Get, "/profile/banner");
public Task UpdateBannerAsync(IBrowserFile file) => public Task UpdateBannerAsync(IBrowserFile file, string? altText) =>
api.CallAsync(HttpMethod.Post, "/profile/banner", data: file); api.CallAsync(HttpMethod.Post, "/profile/banner",
altText != null ? QueryString.Create("altText", altText) : QueryString.Empty, file);
public Task DeleteBannerAsync() => public Task DeleteBannerAsync() =>
api.CallAsync(HttpMethod.Delete, "/profile/banner"); api.CallAsync(HttpMethod.Delete, "/profile/banner");

View file

@ -35,6 +35,11 @@
</div> </div>
} }
<InputFile class="input" OnChange="OnBannerFileChange" accept="image/*" disabled="@DelBanner"/> <InputFile class="input" OnChange="OnBannerFileChange" accept="image/*" disabled="@DelBanner"/>
@if (Banner != null || BannerFile != null)
{
<label for="banner-alt">@(BannerFile != null ? Loc["Set alt text for your new banner"] : Loc["Set alt text for your banner"])</label>
<textarea class="input alt-text" @bind="BannerAlt" id="banner-alt" rows="3" placeholder="@Loc["Alt text"]" disabled="@DelBanner"></textarea>
}
</div> </div>
<div class="section"> <div class="section">
<h3>@Loc["Avatar"]</h3> <h3>@Loc["Avatar"]</h3>
@ -47,6 +52,11 @@
</div> </div>
} }
<InputFile class="input" OnChange="OnAvatarFileChange" accept="image/*" disabled="@DelAvatar"/> <InputFile class="input" OnChange="OnAvatarFileChange" accept="image/*" disabled="@DelAvatar"/>
@if (Avatar != null || AvatarFile != null)
{
<label for="avatar-alt">@(AvatarFile != null ? Loc["Set alt text for your new avatar"] : Loc["Set alt text for your avatar"])</label>
<textarea class="input alt-text" @bind="AvatarAlt" id="avatar-alt" rows="3" placeholder="@Loc["Alt text"]" disabled="@DelAvatar"></textarea>
}
</div> </div>
<div class="section"> <div class="section">
<h3>@Loc["Display Name"]</h3><input class="input" @bind="@UserProfile.DisplayName"/> <h3>@Loc["Display Name"]</h3><input class="input" @bind="@UserProfile.DisplayName"/>
@ -139,8 +149,10 @@
private string FieldValue { get; set; } = ""; private string FieldValue { get; set; } = "";
private StateButton SaveButton { get; set; } = null!; private StateButton SaveButton { get; set; } = null!;
private IBrowserFile? AvatarFile { get; set; } = null; private IBrowserFile? AvatarFile { get; set; } = null;
private string? AvatarAlt { get; set; }
private bool DelAvatar { get; set; } = false; private bool DelAvatar { get; set; } = false;
private IBrowserFile? BannerFile { get; set; } = null; private IBrowserFile? BannerFile { get; set; } = null;
private string? BannerAlt { get; set; }
private bool DelBanner { get; set; } = false; private bool DelBanner { get; set; } = false;
private DateTime Birthday { get; set; } = DateTime.Now; private DateTime Birthday { get; set; } = DateTime.Now;
private bool SetBirthday { get; set; } = false; private bool SetBirthday { get; set; } = false;
@ -168,6 +180,9 @@
State = State.Loaded; State = State.Loaded;
Avatar = await Api.Profile.GetAvatarAsync(); Avatar = await Api.Profile.GetAvatarAsync();
Banner = await Api.Profile.GetBannerAsync(); Banner = await Api.Profile.GetBannerAsync();
AvatarAlt = Avatar.Description;
BannerAlt = Banner.Description;
} }
catch (ApiException e) catch (ApiException e)
{ {
@ -193,19 +208,35 @@
try try
{ {
SaveButton.State = StateButton.StateEnum.Loading; SaveButton.State = StateButton.StateEnum.Loading;
if (!SetBirthday) if (!SetBirthday)
UserProfile.Birthday = null; UserProfile.Birthday = null;
else else
UserProfile.Birthday = Birthday.ToString("yyyy-MM-dd"); UserProfile.Birthday = Birthday.ToString("yyyy-MM-dd");
await Api.Profile.UpdateProfileAsync(UserProfile);
if (DelAvatar) if (DelAvatar)
{
await Api.Profile.DeleteAvatarAsync(); await Api.Profile.DeleteAvatarAsync();
AvatarAlt = null;
}
else if (AvatarFile != null) else if (AvatarFile != null)
await Api.Profile.UpdateAvatarAsync(AvatarFile); {
await Api.Profile.UpdateAvatarAsync(AvatarFile, AvatarAlt);
AvatarAlt = null;
}
if (DelBanner) if (DelBanner)
{
await Api.Profile.DeleteBannerAsync(); await Api.Profile.DeleteBannerAsync();
BannerAlt = null;
}
else if (BannerFile != null) else if (BannerFile != null)
await Api.Profile.UpdateBannerAsync(BannerFile); {
await Api.Profile.UpdateBannerAsync(BannerFile, BannerAlt);
BannerAlt = null;
}
await Api.Profile.UpdateProfileAsync(UserProfile, AvatarAlt, BannerAlt);
SaveButton.State = StateButton.StateEnum.Success; SaveButton.State = StateButton.StateEnum.Success;
} }
catch (ApiException e) catch (ApiException e)
@ -218,11 +249,13 @@
private void OnAvatarFileChange(InputFileChangeEventArgs e) private void OnAvatarFileChange(InputFileChangeEventArgs e)
{ {
AvatarFile = e.GetMultipleFiles().First(p => p.ContentType.StartsWith("image/")); AvatarFile = e.GetMultipleFiles().First(p => p.ContentType.StartsWith("image/"));
AvatarAlt = "";
} }
private void OnBannerFileChange(InputFileChangeEventArgs e) private void OnBannerFileChange(InputFileChangeEventArgs e)
{ {
BannerFile = e.GetMultipleFiles().First(p => p.ContentType.StartsWith("image/")); BannerFile = e.GetMultipleFiles().First(p => p.ContentType.StartsWith("image/"));
BannerAlt = "";
} }
private void ToggleEmojiPicker() private void ToggleEmojiPicker()

View file

@ -28,6 +28,10 @@ input[type="checkbox"] {
color: color-mix(in srgb, var(--font-color) 50%, transparent); color: color-mix(in srgb, var(--font-color) 50%, transparent);
} }
.input.alt-text {
resize: none;
}
.avatar { .avatar {
border-radius: 8px; border-radius: 8px;
object-fit: cover; object-fit: cover;