@page "/account/{accountId}" @attribute [Authorize] @inherits LocalizableComponentBase @(account?.Name ?? Localizer["Profile"]) @* An account as the current persona sees it (/api/v1/accounts/{id}): its header, bio and fields, its counts, what it is to the persona (following, followed by, requested, blocked, muted) with the actions that change it, and its posts, page by page, as posts only, posts and replies, or media. A remote account's profile comes from what PrivaPub knows, never from its server directly. *@

@Localizer["Profile"]

@if (IsLoading) {

@Localizer["loading"]

} else if (account is null) {

@Localizer["This account doesn't exist."]

} else {
@* alt="": the name is next to it. *@
@account.Name @("@" + FullAcct)
@if (!IsOwn) {
@if (relationship?.FollowedBy == true) { @Localizer["Follows you"] } @if (relationship?.Blocking != true) { @FollowLabel } @(relationship?.Muting == true ? Localizer["Unmute"] : Localizer["Mute"]) @(relationship?.Blocking == true ? Localizer["Unblock"] : Localizer["Block"])
}
@if (account.Locked) { @Localizer["Locked account"] } @if (account.Bot) { @Localizer["Bot"] } @if (account.Group) { @Localizer["Groups"] }
@if (!string.IsNullOrWhiteSpace(account.Note)) {
@((MarkupString)account.Note)
} @if (account.Fields.Count > 0) {
@foreach (var field in account.Fields) {
@field.Name
@((MarkupString)field.Value)
}
}
@account.StatusesCount @Localizer["posts"] @account.FollowingCount @Localizer["following"] @account.FollowersCount @Localizer["followers"]
@Localizer["posts"]
@Localizer["Posts and replies"]
@Localizer["Media"]
@foreach (var post in posts) { } @if (posts.Count == 0 && !isLoadingMore) {

@Localizer["Empty"]

} @if (maxId is not null) { @if (isLoadingMore) { } @Localizer["Load more"] }
}
@code { [Inject] AccountService AccountService { get; set; } [Inject] InstanceService InstanceService { get; set; } [Inject] ILocalStorageService LocalStorage { get; set; } [Inject] IJSRuntime JS { get; set; } [Parameter] public string AccountId { get; set; } Account account; Relationship relationship; List posts = []; ProfileTab tab = ProfileTab.Posts; string maxId; string loadedId; string loadedPersonaId; bool isActing; bool isLoadingMore; bool IsOwn => account is not null && account.Id == CascadingState.PersonaId; string FullAcct => account.Acct.Contains('@') ? account.Acct : $"{account.Acct}@{account.Host}"; string HeaderStyle => string.IsNullOrEmpty(account?.Header) ? null : $"background-image:url('{account.Header}')"; string FollowLabel => relationship switch { { Following: true } => Localizer["Unfollow"], { Requested: true } => Localizer["Cancel the request"], _ => Localizer["Follow"] }; protected override async Task OnParametersSetAsync() { if (AccountId == loadedId && CascadingState.PersonaId == loadedPersonaId) return; loadedId = AccountId; loadedPersonaId = CascadingState.PersonaId; if (CascadingState.IsOffline) { WhenOnline(LoadData); StartOnlinePolling(); IsLoading = false; return; } await LoadData(); } async Task LoadData() { IsLoading = true; account = await AccountService.Get(AccountId); if (account is null) { IsLoading = false; return; } relationship = account.Id == CascadingState.PersonaId ? null : await AccountService.Relationship(account.Id); posts = []; maxId = null; IsLoading = false; await LoadPage(); } async Task LoadPage() { isLoadingMore = true; var result = await AccountService.Statuses(account.Id, tab, maxId); isLoadingMore = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } var page = (FeedPage)result.Data; var known = posts.Select(p => p.Key).ToHashSet(); posts.AddRange(page.Posts.Where(p => !known.Contains(p.Key))); maxId = page.MaxId; await InstanceService.Resolve(page.Posts.Select(p => p.Host).Append(InstanceService.SelfHost)); InstanceService.Locate(page.Posts); } async Task LoadMore() => await LoadPage(); async Task SelectTab(int index) { tab = (ProfileTab)index; posts = []; maxId = null; await LoadPage(); } async Task ToggleFollow() => await Do(relationship is { Following: true } or { Requested: true } ? AccountAction.Unfollow : AccountAction.Follow); async Task Do(AccountAction action) { isActing = true; var result = await AccountService.Act(account.Id, action); isActing = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } relationship = (Relationship)result.Data ?? relationship; Toast.AddResponseSuccess(action switch { AccountAction.Follow when relationship?.Requested == true => Localizer["Asked {0} to accept you.", account.Name], AccountAction.Follow => Localizer["Following {0}.", account.Name], AccountAction.Unfollow => Localizer["{0} unfollowed.", account.Name], AccountAction.Block => Localizer["{0} blocked.", account.Name], AccountAction.Unblock => Localizer["{0} unblocked.", account.Name], AccountAction.Mute => Localizer["{0} muted.", account.Name], _ => Localizer["{0} unmuted.", account.Name] }); } void Remove(FeedPost post) => posts.RemoveAll(p => p.Key == post.Key); async Task OpenTag(string tagName) { var filter = await LocalStorage.GetItemAsync(nameof(FeedFilter)) ?? new(); filter.Tag = tagName; filter.TimelineType = TimelineType.Tag; await LocalStorage.SetItemAsync(nameof(FeedFilter), filter); Navigation.NavigateTo(""); } async Task Back() => await JS.InvokeVoidAsync("history.back"); }