Bluesky app fork with some witchin' additions 💫

[Explore] Design tweaks (#8131)

* Fix interests card spacing

* Space out AvatarStack

* SP icon size

* Visual alignment of tab bar and headers

* Tweak spacing around search input

* Drop text size in sp card overflow count

* Tweak

authored by

Eric Bailey and committed by
GitHub
0b08128e aca89d4a

+30 -21
+3 -3
src/components/AvatarStack.tsx
··· 19 numPending?: number 20 backgroundColor?: string 21 }) { 22 - const halfSize = size / 2 23 const t = useTheme() 24 const moderationOpts = useModerationOpts() 25 ··· 43 a.flex_row, 44 a.align_center, 45 a.relative, 46 - {width: size + (items.length - 1) * halfSize}, 47 ]}> 48 {items.map((item, i) => ( 49 <View ··· 54 { 55 width: size, 56 height: size, 57 - left: i * -halfSize, 58 borderWidth: 1, 59 borderColor: backgroundColor ?? t.atoms.bg.backgroundColor, 60 borderRadius: 999,
··· 19 numPending?: number 20 backgroundColor?: string 21 }) { 22 + const translation = size / 3 // overlap by 1/3 23 const t = useTheme() 24 const moderationOpts = useModerationOpts() 25 ··· 43 a.flex_row, 44 a.align_center, 45 a.relative, 46 + {width: size + (items.length - 1) * (size - translation)}, 47 ]}> 48 {items.map((item, i) => ( 49 <View ··· 54 { 55 width: size, 56 height: size, 57 + left: i * -translation, 58 borderWidth: 1, 59 borderColor: backgroundColor ?? t.atoms.bg.backgroundColor, 60 borderRadius: 999,
+11 -2
src/components/ProgressGuide/FollowDialog.tsx
··· 1 import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react' 2 - import {ScrollView, TextInput, useWindowDimensions, View} from 'react-native' 3 import Animated, { 4 LayoutAnimationConfig, 5 LinearTransition, ··· 453 hasSearchText, 454 interestsDisplayNames, 455 TabComponent = Tab, 456 }: { 457 onSelectTab: (tab: string) => void 458 interests: string[] ··· 460 hasSearchText: boolean 461 interestsDisplayNames: Record<string, string> 462 TabComponent?: React.ComponentType<React.ComponentProps<typeof Tab>> 463 }): React.ReactNode => { 464 const listRef = useRef<ScrollView>(null) 465 const [scrollX, setScrollX] = useState(0) ··· 520 <ScrollView 521 ref={listRef} 522 horizontal 523 - contentContainerStyle={[a.gap_sm, a.px_lg]} 524 showsHorizontalScrollIndicator={false} 525 decelerationRate="fast" 526 snapToOffsets={
··· 1 import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react' 2 + import { 3 + ScrollView, 4 + type StyleProp, 5 + TextInput, 6 + useWindowDimensions, 7 + View, 8 + type ViewStyle, 9 + } from 'react-native' 10 import Animated, { 11 LayoutAnimationConfig, 12 LinearTransition, ··· 460 hasSearchText, 461 interestsDisplayNames, 462 TabComponent = Tab, 463 + contentContainerStyle, 464 }: { 465 onSelectTab: (tab: string) => void 466 interests: string[] ··· 468 hasSearchText: boolean 469 interestsDisplayNames: Record<string, string> 470 TabComponent?: React.ComponentType<React.ComponentProps<typeof Tab>> 471 + contentContainerStyle?: StyleProp<ViewStyle> 472 }): React.ReactNode => { 473 const listRef = useRef<ScrollView>(null) 474 const [scrollX, setScrollX] = useState(0) ··· 529 <ScrollView 530 ref={listRef} 531 horizontal 532 + contentContainerStyle={[a.gap_sm, a.px_lg, contentContainerStyle]} 533 showsHorizontalScrollIndicator={false} 534 decelerationRate="fast" 535 snapToOffsets={
+4 -1
src/screens/Search/Explore.tsx
··· 45 import * as FeedCard from '#/components/FeedCard' 46 import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon} from '#/components/icons/Chevron' 47 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 48 import {type Props as SVGIconProps} from '#/components/icons/common' 49 import {ListSparkle_Stroke2_Corner0_Rounded as ListSparkle} from '#/components/icons/ListSparkle' 50 import {StarterPack} from '#/components/icons/StarterPack' ··· 110 key: string 111 title: string 112 icon: React.ComponentType<SVGIconProps> 113 searchButton?: { 114 label: string 115 metricsTag: MetricEvents['explore:module:searchButtonPress']['module'] ··· 461 key: 'suggested-starterPacks-header', 462 title: _(msg`Starter Packs`), 463 icon: StarterPack, 464 }) 465 466 if (isLoadingSuggestedSPs) { ··· 562 case 'header': { 563 return ( 564 <ModuleHeader.Container> 565 - <ModuleHeader.Icon icon={item.icon} /> 566 <ModuleHeader.TitleText>{item.title}</ModuleHeader.TitleText> 567 {item.searchButton && ( 568 <ModuleHeader.SearchButton
··· 45 import * as FeedCard from '#/components/FeedCard' 46 import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon} from '#/components/icons/Chevron' 47 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 48 + import {type Props as IcoProps} from '#/components/icons/common' 49 import {type Props as SVGIconProps} from '#/components/icons/common' 50 import {ListSparkle_Stroke2_Corner0_Rounded as ListSparkle} from '#/components/icons/ListSparkle' 51 import {StarterPack} from '#/components/icons/StarterPack' ··· 111 key: string 112 title: string 113 icon: React.ComponentType<SVGIconProps> 114 + iconSize?: IcoProps['size'] 115 searchButton?: { 116 label: string 117 metricsTag: MetricEvents['explore:module:searchButtonPress']['module'] ··· 463 key: 'suggested-starterPacks-header', 464 title: _(msg`Starter Packs`), 465 icon: StarterPack, 466 + iconSize: 'xl', 467 }) 468 469 if (isLoadingSuggestedSPs) { ··· 565 case 'header': { 566 return ( 567 <ModuleHeader.Container> 568 + <ModuleHeader.Icon icon={item.icon} size={item.iconSize} /> 569 <ModuleHeader.TitleText>{item.title}</ModuleHeader.TitleText> 570 {item.searchButton && ( 571 <ModuleHeader.SearchButton
+1 -1
src/screens/Search/Shell.tsx
··· 318 </Layout.Header.Outer> 319 </View> 320 )} 321 - <View style={[a.px_md, a.pt_sm, a.pb_sm, a.overflow_hidden]}> 322 <View style={[a.gap_sm]}> 323 <View style={[a.w_full, a.flex_row, a.align_stretch, a.gap_xs]}> 324 <View style={[a.flex_1]}>
··· 318 </Layout.Header.Outer> 319 </View> 320 )} 321 + <View style={[a.px_lg, a.pt_sm, a.pb_sm, a.overflow_hidden]}> 322 <View style={[a.gap_sm]}> 323 <View style={[a.w_full, a.flex_row, a.align_stretch, a.gap_xs]}> 324 <View style={[a.flex_1]}>
+2 -10
src/screens/Search/components/ModuleHeader.tsx
··· 6 import {makeCustomFeedLink} from '#/lib/routes/links' 7 import {logger} from '#/logger' 8 import {UserAvatar} from '#/view/com/util/UserAvatar' 9 - import { 10 - atoms as a, 11 - native, 12 - useGutters, 13 - useTheme, 14 - type ViewStyleProp, 15 - web, 16 - } from '#/alf' 17 import {Button, ButtonIcon} from '#/components/Button' 18 import * as FeedCard from '#/components/FeedCard' 19 import {sizes as iconSizes} from '#/components/icons/common' ··· 27 headerHeight, 28 }: {children: React.ReactNode; headerHeight?: number} & ViewStyleProp) { 29 const t = useTheme() 30 - const gutters = useGutters([0, 'base']) 31 return ( 32 <View 33 style={[ 34 - gutters, 35 a.flex_row, 36 a.align_center, 37 a.pt_2xl, 38 a.pb_md, 39 a.gap_sm,
··· 6 import {makeCustomFeedLink} from '#/lib/routes/links' 7 import {logger} from '#/logger' 8 import {UserAvatar} from '#/view/com/util/UserAvatar' 9 + import {atoms as a, native, useTheme, type ViewStyleProp, web} from '#/alf' 10 import {Button, ButtonIcon} from '#/components/Button' 11 import * as FeedCard from '#/components/FeedCard' 12 import {sizes as iconSizes} from '#/components/icons/common' ··· 20 headerHeight, 21 }: {children: React.ReactNode; headerHeight?: number} & ViewStyleProp) { 22 const t = useTheme() 23 return ( 24 <View 25 style={[ 26 a.flex_row, 27 a.align_center, 28 + a.px_lg, 29 a.pt_2xl, 30 a.pb_md, 31 a.gap_sm,
+1 -1
src/screens/Search/components/StarterPackCard.tsx
··· 234 {computedTotal > 0 ? ( 235 <Text 236 style={[ 237 - gtPhone ? a.text_md : a.text_sm, 238 a.font_bold, 239 a.leading_snug, 240 {color: 'white'},
··· 234 {computedTotal > 0 ? ( 235 <Text 236 style={[ 237 + gtPhone ? a.text_md : a.text_xs, 238 a.font_bold, 239 a.leading_snug, 240 {color: 'white'},
+2 -3
src/screens/Search/modules/ExploreInterestsCard.tsx
··· 6 import {Nux, useSaveNux} from '#/state/queries/nuxs' 7 import {usePreferencesQuery} from '#/state/queries/preferences' 8 import {useInterestsDisplayNames} from '#/screens/Onboarding/state' 9 - import {atoms as a, useGutters, useTheme} from '#/alf' 10 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 11 import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' 12 import {Link} from '#/components/Link' ··· 16 export function ExploreInterestsCard() { 17 const t = useTheme() 18 const {_} = useLingui() 19 - const gutters = useGutters([0, 'base']) 20 const {data: preferences} = usePreferencesQuery() 21 const interestsDisplayNames = useInterestsDisplayNames() 22 const {mutateAsync: saveNux} = useSaveNux() ··· 53 onConfirm={onConfirmClose} 54 /> 55 56 - <View style={[gutters, a.pt_lg, a.pb_2xs]}> 57 <View 58 style={[ 59 a.p_lg,
··· 6 import {Nux, useSaveNux} from '#/state/queries/nuxs' 7 import {usePreferencesQuery} from '#/state/queries/preferences' 8 import {useInterestsDisplayNames} from '#/screens/Onboarding/state' 9 + import {atoms as a, useTheme} from '#/alf' 10 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 11 import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' 12 import {Link} from '#/components/Link' ··· 16 export function ExploreInterestsCard() { 17 const t = useTheme() 18 const {_} = useLingui() 19 const {data: preferences} = usePreferencesQuery() 20 const interestsDisplayNames = useInterestsDisplayNames() 21 const {mutateAsync: saveNux} = useSaveNux() ··· 52 onConfirm={onConfirmClose} 53 /> 54 55 + <View style={[a.p_lg, a.pb_2xs]}> 56 <View 57 style={[ 58 a.p_lg,
+6
src/screens/Search/modules/ExploreSuggestedAccounts.tsx
··· 87 ...interestsDisplayNames, 88 }} 89 TabComponent={Tab} 90 /> 91 </BlockDrawerGesture> 92 )
··· 87 ...interestsDisplayNames, 88 }} 89 TabComponent={Tab} 90 + contentContainerStyle={[ 91 + { 92 + // visual alignment 93 + paddingLeft: a.px_md.paddingLeft, 94 + }, 95 + ]} 96 /> 97 </BlockDrawerGesture> 98 )