From a148b2477b93c60c9b5bd641b2fe1dd3b1e6ba7a Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Wed, 4 Feb 2026 15:06:42 -0500 Subject: [PATCH 1/6] update design for stacked avatars --- src/components/ProgressGuide/List.tsx | 138 ++++++++++++++------------ src/components/icons/Person.tsx | 5 + 2 files changed, 82 insertions(+), 61 deletions(-) diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx index 81abc586f3..f86059a4c9 100644 --- a/src/components/ProgressGuide/List.tsx +++ b/src/components/ProgressGuide/List.tsx @@ -1,4 +1,10 @@ -import {type StyleProp, View, type ViewStyle} from 'react-native' +import {useState} from 'react' +import { + type LayoutChangeEvent, + type StyleProp, + View, + type ViewStyle, +} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -11,7 +17,7 @@ import { import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useBreakpoints, useLayoutBreakpoints, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' -import {Person_Stroke2_Corner2_Rounded as PersonIcon} from '#/components/icons/Person' +import {Person_Filled as PersonIcon} from '#/components/icons/Person' import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times' import {Text} from '#/components/Typography' import type * as bsky from '#/types/bsky' @@ -51,7 +57,7 @@ export function ProgressGuideList({style}: {style?: StyleProp}) { a.flex_col, a.gap_md, a.rounded_md, - t.atoms.bg_contrast_25, + t.atoms.bg_contrast_50, a.p_lg, style, ]}> @@ -112,70 +118,80 @@ export function ProgressGuideList({style}: {style?: StyleProp}) { function StackedAvatars({follows}: {follows?: bsky.profile.AnyProfileView[]}) { const t = useTheme() - const {centerColumnOffset} = useLayoutBreakpoints() + const [containerWidth, setContainerWidth] = useState(0) - // Smaller avatars for narrower viewport - const avatarSize = centerColumnOffset ? 30 : 37 - const overlap = centerColumnOffset ? 9 : 11 - const iconSize = centerColumnOffset ? 14 : 18 + const onLayout = (e: LayoutChangeEvent) => { + setContainerWidth(e.nativeEvent.layout.width) + } + + // Overlap ratio (22% of avatar size) + const overlapRatio = 0.22 + + // Calculate avatar size to fill container width + // Formula: containerWidth = avatarSize * count - overlap * (count - 1) + // Where overlap = avatarSize * overlapRatio + const visiblePortions = TOTAL_AVATARS - overlapRatio * (TOTAL_AVATARS - 1) + const avatarSize = containerWidth > 0 ? containerWidth / visiblePortions : 0 + const overlap = avatarSize * overlapRatio + const iconSize = avatarSize * 0.45 - // 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) => ( - - - - ))} + + {containerWidth > 0 && ( + <> + {/* Show followed user avatars */} + {followedAvatars.map((follow, i) => ( + + + + ))} + {/* Show placeholder avatars for remaining slots */} + {Array(remainingSlots) + .fill(0) + .map((_, i) => ( + + + + ))} + + )} ) } diff --git a/src/components/icons/Person.tsx b/src/components/icons/Person.tsx index 55324f10d6..74a8ac97dd 100644 --- a/src/components/icons/Person.tsx +++ b/src/components/icons/Person.tsx @@ -36,3 +36,8 @@ export const PersonPlus_Stroke2_Corner2_Rounded = createSinglePathSVG({ export const PersonGroup_Stroke2_Corner2_Rounded = createSinglePathSVG({ path: 'M8 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM4 7a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm13-1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm-3.5 1.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Zm7.301 9.7c-.836-2.6-2.88-3.503-4.575-3.111a1 1 0 0 1-.451-1.949c2.815-.651 5.81.966 6.93 4.448a2.49 2.49 0 0 1-.506 2.43A2.92 2.92 0 0 1 20 20h-2a1 1 0 1 1 0-2h2a.92.92 0 0 0 .69-.295.49.49 0 0 0 .112-.505ZM8 14c-1.865 0-3.878 1.274-4.681 4.151a.57.57 0 0 0 .132.55c.15.171.4.299.695.299h7.708a.93.93 0 0 0 .695-.299.57.57 0 0 0 .132-.55C11.878 15.274 9.865 14 8 14Zm0-2c2.87 0 5.594 1.98 6.607 5.613.53 1.9-1.09 3.387-2.753 3.387H4.146c-1.663 0-3.283-1.487-2.753-3.387C2.406 13.981 5.129 12 8 12Z', }) + +export const Person_Filled = createSinglePathSVG({ + viewBox: '0 0 14 16', + path: 'M6.5 0C4.567 0 3 1.567 3 3.5C3 5.433 4.567 7 6.5 7C8.43301 7 10 5.433 10 3.5C10 1.567 8.43301 0 6.5 0Z M6.50002 8C3.43062 8 1.08148 9.78593 0.127626 12.2902C-0.145062 13.0062 0.0416292 13.7118 0.475104 14.2134C0.897533 14.7023 1.54964 15 2.25212 15H10.7479C11.4503 15 12.1024 14.7023 12.5249 14.2134C12.9584 13.7118 13.145 13.0062 12.8724 12.2902C11.9185 9.78593 9.56941 8 6.50002 8Z', +}) From a57faedbae1508f89df6c510fcab5d6f35d71c44 Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Thu, 5 Feb 2026 08:51:53 -0500 Subject: [PATCH 2/6] update icon --- assets/icons/person_filled_corner2_rounded.svg | 1 + src/components/ProgressGuide/List.tsx | 4 ++-- src/components/icons/Person.tsx | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 assets/icons/person_filled_corner2_rounded.svg diff --git a/assets/icons/person_filled_corner2_rounded.svg b/assets/icons/person_filled_corner2_rounded.svg new file mode 100644 index 0000000000..d22056593d --- /dev/null +++ b/assets/icons/person_filled_corner2_rounded.svg @@ -0,0 +1 @@ + diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx index f86059a4c9..868ee79a3e 100644 --- a/src/components/ProgressGuide/List.tsx +++ b/src/components/ProgressGuide/List.tsx @@ -17,7 +17,7 @@ import { import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useBreakpoints, useLayoutBreakpoints, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' -import {Person_Filled as PersonIcon} from '#/components/icons/Person' +import {Person_Filled_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 type * as bsky from '#/types/bsky' @@ -133,7 +133,7 @@ function StackedAvatars({follows}: {follows?: bsky.profile.AnyProfileView[]}) { const visiblePortions = TOTAL_AVATARS - overlapRatio * (TOTAL_AVATARS - 1) const avatarSize = containerWidth > 0 ? containerWidth / visiblePortions : 0 const overlap = avatarSize * overlapRatio - const iconSize = avatarSize * 0.45 + const iconSize = avatarSize * 0.5 const followedAvatars = follows?.slice(0, TOTAL_AVATARS) ?? [] const remainingSlots = TOTAL_AVATARS - followedAvatars.length diff --git a/src/components/icons/Person.tsx b/src/components/icons/Person.tsx index 74a8ac97dd..83a6db1f28 100644 --- a/src/components/icons/Person.tsx +++ b/src/components/icons/Person.tsx @@ -37,7 +37,6 @@ export const PersonGroup_Stroke2_Corner2_Rounded = createSinglePathSVG({ path: 'M8 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM4 7a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm13-1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm-3.5 1.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Zm7.301 9.7c-.836-2.6-2.88-3.503-4.575-3.111a1 1 0 0 1-.451-1.949c2.815-.651 5.81.966 6.93 4.448a2.49 2.49 0 0 1-.506 2.43A2.92 2.92 0 0 1 20 20h-2a1 1 0 1 1 0-2h2a.92.92 0 0 0 .69-.295.49.49 0 0 0 .112-.505ZM8 14c-1.865 0-3.878 1.274-4.681 4.151a.57.57 0 0 0 .132.55c.15.171.4.299.695.299h7.708a.93.93 0 0 0 .695-.299.57.57 0 0 0 .132-.55C11.878 15.274 9.865 14 8 14Zm0-2c2.87 0 5.594 1.98 6.607 5.613.53 1.9-1.09 3.387-2.753 3.387H4.146c-1.663 0-3.283-1.487-2.753-3.387C2.406 13.981 5.129 12 8 12Z', }) -export const Person_Filled = createSinglePathSVG({ - viewBox: '0 0 14 16', - path: 'M6.5 0C4.567 0 3 1.567 3 3.5C3 5.433 4.567 7 6.5 7C8.43301 7 10 5.433 10 3.5C10 1.567 8.43301 0 6.5 0Z M6.50002 8C3.43062 8 1.08148 9.78593 0.127626 12.2902C-0.145062 13.0062 0.0416292 13.7118 0.475104 14.2134C0.897533 14.7023 1.54964 15 2.25212 15H10.7479C11.4503 15 12.1024 14.7023 12.5249 14.2134C12.9584 13.7118 13.145 13.0062 12.8724 12.2902C11.9185 9.78593 9.56941 8 6.50002 8Z', +export const Person_Filled_Corner2_Rounded = createSinglePathSVG({ + path: 'M12.233 2a4.433 4.433 0 1 0 0 8.867 4.433 4.433 0 0 0 0-8.867ZM12.233 12.133c-3.888 0-6.863 2.263-8.071 5.435-.346.906-.11 1.8.44 2.436.535.619 1.36.996 2.25.996h10.762c.89 0 1.716-.377 2.25-.996.55-.636.786-1.53.441-2.436-1.208-3.173-4.184-5.435-8.072-5.435Z', }) From 8581285d34e635898fdd396a9a98cd3488a5cba1 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 11 Feb 2026 16:20:48 +0200 Subject: [PATCH 3/6] simplify some conditionals --- src/components/ProgressGuide/List.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx index 868ee79a3e..d7cb4d0f03 100644 --- a/src/components/ProgressGuide/List.tsx +++ b/src/components/ProgressGuide/List.tsx @@ -87,8 +87,7 @@ export function ProgressGuideList({style}: {style?: StyleProp}) { a.justify_between, a.gap_sm, ] - : a.flex_col, - !inlineLayout && a.gap_md, + : [a.flex_col, a.gap_md], ]}> From 63fbf577d994ae0fd0e0ea9be75dfdfff7b0ddfb Mon Sep 17 00:00:00 2001 From: Spence Pope Date: Wed, 11 Feb 2026 13:11:15 -0500 Subject: [PATCH 4/6] Update src/components/ProgressGuide/List.tsx Co-authored-by: Samuel Newman --- src/components/ProgressGuide/List.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx index d7cb4d0f03..4a25e02e79 100644 --- a/src/components/ProgressGuide/List.tsx +++ b/src/components/ProgressGuide/List.tsx @@ -158,6 +158,7 @@ function StackedAvatars({follows}: {follows?: bsky.profile.AnyProfileView[]}) { type="user" size={avatarSize - 2} avatar={follow.avatar} + noBorder /> ))} From 1ec70420e60d5ceaba970624ae7cb9ba1a01693f Mon Sep 17 00:00:00 2001 From: Spence Pope Date: Wed, 11 Feb 2026 13:11:24 -0500 Subject: [PATCH 5/6] Update src/components/ProgressGuide/List.tsx Co-authored-by: Samuel Newman --- src/components/ProgressGuide/List.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx index 4a25e02e79..8ce57a8436 100644 --- a/src/components/ProgressGuide/List.tsx +++ b/src/components/ProgressGuide/List.tsx @@ -147,11 +147,11 @@ function StackedAvatars({follows}: {follows?: bsky.profile.AnyProfileView[]}) { key={follow.did} style={[ a.rounded_full, + a.border, + t.atoms.border_contrast_low, { marginLeft: i === 0 ? 0 : -overlap, zIndex: TOTAL_AVATARS - i, - borderWidth: 1, - borderColor: t.atoms.bg_contrast_25.backgroundColor, }, ]}> Date: Wed, 11 Feb 2026 13:11:30 -0500 Subject: [PATCH 6/6] Update src/components/ProgressGuide/List.tsx Co-authored-by: Samuel Newman --- src/components/ProgressGuide/List.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx index 8ce57a8436..e07f3305f2 100644 --- a/src/components/ProgressGuide/List.tsx +++ b/src/components/ProgressGuide/List.tsx @@ -173,14 +173,14 @@ function StackedAvatars({follows}: {follows?: bsky.profile.AnyProfileView[]}) { a.justify_center, a.rounded_full, t.atoms.bg_contrast_300, + a.border, + t.atoms.border_contrast_low, { width: avatarSize, height: avatarSize, marginLeft: followedAvatars.length === 0 && i === 0 ? 0 : -overlap, zIndex: TOTAL_AVATARS - followedAvatars.length - i, - borderWidth: 1, - borderColor: t.atoms.border_contrast_low.borderColor, }, ]}>