@page "/groups" @attribute [Authorize] @inherits LocalizableComponentBase @Localizer["Groups"] @* The current persona's communities and circles (/clientapi/group): made, joined by invitation, and left; for the ones it runs, edited, their invitation shown, the requests to join answered and members taken out. A circle's members are never shown to anyone but its owner and moderators, here as on every server. *@

@Localizer["Groups"]

@Localizer["A new group"]
@@
@if (draft.IsCommunity) { @foreach (var policy in PostingPolicies) { } } @Localizer["Make the group"]
@Localizer["Join by invitation"] @Localizer["Join"]
@if (IsLoading) {

@Localizer["loading"]

} else if (groups.Count == 0) {

@Localizer["This persona is in no group yet."]

}
@foreach (var group in groups) {
@(group.Name ?? group.UserName) @("@" + group.Handle) @(group.IsCommunity ? Localizer["Community"] : Localizer["Circle"]) @group.MembersCount @if (group.IsOwner) { } else { @Localizer["Leave"] }
@if (open?.Id == group.Id) { @if (group.InvitationCode is { Length: > 0 } invitation) {
@Localizer["Invitation code"]@(group.IsPasswordRequired ? " ยท " + Localizer["it asks for a password"] : null)
} @if (group.IsCommunity) { @foreach (var policy in PostingPolicies) { } } @if (group.IsPasswordRequired) { } @Localizer["Save"]

@Localizer["Members"] @members.Count(m => !m.IsPending)

@foreach (var member in members.OrderBy(m => m.IsPending ? 0 : 1).ThenBy(m => m.Since)) {
@* alt="": the name is next to it. *@ @(member.Name ?? member.Handle) @("@" + member.Handle) @if (member.IsPending) { @Localizer["asks to join"] @Localizer["Let in"] @Localizer["Decline"] } else if (member.Role == "owner") { @Localizer["owner"] } else { }
}
}
}
@code { static readonly string[] PostingPolicies = ["followers", "anyone", "moderators"]; [Inject] GroupService GroupService { get; set; } [Inject] ConfirmService Confirm { get; set; } List groups = []; List members = []; InsertGroupForm draft = new() { PostingPolicy = "followers" }; JoinGroupForm joining = new(); UpdateGroupForm edit = new(); bool editDiscoverable; bool editApproves; ViewGroup open; string loadedPersonaId; bool isSaving; string PolicyName(string policy) => policy switch { "anyone" => Localizer["Anyone may post"], "moderators" => Localizer["Only its moderators post"], _ => Localizer["Its followers post"] }; // The persona changed (NavMenu's switcher): its groups are another persona's. protected override async Task OnParametersSetAsync() { if (CascadingState.PersonaId is null) { IsLoading = false; return; } if (CascadingState.PersonaId == loadedPersonaId) return; loadedPersonaId = CascadingState.PersonaId; if (CascadingState.IsOffline) { WhenOnline(LoadData); StartOnlinePolling(); IsLoading = false; return; } await LoadData(); } async Task LoadData() { IsLoading = true; open = null; members = []; // (the forms are the persona's: validated as they are filled, so they name it from the start) draft.AvatarId = CascadingState.PersonaId; joining.AvatarId = CascadingState.PersonaId; groups = await GroupService.List(CascadingState.PersonaId); IsLoading = false; } async Task Create() { if (!draft.IsCommunity) draft.IsDiscoverable = false; isSaving = true; var result = await GroupService.Insert(draft); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } var group = (ViewGroup)result.Data; draft = new() { AvatarId = CascadingState.PersonaId, PostingPolicy = "followers" }; Toast.AddResponseSuccess(Localizer["{0} made.", group.Name ?? group.UserName]); await LoadData(); if (groups.FirstOrDefault(g => g.Id == group.Id) is { } made) await Toggle(made); } async Task Join() { isSaving = true; var result = await GroupService.Join(joining); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } var group = (ViewGroup)result.Data; joining = new() { AvatarId = CascadingState.PersonaId }; Toast.AddResponseSuccess(Localizer["You joined {0}.", group.Name ?? group.UserName]); await LoadData(); } async Task Leave(ViewGroup group) { if (!await Confirm.Ask(Localizer["Leave {0}?", group.Name ?? group.UserName], null, Localizer["Leave"], true)) return; isSaving = true; var result = await GroupService.Leave(CascadingState.PersonaId, group.Id); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } groups.Remove(group); Toast.AddResponseSuccess(Localizer["You left {0}.", group.Name ?? group.UserName]); } async Task Toggle(ViewGroup group) { if (open?.Id == group.Id) { open = null; members = []; return; } open = group; edit = new UpdateGroupForm { AvatarId = CascadingState.PersonaId, GroupId = group.Id, Name = group.Name, Description = group.Description, PostingPolicy = group.PostingPolicy }; editDiscoverable = group.IsDiscoverable; editApproves = group.ManuallyApprovesMembers; members = await GroupService.Members(CascadingState.PersonaId, group.Id); } async Task Save() { edit.IsDiscoverable = open.IsCommunity ? editDiscoverable : null; edit.ManuallyApprovesMembers = editApproves; if (string.IsNullOrEmpty(edit.InvitationPassword)) edit.InvitationPassword = null; isSaving = true; var result = await GroupService.Update(edit); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } var updated = (ViewGroup)result.Data; var index = groups.FindIndex(g => g.Id == updated.Id); if (index >= 0) groups[index] = updated; open = updated; edit.InvitationPassword = null; edit.RemoveInvitationPassword = false; edit.RegenerateInvitationCode = false; Toast.AddResponseSuccess(Localizer["Saved."]); } async Task Answer(ViewGroupMember member, bool approve) { isSaving = true; var result = approve ? await GroupService.Approve(CascadingState.PersonaId, open.Id, member.ActorUri) : await GroupService.Reject(CascadingState.PersonaId, open.Id, member.ActorUri); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } members = await GroupService.Members(CascadingState.PersonaId, open.Id); } async Task Remove(ViewGroupMember member) { isSaving = true; var result = await GroupService.Remove(CascadingState.PersonaId, open.Id, member.ActorUri); isSaving = false; if (result.IsInvalid) { Toast.AddResponseError(result); return; } members.Remove(member); Toast.AddResponseSuccess(Localizer["{0} is out of the group.", member.Name ?? member.Handle]); } }