@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 {
@Localizer[ @Localizer[
@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["Profile fields"] @foreach (var field in form.Fields) {
}
@Localizer["New posts"]
@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; only a small picture of it is kept to show it now var picture = new UploadMedia { File = file, FileName = file.Name, ContentType = file.ContentType, Size = file.Size }; 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.ProcessError(ex, $"{nameof(ProfileEditor)}.{nameof(Pick)}:{file.Name}"); pictureErrors.Add(Localizer["The file \"{0}\" could not be read.", file.Name]); return; } if (isAvatar) form.Avatar = picture; else form.Header = picture; } 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."]); } }