diff --git a/src/lib/atproto/server/feed.remote.ts b/src/lib/atproto/server/feed.remote.ts index 9257e6e..f9cbea6 100644 --- a/src/lib/atproto/server/feed.remote.ts +++ b/src/lib/atproto/server/feed.remote.ts @@ -162,6 +162,26 @@ export const deleteRsvp = command( } ); +export const getProfile = command( + v.object({ + actor: v.string() + }), + async (input) => { + const { locals } = getRequestEvent(); + + const client = locals.client ?? new Client({ + handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }) + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const res = await client.get('app.bsky.actor.getProfile', { + params: { actor: input.actor as any } // eslint-disable-line @typescript-eslint/no-explicit-any + }); + if (!res.ok) error(res.status, 'Failed to load profile'); + return res.data; + } +); + export const getPostThread = command( v.object({ uri: v.string(), diff --git a/src/routes/profile/[handle]/+page.svelte b/src/routes/profile/[handle]/+page.svelte index 72e8b82..73849e5 100644 --- a/src/routes/profile/[handle]/+page.svelte +++ b/src/routes/profile/[handle]/+page.svelte @@ -6,10 +6,9 @@ import { Button } from '@foxui/core'; import { Loader2, LogOut } from '@lucide/svelte'; import { user, logout } from '$lib/atproto/auth.svelte'; - import { actorToDid, getDetailedProfile } from '$lib/atproto/methods'; + import { actorToDid } from '$lib/atproto/methods'; import { getCachedProfile, cacheProfile, prefetchThread, ingestFeedPosts } from '$lib/cache.svelte'; - import { getAuthorFeed, followUser, unfollowUser } from '$lib/atproto/server/feed.remote'; - import { Client, simpleFetchHandler } from '@atcute/client'; + import { getAuthorFeed, followUser, unfollowUser, getProfile } from '$lib/atproto/server/feed.remote'; import type { FeedItem } from '$lib/cache.svelte'; import ScrollablePostList, { getCachedList, setCachedList } from '$lib/components/ScrollablePostList.svelte'; @@ -18,6 +17,8 @@ let isOwnProfile = $derived(user.did && profile?.did === user.did); let followUri = $state(null); let isFollowing = $derived(followUri !== null); + let followsMe = $state(false); + let isMutual = $derived(isFollowing && followsMe); let followLoading = $state(false); let loading = $state(true); @@ -60,6 +61,7 @@ async function loadProfile(actor: string) { error = null; followUri = null; + followsMe = false; const key = `profile-${actor}`; @@ -90,16 +92,14 @@ postsLoading = true; } - // Fetch fresh profile + // Fetch fresh profile (authenticated if logged in, for viewer state) try { - const client = new Client({ - handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }) - }); - const fresh = await getDetailedProfile({ did, client }); + const fresh = await getProfile({ actor }); if (fresh) { profile = fresh; cacheProfile(fresh); followUri = fresh.viewer?.following ?? null; + followsMe = !!fresh.viewer?.followedBy; } else if (!cached) { error = 'Profile not found'; } @@ -155,7 +155,7 @@
-
+
{#if loading}
@@ -176,7 +176,7 @@ class="" >
-
+
+ {#if !isOwnProfile && user.did} + {#if isMutual} + Mutuals + {:else if followsMe} + Follows you + {/if} + {/if}
{#if isOwnProfile} {:else if user.did}