@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 { @if (account.Moved is { } moved) {

@Localizer["{0} has moved to", account.Name] @("@" + moved.Acct)

}
@* alt="": the name is next to it. *@
@CustomEmojis.Text(account.Name, account.Emojis) @("@" + FullAcct)
@if (!IsOwn) {
@if (relationship?.FollowedBy == true) { @Localizer["Follows you"] } @if (relationship?.Blocking != true) { @FollowLabel } @if (relationship?.Following == true) { @Localizer["Lists"] @if (allLists.Count == 0) {

@Localizer["This persona has no list yet."]

} @foreach (var list in allLists) { } @Localizer["Manage lists"]
} @(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)CustomEmojis.Html(account.Note, account.Emojis))
} @if (account.Fields.Count > 0) {
@foreach (var field in account.Fields) {
@CustomEmojis.Text(field.Name, account.Emojis)
@((MarkupString)CustomEmojis.Html(field.Value, account.Emojis))
}
}
@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] ListService ListService { 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 isListMenuOpen; List allLists = []; HashSet inLists = []; 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); await LoadLists(); posts = []; maxId = null; IsLoading = false; await LoadPage(); } /// The persona's lists and which of them hold the account, for an account the persona follows. async Task LoadLists() { if (relationship?.Following != true) { allLists = []; inLists = []; return; } allLists = await ListService.All(); inLists = [.. (await ListService.Of(account.Id)).Select(l => l.Id)]; } async Task SetListMember(MastodonList list, bool member) { isActing = true; var result = await ListService.SetMember(list.Id, account.Id, member); isActing = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } if (member) inLists.Add(list.Id); else inLists.Remove(list.Id); Toast.AddResponseSuccess(member ? Localizer["{0} added to {1}.", account.Name, list.Title] : Localizer["{0} removed from {1}.", account.Name, list.Title]); } 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; if (action is AccountAction.Follow or AccountAction.Unfollow) await LoadLists(); 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"); }