Let users browse followers and following lists

Following lists are public; followers are visible to the user and to mutual followers.
Adds GET /users/{id}/followers and /following, exposes isFollowing and canViewFollowers
on the profile, and returns isFollowedByViewer from user search so the web player no
longer calls /is-following per user.
This commit is contained in:
2026-09-23 22:55:14 +02:00
parent b9a40186b1
commit cb046bba9e
26 changed files with 768 additions and 64 deletions
@@ -5,7 +5,7 @@
title: string;
eyebrow?: string;
description?: string;
meta?: string;
meta?: string | Snippet;
cover?: Snippet;
actions?: Snippet;
align?: 'center' | 'end';
@@ -45,8 +45,10 @@
{#if description}
<p class="mt-3 max-w-2xl text-body text-fg-2">{description}</p>
{/if}
{#if meta}
{#if typeof meta === 'string'}
<p class="mt-3 text-sm text-fg-2">{meta}</p>
{:else if meta}
<p class="mt-3 text-sm text-fg-2">{@render meta()}</p>
{/if}
</div>
{#if actions && !cover}
@@ -0,0 +1,28 @@
<script lang="ts">
import ListRow from './ListRow.svelte';
import Avatar from './Avatar.svelte';
import FollowButton from './FollowButton.svelte';
interface Props {
user: {
id: string;
name: string;
profilePictureUrl: string | null;
isFollowedByViewer: boolean;
};
canFollow?: boolean;
}
let { user, canFollow = false }: Props = $props();
</script>
<div class="flex items-center gap-3">
<ListRow title={user.name} href="/user/{user.id}" size="lg" class="min-w-0 flex-1">
{#snippet art()}
<Avatar name={user.name} src={user.profilePictureUrl} size="md" />
{/snippet}
</ListRow>
{#if canFollow}
<FollowButton following={user.isFollowedByViewer} userId={user.id} class="shrink-0" />
{/if}
</div>