The Groups page (/groups, in the menu) is where a persona makes a community or a circle (with its invitation code and an optional password), joins one by invitation and leaves one. For the groups it runs it edits them, shows the invitation, lets in or declines the requests to join, and takes members out, through PrivaPub's new /clientapi/group/members, reject and remove. The e2e makes a circle, joins it as another persona with the code, and has the owner take that member out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
99 lines
5.2 KiB
C#
99 lines
5.2 KiB
C#
using System.Net;
|
|
|
|
using collAnon.Client.Services;
|
|
|
|
using decePubClient.Extensions;
|
|
using decePubClient.Models;
|
|
|
|
using PrivaPub.ClientModels;
|
|
using PrivaPub.ClientModels.Group;
|
|
|
|
namespace decePubClient.Services
|
|
{
|
|
/// <summary>The persona's communities and circles (/clientapi/group): made, joined by invitation, edited and left;
|
|
/// for their owners and moderators, who is in them and who asks to join.</summary>
|
|
public class GroupService(IHttpService httpService, ILoggingService logger, CoalescingStringLocalizer localizer)
|
|
{
|
|
/// <summary>The persona's groups, communities first; empty on failure.</summary>
|
|
public async Task<List<ViewGroup>> List(string personaId, CancellationToken cancellationToken = default)
|
|
{
|
|
if (personaId is null)
|
|
return [];
|
|
|
|
using var response = await httpService.Get(APIs.ClientApi.GET_Groups, [$"avatarId={Uri.EscapeDataString(personaId)}"],
|
|
cancellationToken: cancellationToken);
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var result = await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
|
|
if (result.StatusCode is not (int)HttpStatusCode.Gone)
|
|
await logger.ProcessWarning(result.ToString(localizer), $"{nameof(GroupService)}.{nameof(List)}", cancellationToken);
|
|
return [];
|
|
}
|
|
|
|
var groups = await response.DefaultReadFromJsonAsync<List<ViewGroup>>(cancellationToken) ?? [];
|
|
return [.. groups.OrderByDescending(g => g.IsCommunity).ThenBy(g => g.Name ?? g.UserName)];
|
|
}
|
|
|
|
/// <summary>Makes a community or a circle owned by the persona; Data is the ViewGroup.</summary>
|
|
public Task<WebResult> Insert(InsertGroupForm form, CancellationToken cancellationToken = default) =>
|
|
GroupResult(APIs.ClientApi.POST_InsertGroup, form, cancellationToken);
|
|
|
|
/// <summary>Changes a group the persona runs; Data is the ViewGroup.</summary>
|
|
public Task<WebResult> Update(UpdateGroupForm form, CancellationToken cancellationToken = default) =>
|
|
GroupResult(APIs.ClientApi.POST_UpdateGroup, form, cancellationToken);
|
|
|
|
/// <summary>Joins a group by its invitation (and its password, when it has one); Data is the ViewGroup.</summary>
|
|
public Task<WebResult> Join(JoinGroupForm form, CancellationToken cancellationToken = default) =>
|
|
GroupResult(APIs.ClientApi.POST_JoinGroup, form, cancellationToken);
|
|
|
|
public Task<WebResult> Leave(string personaId, string groupId, CancellationToken cancellationToken = default) =>
|
|
Membership(APIs.ClientApi.POST_LeaveGroup, personaId, groupId, null, cancellationToken);
|
|
|
|
/// <summary>Lets in someone who asked to join a group the persona runs.</summary>
|
|
public Task<WebResult> Approve(string personaId, string groupId, string actorUri, CancellationToken cancellationToken = default) =>
|
|
Membership(APIs.ClientApi.POST_ApproveGroupMember, personaId, groupId, actorUri, cancellationToken);
|
|
|
|
/// <summary>Declines a request to join; the asker's server is told.</summary>
|
|
public Task<WebResult> Reject(string personaId, string groupId, string actorUri, CancellationToken cancellationToken = default) =>
|
|
Membership(APIs.ClientApi.POST_RejectGroupMember, personaId, groupId, actorUri, cancellationToken);
|
|
|
|
/// <summary>Takes a member out of a group the persona runs.</summary>
|
|
public Task<WebResult> Remove(string personaId, string groupId, string actorUri, CancellationToken cancellationToken = default) =>
|
|
Membership(APIs.ClientApi.POST_RemoveGroupMember, personaId, groupId, actorUri, cancellationToken);
|
|
|
|
/// <summary>Who is in a group the persona runs, and who asks to join; empty on failure.</summary>
|
|
public async Task<List<ViewGroupMember>> Members(string personaId, string groupId, CancellationToken cancellationToken = default)
|
|
{
|
|
using var response = await httpService.Get(APIs.ClientApi.GET_GroupMembers,
|
|
[$"avatarId={Uri.EscapeDataString(personaId)}", $"groupId={Uri.EscapeDataString(groupId)}"], cancellationToken: cancellationToken);
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var result = await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
|
|
await logger.ProcessWarning(result.ToString(localizer), $"{nameof(GroupService)}.{nameof(Members)}", cancellationToken);
|
|
return [];
|
|
}
|
|
|
|
return await response.DefaultReadFromJsonAsync<List<ViewGroupMember>>(cancellationToken) ?? [];
|
|
}
|
|
|
|
async Task<WebResult> GroupResult(string route, object form, CancellationToken cancellationToken)
|
|
{
|
|
using var response = await httpService.Post(route, form, cancellationToken: cancellationToken);
|
|
if (!response.IsSuccessStatusCode)
|
|
return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
|
|
|
|
return new WebResult { Data = await response.DefaultReadFromJsonAsync<ViewGroup>(cancellationToken) };
|
|
}
|
|
|
|
async Task<WebResult> Membership(string route, string personaId, string groupId, string actorUri, CancellationToken cancellationToken)
|
|
{
|
|
using var response = await httpService.Post(route, new GroupMembershipForm { AvatarId = personaId, GroupId = groupId, MemberActorURI = actorUri },
|
|
cancellationToken: cancellationToken);
|
|
if (!response.IsSuccessStatusCode)
|
|
return await response.Content.ReadWebResult(response.StatusCode, localizer, cancellationToken);
|
|
|
|
return new WebResult();
|
|
}
|
|
}
|
|
}
|