diff --git a/src/components/ProgressGuide/FollowDialog.tsx b/src/components/ProgressGuide/FollowDialog.tsx
index bf567091b3..383b5a1610 100644
--- a/src/components/ProgressGuide/FollowDialog.tsx
+++ b/src/components/ProgressGuide/FollowDialog.tsx
@@ -32,7 +32,6 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {MagnifyingGlass_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass'
-import {PersonGroup_Stroke2_Corner2_Rounded as PersonGroupIcon} from '#/components/icons/Person'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {boostInterests, InterestTabs} from '#/components/InterestTabs'
import * as ProfileCard from '#/components/ProfileCard'
@@ -77,7 +76,6 @@ export function FollowDialog({guide}: {guide: Follow10ProgressGuide}) {
size={gtMobile ? 'small' : 'large'}
color="primary"
variant="solid">
-
Find people to follow
diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx
index 1750b0ffcf..bb962cfe46 100644
--- a/src/components/ProgressGuide/List.tsx
+++ b/src/components/ProgressGuide/List.tsx
@@ -2,24 +2,41 @@ import {type StyleProp, View, type ViewStyle} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
+import {useSession} from '#/state/session'
import {
useProgressGuide,
useProgressGuideControls,
} from '#/state/shell/progress-guide'
+import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
+import {Person_Stroke2_Corner2_Rounded as PersonIcon} from '#/components/icons/Person'
import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times'
import {Text} from '#/components/Typography'
import {FollowDialog} from './FollowDialog'
import {ProgressGuideTask} from './Task'
+const TOTAL_AVATARS = 10
+
export function ProgressGuideList({style}: {style?: StyleProp}) {
const t = useTheme()
const {_} = useLingui()
+ const {currentAccount} = useSession()
const followProgressGuide = useProgressGuide('follow-10')
const followAndLikeProgressGuide = useProgressGuide('like-10-and-follow-7')
const guide = followProgressGuide || followAndLikeProgressGuide
const {endProgressGuide} = useProgressGuideControls()
+ const {data: follows} = useProfileFollowsQuery(currentAccount?.did, {
+ limit: TOTAL_AVATARS,
+ })
+
+ const actualFollowsCount = follows?.pages?.[0]?.follows?.length ?? 0
+
+ // Hide if user already follows 10+ people
+ if (guide?.guide === 'follow-10' && actualFollowsCount >= TOTAL_AVATARS) {
+ return null
+ }
if (guide) {
return (
@@ -36,9 +53,8 @@ export function ProgressGuideList({style}: {style?: StyleProp}) {
a.pl_md,
]}>
-
- Getting Started
+
+ Follow 10 people to get started
{guide.guide === 'follow-10' && (
<>
-
+
>
)}
@@ -83,3 +94,69 @@ export function ProgressGuideList({style}: {style?: StyleProp}) {
}
return null
}
+
+function StackedAvatars({follows}: {follows?: {avatar?: string}[]}) {
+ const t = useTheme()
+ const avatarSize = 38
+ const overlap = 11
+
+ // Use actual follows count, not the guide's event counter
+ const followedAvatars = follows?.slice(0, TOTAL_AVATARS) ?? []
+ const remainingSlots = TOTAL_AVATARS - followedAvatars.length
+
+ // Total width calculation: first avatar + (remaining * visible portion)
+ const totalWidth = avatarSize + (TOTAL_AVATARS - 1) * (avatarSize - overlap)
+
+ return (
+
+ {/* Show followed user avatars */}
+ {followedAvatars.map((follow, i) => (
+
+
+
+ ))}
+ {/* Show placeholder avatars for remaining slots */}
+ {Array(remainingSlots)
+ .fill(0)
+ .map((_, i) => (
+
+
+
+ ))}
+
+ )
+}
diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts
index 9d30288d40..be31aa94c5 100644
--- a/src/state/queries/profile.ts
+++ b/src/state/queries/profile.ts
@@ -26,6 +26,7 @@ import {type Shadow} from '#/state/cache/types'
import {type ImageMeta} from '#/state/gallery'
import {STALE} from '#/state/queries'
import {resetProfilePostsQueries} from '#/state/queries/post-feed'
+import {RQKEY as PROFILE_FOLLOWS_RQKEY} from '#/state/queries/profile-follows'
import {
unstableCacheProfileView,
useUnstableProfileViewCache,
@@ -247,6 +248,7 @@ export function useProfileFollowMutationQueue(
) {
const agent = useAgent()
const queryClient = useQueryClient()
+ const {currentAccount} = useSession()
const did = profile.did
const initialFollowingUri = profile.viewer?.following
const followMutation = useProfileFollowMutation(
@@ -283,6 +285,44 @@ export function useProfileFollowMutationQueue(
followingUri: finalFollowingUri,
})
+ // Optimistically update profile follows cache for avatar displays
+ if (currentAccount?.did) {
+ queryClient.setQueryData(
+ PROFILE_FOLLOWS_RQKEY(currentAccount.did),
+ (old: any) => {
+ if (!old?.pages?.[0]) return old
+ if (finalFollowingUri) {
+ // Add the followed profile to the beginning
+ const alreadyExists = old.pages[0].follows.some(
+ (f: any) => f.did === profile.did,
+ )
+ if (alreadyExists) return old
+ return {
+ ...old,
+ pages: [
+ {
+ ...old.pages[0],
+ follows: [profile, ...old.pages[0].follows],
+ },
+ ...old.pages.slice(1),
+ ],
+ }
+ } else {
+ // Remove the unfollowed profile
+ return {
+ ...old,
+ pages: old.pages.map((page: any) => ({
+ ...page,
+ follows: page.follows.filter(
+ (f: any) => f.did !== profile.did,
+ ),
+ })),
+ }
+ }
+ },
+ )
+ }
+
if (finalFollowingUri) {
agent.app.bsky.graph
.getSuggestedFollowsByActor({
diff --git a/src/view/shell/desktop/Feeds.tsx b/src/view/shell/desktop/Feeds.tsx
index b8a53982b0..496946de90 100644
--- a/src/view/shell/desktop/Feeds.tsx
+++ b/src/view/shell/desktop/Feeds.tsx
@@ -70,7 +70,7 @@ export function DesktopFeeds() {
* Small padding prevents overflow prior to actually overflowing the
* height of the screen with lots of feeds.
*/
- paddingVertical: 2,
+ paddingTop: 2,
overflowY: 'auto',
}),
]}>
@@ -112,40 +112,50 @@ export function DesktopFeeds() {
a.self_start,
a.rounded_sm,
{paddingVertical: 6, paddingHorizontal: 8},
+ route.name === 'Feeds' && {backgroundColor: t.palette.primary_50},
]}>
- {({hovered}) => (
- <>
-
-
-
-
- {_(msg`More feeds`)}
-
- >
- )}
+ {({hovered}) => {
+ const isActive = route.name === 'Feeds'
+ return (
+ <>
+
+
+
+
+ {_(msg`More feeds`)}
+
+ >
+ )
+ }}
)
@@ -183,7 +193,6 @@ function FeedItem({
a.self_start,
a.rounded_sm,
{paddingVertical: 6, paddingHorizontal: 8},
- // current && t.atoms.bg_contrast_50,
current && {backgroundColor: t.palette.primary_50},
]}>
{isFollowing ? (
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 2672fd1769..788df6e64c 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -100,25 +100,31 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
email: currentAccount?.email,
handle: currentAccount?.handle,
})}
+ style={[t.atoms.text_contrast_medium]}
label={_(msg`Feedback`)}>
{_(msg`Feedback`)}
- {' • '}
+ {' ∙ '}
>
)}
{_(msg`Privacy`)}
- {' • '}
+ {' ∙ '}
{_(msg`Terms`)}
- {' • '}
-
+ {' ∙ '}
+
{_(msg`Help`)}
diff --git a/src/view/shell/desktop/SidebarTrendingTopics.tsx b/src/view/shell/desktop/SidebarTrendingTopics.tsx
index 1d28e49f56..29b8f014b0 100644
--- a/src/view/shell/desktop/SidebarTrendingTopics.tsx
+++ b/src/view/shell/desktop/SidebarTrendingTopics.tsx
@@ -97,6 +97,7 @@ function Inner() {
{
logEvent('trendingTopic:click', {context: 'sidebar'})
}}>