@inherits LocalizableComponentBase
@* The current persona's profile (/api/v1/accounts/update_credentials): its pictures, name, biography and fields as it
wrote them, whether it approves followers by hand, is a bot, is listed and searchable, and how its new posts start.
PrivaPub tells the persona's followers' servers of the change. *@
@if (IsLoading)
{
@Localizer["loading"]
}
else if (account is null)
{
@Localizer["No persona"]
}
else
{
@if (HasOwn(account.Avatar) && form.Avatar is null)
{
@Localizer["Remove the avatar"]
}
@if (HasOwn(account.Header) && form.Header is null)
{
@Localizer["Remove the header"]
}
@Localizer["Reset"]@Localizer["Save"]
}
@code {
[Inject] AccountService AccountService { get; set; }
[Inject] MediaService MediaService { get; set; }
[Inject] AuthService AuthService { get; set; }
[Inject] ILoggingService Logger { get; set; }
static readonly MessageType[] Visibilities = [MessageType.Public, MessageType.Unlisted, MessageType.FollowersOnly];
Account account;
ProfileForm form = new();
string loadedPersonaId;
bool isSaving;
List pictureErrors = [];
// the picture types the server takes, read with the account
List pictureTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
string PictureTypes => string.Join(",", pictureTypes);
// a picture of its own, not the server's default
static bool HasOwn(string url) => url is { Length: > 0 } && !url.Contains("/missing-", StringComparison.Ordinal);
protected override async Task OnParametersSetAsync()
{
if (CascadingState.PersonaId == loadedPersonaId)
return;
loadedPersonaId = CascadingState.PersonaId;
if (CascadingState.IsOffline)
{
WhenOnline(LoadData);
StartOnlinePolling();
IsLoading = false;
return;
}
await LoadData();
IsLoading = false;
}
async Task LoadData()
{
// the account as the persona sees itself, with its Source
account = await AuthService.CurrentAccount();
pictureTypes = [.. (await MediaService.Limits()).SupportedMimeTypes.Where(t => t.StartsWith("image/"))];
Reset();
}
void Reset()
{
pictureErrors = [];
form = account is null ? new() : ProfileForm.Of(account);
}
string QuotePolicyName(QuotePolicy policy) => policy switch
{
QuotePolicy.Followers => Localizer["Its followers"],
QuotePolicy.Nobody => Localizer["Nobody"],
_ => Localizer["Anyone"]
};
async Task Pick(InputFileChangeEventArgs eventArgs, bool isAvatar)
{
pictureErrors = [];
var file = eventArgs.File;
if (!pictureTypes.Contains(file.ContentType))
{
pictureErrors.Add(Localizer["\"{0}\" is not a kind of file this server takes.", file.Name]);
return;
}
if (file.Size > ProfileForm.MaximumPictureSize)
{
pictureErrors.Add(Localizer["Supplied file \"{0}\" with size {1} exceeds the maximum of {2}.", file.Name,
file.Size.GetFileSize(Localizer), ProfileForm.MaximumPictureSize.GetFileSize(Localizer)]);
return;
}
// the file is sent as it is when saving, and is the form's at once (a save meanwhile sends it); only a small picture
// of it is kept to show it, made afterwards, and PrivaPub decides about a file the browser can't draw
var picture = new UploadMedia { File = file, FileName = file.Name, ContentType = file.ContentType, Size = file.Size };
if (isAvatar)
form.Avatar = picture;
else
form.Header = picture;
try
{
var small = await file.RequestImageFileAsync("image/jpeg", isAvatar ? 240 : 900, isAvatar ? 240 : 300);
await using var stream = small.OpenReadStream(4 * 1024 * 1024);
using var buffer = new MemoryStream();
await stream.CopyToAsync(buffer);
picture.Preview = $"data:image/jpeg;base64,{Convert.ToBase64String(buffer.ToArray())}";
}
catch (Exception ex)
{
await Logger.ProcessWarning(ex.Message, $"{nameof(ProfileEditor)}.{nameof(Pick)}:{file.Name}");
}
}
async Task RemovePicture(bool avatar)
{
isSaving = true;
var result = await AccountService.RemovePicture(avatar);
isSaving = false;
if (result.IsInvalid)
{
Toast.AddResponseError(result);
return;
}
// the rest of the form stays as being written: it shows the server's pictures, which are the account's
account = (Account)result.Data;
CascadingState.PersonaChanged(account);
Toast.AddResponseSuccess(avatar ? Localizer["Avatar removed."] : Localizer["Header removed."]);
}
async Task Save()
{
isSaving = true;
var result = await AccountService.UpdateProfile(form);
isSaving = false;
if (result.IsInvalid)
{
Toast.AddResponseError(result);
return;
}
account = (Account)result.Data;
CascadingState.PersonaChanged(account);
Reset();
Toast.AddResponseSuccess(Localizer["Profile saved."]);
}
}