Lists: made on /lists, read as a tab of the feed

A persona's lists (PrivaPub's /api/v1/lists): made, renamed, given a
replies policy and made exclusive on /lists, filled from the accounts it
follows (accounts/search?following=true), and read as the feed's Lists tab.
A followed account's profile has a Lists menu to put it in or take it out.
ListService does the calls; the e2e tests check each step against
PrivaPub's own answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-05 02:49:51 +02:00
1 parent 011bafb6af
commit e2491de16e
18 files changed
+914 -9

No files matched your search

+8
View File
@@ -62,6 +62,14 @@ public static class APIs
$"api/v1/pleroma/statuses/{Uri.EscapeDataString(statusId)}/reactions/{Uri.EscapeDataString(emoji)}";
public static string POST_M_PollVotes(string pollId) => $"api/v1/polls/{Uri.EscapeDataString(pollId)}/votes";
public static string POST_M_FollowRequest(string accountId, string answer) => $"api/v1/follow_requests/{Uri.EscapeDataString(accountId)}/{answer}";
public const string GET_Lists = "api/v1/lists";
public const string POST_List = "api/v1/lists";
public const string GET_AccountSearch = "api/v1/accounts/search";
public static string PUT_M_List(string listId) => $"api/v1/lists/{Uri.EscapeDataString(listId)}";
public static string DELETE_M_List(string listId) => $"api/v1/lists/{Uri.EscapeDataString(listId)}";
public static string M_ListAccounts(string listId) => $"api/v1/lists/{Uri.EscapeDataString(listId)}/accounts";
public static string GET_M_AccountLists(string accountId) => $"api/v1/accounts/{Uri.EscapeDataString(accountId)}/lists";
public static string GET_M_ListTimeline(string listId) => $"api/v1/timelines/list/{Uri.EscapeDataString(listId)}";
}
public static class PrivaPub
+2
View File
@@ -8,6 +8,8 @@ public class FeedFilter
public TimelineType TimelineType { get; set; } = TimelineType.Home;
/// <summary>The community or circle the Groups tab shows.</summary>
public string GroupId { get; set; }
/// <summary>The persona's list the Lists tab shows.</summary>
public string ListId { get; set; }
/// <summary>The hashtag the Tag tab shows, without #.</summary>
public string Tag { get; set; }
/// <summary>Whether the globe is open where it can be closed (narrow screens).</summary>
+29
View File
@@ -0,0 +1,29 @@
using System.Text.Json.Serialization;
namespace decePubClient.Models.Mastodon;
/// <summary>A persona's list of accounts it follows (Mastodon's List): read as its own feed, and when exclusive kept out of
/// the home feed. Lists are the persona's own; PrivaPub never federates them.</summary>
public class MastodonList
{
public string Id { get; set; }
public string Title { get; set; }
public ListRepliesPolicy RepliesPolicy { get; set; } = ListRepliesPolicy.List;
public bool Exclusive { get; set; }
}
/// <summary>Which replies a list's feed shows besides its members' replies to themselves and to the persona.</summary>
[JsonConverter(typeof(JsonStringEnumConverter<ListRepliesPolicy>))]
public enum ListRepliesPolicy
{
/// <summary>Replies to any account the persona follows.</summary>
[JsonStringEnumMemberName("followed")] Followed,
/// <summary>Replies to the list's members.</summary>
[JsonStringEnumMemberName("list")] List,
[JsonStringEnumMemberName("none")] None
}
public class ListAccountsForm
{
public List<string> AccountIds { get; set; } = [];
}
+2 -1
View File
@@ -12,6 +12,7 @@ namespace decePubClient.Models.Types
Notifications,
Favourites,
Bookmarks,
Tag
Tag,
Lists
}
}