Bluesky app fork with some witchin' additions 💫

Remove use of `flatten` where possible (#9325)

* remove use of flatten where possible

* restore flatten where it says it's needed

* missing utils alpha

authored by samuel.fm and committed by GitHub 1b0fbd50 0bcde990

Changed files
+67 -97
src
components
screens
Messages
Onboarding
Profile
ProfileList
components
StarterPack
VideoFeed
view
com
util
icons
+4 -6
src/components/Button.tsx
··· 7 7 Pressable, 8 8 type PressableProps, 9 9 type StyleProp, 10 - StyleSheet, 11 10 type TargetedEvent, 12 11 type TextProps, 13 12 type TextStyle, ··· 512 511 [state, variant, color, size, disabled], 513 512 ) 514 513 515 - const flattenedBaseStyles = flatten([baseStyles, style]) 516 - 517 514 return ( 518 515 <PressableComponent 519 516 role="button" ··· 533 530 a.align_center, 534 531 a.justify_center, 535 532 a.curve_continuous, 536 - flattenedBaseStyles, 533 + baseStyles, 534 + style, 537 535 ...(state.hovered || state.pressed 538 - ? [hoverStyles, flatten(hoverStyleProp)] 536 + ? [hoverStyles, hoverStyleProp] 539 537 : []), 540 538 ]} 541 539 onPressIn={onPressIn} ··· 726 724 baseStyles.push(a.text_xs, a.leading_snug, a.font_semi_bold) 727 725 } 728 726 729 - return StyleSheet.flatten(baseStyles) 727 + return flatten(baseStyles) 730 728 }, [t, variant, color, size, disabled]) 731 729 } 732 730
+4 -4
src/components/Dialog/index.web.tsx
··· 16 16 import {logger} from '#/logger' 17 17 import {useA11y} from '#/state/a11y' 18 18 import {useDialogStateControlContext} from '#/state/dialogs' 19 - import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf' 19 + import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' 20 20 import {Button, ButtonIcon} from '#/components/Button' 21 21 import {Context} from '#/components/Dialog/context' 22 22 import { ··· 180 180 onClick={stopPropagation} 181 181 onStartShouldSetResponder={_ => true} 182 182 onTouchEnd={stopPropagation} 183 - style={flatten([ 183 + style={[ 184 184 a.relative, 185 185 a.rounded_md, 186 186 a.w_full, ··· 195 195 }, 196 196 !reduceMotionEnabled && a.zoom_fade_in, 197 197 style, 198 - ])}> 198 + ]}> 199 199 <DismissableLayer.DismissableLayer 200 200 onInteractOutside={preventDefault} 201 201 onFocusOutside={preventDefault} ··· 244 244 contentContainerStyle={[a.h_full, a.px_0, webInnerContentContainerStyle]}> 245 245 <FlatList 246 246 ref={ref} 247 - style={[a.h_full, gtMobile ? a.px_2xl : a.px_xl, flatten(style)]} 247 + style={[a.h_full, gtMobile ? a.px_2xl : a.px_xl, style]} 248 248 {...props} 249 249 /> 250 250 {footer}
+1 -1
src/components/FeedCard.tsx
··· 186 186 return rt 187 187 }, [description]) 188 188 if (!rt) return null 189 - return <RichText value={rt} style={[a.leading_snug]} disableLinks {...rest} /> 189 + return <RichText value={rt} disableLinks {...rest} /> 190 190 } 191 191 192 192 export function DescriptionPlaceholder() {
+2 -11
src/components/IconCircle.tsx
··· 2 2 3 3 import { 4 4 atoms as a, 5 - flatten, 6 5 type TextStyleProp, 7 6 useTheme, 8 7 type ViewStyleProp, ··· 33 32 height: size === 'lg' ? 52 : 64, 34 33 backgroundColor: t.palette.primary_50, 35 34 }, 36 - flatten(style), 35 + style, 37 36 ]}> 38 - <Icon 39 - size={size} 40 - style={[ 41 - { 42 - color: t.palette.primary_500, 43 - }, 44 - flatten(iconStyle), 45 - ]} 46 - /> 37 + <Icon size={size} style={[{color: t.palette.primary_500}, iconStyle]} /> 47 38 </View> 48 39 ) 49 40 }
+1 -1
src/components/LabelingServiceCard/index.tsx
··· 55 55 const {_} = useLingui() 56 56 return value ? ( 57 57 <Text numberOfLines={2}> 58 - <RichText value={value} style={[a.leading_snug]} /> 58 + <RichText value={value} /> 59 59 </Text> 60 60 ) : ( 61 61 <Text emoji style={[a.leading_snug]}>
+1 -1
src/components/Link.tsx
··· 296 296 return ( 297 297 <Button 298 298 {...rest} 299 - style={[a.justify_start, flatten(rest.style)]} 299 + style={[a.justify_start, rest.style]} 300 300 role="link" 301 301 accessibilityRole="link" 302 302 href={href}
+2 -2
src/components/Lists.tsx
··· 5 5 6 6 import {cleanError} from '#/lib/strings/errors' 7 7 import {CenteredView} from '#/view/com/util/Views' 8 - import {atoms as a, flatten, useBreakpoints, useTheme} from '#/alf' 8 + import {atoms as a, useBreakpoints, useTheme} from '#/alf' 9 9 import {Button, ButtonText} from '#/components/Button' 10 10 import {Error} from '#/components/Error' 11 11 import {Loader} from '#/components/Loader' ··· 43 43 a.pb_lg, 44 44 t.atoms.border_contrast_low, 45 45 {height: height ?? 180, paddingTop: 30}, 46 - flatten(style), 46 + style, 47 47 ]}> 48 48 {isFetchingNextPage ? ( 49 49 <Loader size="xl" />
+2 -7
src/components/Loader.tsx
··· 7 7 withTiming, 8 8 } from 'react-native-reanimated' 9 9 10 - import {atoms as a, flatten, useTheme} from '#/alf' 10 + import {atoms as a, useTheme} from '#/alf' 11 11 import {type Props, useCommonSVGProps} from '#/components/icons/common' 12 12 import {Loader_Stroke2_Corner0_Rounded as Icon} from '#/components/icons/Loader' 13 13 ··· 37 37 ]}> 38 38 <Icon 39 39 {...props} 40 - style={[ 41 - a.absolute, 42 - a.inset_0, 43 - t.atoms.text_contrast_high, 44 - flatten(props.style), 45 - ]} 40 + style={[a.absolute, a.inset_0, t.atoms.text_contrast_high, props.style]} 46 41 /> 47 42 </Animated.View> 48 43 )
+2 -2
src/components/Loader.web.tsx
··· 1 1 import {View} from 'react-native' 2 2 3 - import {atoms as a, flatten, useTheme} from '#/alf' 3 + import {atoms as a, useTheme} from '#/alf' 4 4 import {type Props, useCommonSVGProps} from '#/components/icons/common' 5 5 import {Loader_Stroke2_Corner0_Rounded as Icon} from '#/components/icons/Loader' 6 6 ··· 24 24 a.absolute, 25 25 a.inset_0, 26 26 t.atoms.text_contrast_high, 27 - flatten(props.style), 27 + props.style, 28 28 ]} 29 29 /> 30 30 </div>
+6 -6
src/components/PolicyUpdateOverlay/Overlay.tsx
··· 5 5 useSafeAreaInsets, 6 6 } from 'react-native-safe-area-context' 7 7 import {LinearGradient} from 'expo-linear-gradient' 8 + import {utils} from '@bsky.app/alf' 8 9 9 10 import {isAndroid, isNative} from '#/platform/detection' 10 11 import {useA11y} from '#/state/a11y' 11 - import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf' 12 - import {transparentifyColor} from '#/alf/util/colorGeneration' 12 + import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' 13 13 import {FocusScope} from '#/components/FocusScope' 14 14 import {LockScroll} from '#/components/LockScroll' 15 15 ··· 47 47 ) : ( 48 48 <LinearGradient 49 49 colors={[ 50 - transparentifyColor(t.atoms.bg.backgroundColor, 0), 50 + utils.alpha(t.atoms.bg.backgroundColor, 0), 51 51 t.atoms.bg.backgroundColor, 52 52 t.atoms.bg.backgroundColor, 53 53 ]} ··· 97 97 ]}> 98 98 <LinearGradient 99 99 colors={[ 100 - transparentifyColor(t.atoms.bg.backgroundColor, 0), 100 + utils.alpha(t.atoms.bg.backgroundColor, 0), 101 101 t.atoms.bg.backgroundColor, 102 102 ]} 103 103 start={[0.5, 0]} ··· 113 113 role="dialog" 114 114 aria-role="dialog" 115 115 aria-label={label} 116 - style={flatten([ 116 + style={[ 117 117 a.relative, 118 118 a.w_full, 119 119 a.p_2xl, ··· 128 128 maxWidth: 420, 129 129 }), 130 130 ], 131 - ])}> 131 + ]}> 132 132 {children} 133 133 </View> 134 134 </FocusScope>
+8 -7
src/components/PostControls/index.tsx
··· 1 - import {memo, useState} from 'react' 1 + import {memo, useMemo, useState} from 'react' 2 2 import {type StyleProp, View, type ViewStyle} from 'react-native' 3 3 import { 4 4 type AppBskyFeedDefs, ··· 25 25 useProgressGuideControls, 26 26 } from '#/state/shell/progress-guide' 27 27 import * as Toast from '#/view/com/util/Toast' 28 - import {atoms as a, flatten, useBreakpoints} from '#/alf' 28 + import {atoms as a, useBreakpoints} from '#/alf' 29 29 import {Reply as Bubble} from '#/components/icons/Reply' 30 30 import {useFormatPostStatCount} from '#/components/PostControls/util' 31 31 import {BookmarkButton} from './BookmarkButton' ··· 188 188 }) 189 189 } 190 190 191 - const secondaryControlSpacingStyles = flatten([ 192 - {gap: 0}, // default, we want `gap` to be defined on the resulting object 193 - variant !== 'compact' && a.gap_xs, 194 - (big || gtPhone) && a.gap_sm, 195 - ]) 191 + const secondaryControlSpacingStyles = useMemo(() => { 192 + let gap = 0 // default, we want `gap` to be defined on the resulting object 193 + if (variant !== 'compact') gap = a.gap_xs.gap 194 + if (big || gtPhone) gap = a.gap_sm.gap 195 + return {gap} 196 + }, [variant, big, gtPhone]) 196 197 197 198 return ( 198 199 <View
+1 -1
src/components/ProfileCard.tsx
··· 396 396 <View style={[a.pt_xs]}> 397 397 <RichText 398 398 value={rt} 399 - style={[a.leading_snug, style]} 399 + style={style} 400 400 numberOfLines={numberOfLines} 401 401 disableLinks 402 402 />
+3 -7
src/components/RichText.tsx
··· 48 48 [value], 49 49 ) 50 50 51 - const flattenedStyle = flatten(style) 52 - const plainStyles = [a.leading_snug, flattenedStyle] 53 - const interactiveStyles = [ 54 - a.leading_snug, 55 - flatten(interactiveStyle), 56 - flattenedStyle, 57 - ] 51 + const plainStyles = [a.leading_snug, style] 52 + const interactiveStyles = [plainStyles, interactiveStyle] 58 53 59 54 const {text, facets} = richText 60 55 61 56 if (!facets?.length) { 62 57 if (isOnlyEmoji(text)) { 58 + const flattenedStyle = flatten(style) 63 59 const fontSize = 64 60 (flattenedStyle.fontSize ?? a.text_sm.fontSize) * emojiMultiplier 65 61 return (
+4 -4
src/components/Tooltip/index.web.tsx
··· 1 1 import {Children, createContext, useContext, useMemo} from 'react' 2 2 import {View} from 'react-native' 3 + import {utils} from '@bsky.app/alf' 3 4 import {Popover} from 'radix-ui' 4 5 5 6 import {atoms as a, flatten, select, useTheme} from '#/alf' 6 - import {transparentifyColor} from '#/alf/util/colorGeneration' 7 7 import { 8 8 ARROW_SIZE, 9 9 BUBBLE_MAX_WIDTH, ··· 80 80 { 81 81 minWidth: 'max-content', 82 82 boxShadow: select(t.name, { 83 - light: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`, 84 - dark: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`, 85 - dim: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`, 83 + light: `0 0 24px ${utils.alpha(t.palette.black, 0.2)}`, 84 + dark: `0 0 24px ${utils.alpha(t.palette.black, 0.2)}`, 85 + dim: `0 0 24px ${utils.alpha(t.palette.black, 0.2)}`, 86 86 }), 87 87 }, 88 88 ])}>
+3 -3
src/components/Typography.tsx
··· 1 1 import {UITextView} from 'react-native-uitextview' 2 2 3 3 import {logger} from '#/logger' 4 - import {atoms, flatten, useAlf, useTheme, web} from '#/alf' 4 + import {atoms, useAlf, useTheme, web} from '#/alf' 5 5 import { 6 6 childHasEmoji, 7 7 normalizeTextStyles, ··· 26 26 }: TextProps) { 27 27 const {fonts, flags} = useAlf() 28 28 const t = useTheme() 29 - const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)], { 29 + const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, style], { 30 30 fontScale: fonts.scaleMultiplier, 31 31 fontFamily: fonts.family, 32 32 flags, ··· 84 84 <Text 85 85 {...attr} 86 86 {...rest} 87 - style={[atoms.text_md, atoms.leading_normal, flatten(style)]} 87 + style={[atoms.text_md, atoms.leading_relaxed, style]} 88 88 /> 89 89 ) 90 90 }
+3 -3
src/components/WelcomeModal.tsx
··· 8 8 import {logger} from '#/logger' 9 9 import {useLoggedOutViewControls} from '#/state/shell/logged-out' 10 10 import {Logo} from '#/view/icons/Logo' 11 - import {atoms as a, flatten, useBreakpoints, web} from '#/alf' 11 + import {atoms as a, useBreakpoints, web} from '#/alf' 12 12 import {Button, ButtonText} from '#/components/Button' 13 13 import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' 14 14 import {Text} from '#/components/Typography' ··· 78 78 ]}> 79 79 <FocusScope.FocusScope asChild loop trapped> 80 80 <View 81 - style={flatten([ 81 + style={[ 82 82 { 83 83 maxWidth: 800, 84 84 maxHeight: 600, ··· 89 89 a.rounded_lg, 90 90 a.overflow_hidden, 91 91 a.zoom_in, 92 - ])}> 92 + ]}> 93 93 <ImageBackground 94 94 source={welcomeModalBg} 95 95 style={[a.flex_1, a.justify_center]}
+2 -2
src/components/dms/EmojiReactionPicker.web.tsx
··· 134 134 accessibilityRole="button" 135 135 role="button" 136 136 onPress={() => setExpanded(true)} 137 - style={flatten([ 137 + style={[ 138 138 a.rounded_full, 139 139 {height: 34, width: 34}, 140 140 a.justify_center, 141 141 a.align_center, 142 - ])}> 142 + ]}> 143 143 <DotGridIcon size="lg" style={t.atoms.text_contrast_medium} /> 144 144 </Pressable> 145 145 </DropdownMenu.Item>
+1 -1
src/components/dms/ReportDialog.tsx
··· 424 424 ]}> 425 425 <RichText 426 426 value={rt} 427 - style={[a.text_md, a.leading_snug]} 427 + style={[a.text_md]} 428 428 interactiveStyle={a.underline} 429 429 enableTags 430 430 />
+2 -3
src/components/forms/Toggle.tsx
··· 6 6 import {isNative} from '#/platform/detection' 7 7 import { 8 8 atoms as a, 9 - flatten, 10 9 native, 11 10 type TextStyleProp, 12 11 useTheme, ··· 233 232 onPressOut={onPressOut} 234 233 onFocus={onFocus} 235 234 onBlur={onBlur} 236 - style={[a.flex_row, a.align_center, a.gap_sm, flatten(style)]}> 235 + style={[a.flex_row, a.align_center, a.gap_sm, style]}> 237 236 {typeof children === 'function' ? children(state) : children} 238 237 </Pressable> 239 238 </ItemContext.Provider> ··· 260 259 native({ 261 260 paddingTop: 2, 262 261 }), 263 - flatten(style), 262 + style, 264 263 ]}> 265 264 {children} 266 265 </Text>
+1 -1
src/components/icons/TEMPLATE.tsx
··· 40 40 viewBox="0 0 24 24" 41 41 width={size} 42 42 height={size} 43 - style={[style]}> 43 + style={style}> 44 44 {gradient} 45 45 <Path fill={fill} fillRule="evenodd" clipRule="evenodd" d={path} /> 46 46 </Svg>
+3 -3
src/screens/Messages/components/MessageInput.web.tsx
··· 1 1 import React from 'react' 2 - import {Pressable, StyleSheet, View} from 'react-native' 2 + import {Pressable, View} from 'react-native' 3 3 import {msg} from '@lingui/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 import Graphemer from 'graphemer' ··· 19 19 type EmojiPickerPosition, 20 20 } from '#/view/com/composer/text-input/web/EmojiPicker' 21 21 import * as Toast from '#/view/com/util/Toast' 22 - import {atoms as a, useTheme} from '#/alf' 22 + import {atoms as a, flatten, useTheme} from '#/alf' 23 23 import {Button} from '#/components/Button' 24 24 import {useSharedInputStyles} from '#/components/forms/TextField' 25 25 import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' ··· 199 199 </Button> 200 200 <TextareaAutosize 201 201 ref={textAreaRef} 202 - style={StyleSheet.flatten([ 202 + style={flatten([ 203 203 a.flex_1, 204 204 a.px_sm, 205 205 a.border_0,
+2 -11
src/screens/Onboarding/Layout.tsx
··· 10 10 import {Context} from '#/screens/Onboarding/state' 11 11 import { 12 12 atoms as a, 13 - flatten, 14 13 native, 15 14 type TextStyleProp, 16 15 tokens, ··· 228 227 }: React.PropsWithChildren<TextStyleProp>) { 229 228 return ( 230 229 <Text 231 - style={[ 232 - a.pb_sm, 233 - a.text_4xl, 234 - a.font_semi_bold, 235 - a.leading_tight, 236 - flatten(style), 237 - ]}> 230 + style={[a.pb_sm, a.text_4xl, a.font_semi_bold, a.leading_tight, style]}> 238 231 {children} 239 232 </Text> 240 233 ) ··· 245 238 style, 246 239 }: React.PropsWithChildren<TextStyleProp>) { 247 240 const t = useTheme() 248 - return ( 249 - <P style={[t.atoms.text_contrast_medium, flatten(style)]}>{children}</P> 250 - ) 241 + return <P style={[t.atoms.text_contrast_medium, style]}>{children}</P> 251 242 }
+1 -1
src/screens/Profile/components/ProfileFeedHeader.tsx
··· 481 481 </Button> 482 482 </View> 483 483 484 - <RichText value={rt} style={[a.text_md, a.leading_snug]} /> 484 + <RichText value={rt} style={[a.text_md]} /> 485 485 486 486 <View style={[a.flex_row, a.gap_sm, a.align_center]}> 487 487 {typeof likeCount === 'number' && (
+1 -1
src/screens/ProfileList/components/Header.tsx
··· 200 200 </ProfileSubpageHeader> 201 201 {descriptionRT ? ( 202 202 <View style={[a.px_lg, a.pt_sm, a.pb_sm, a.gap_md]}> 203 - <RichText value={descriptionRT} style={[a.text_md, a.leading_snug]} /> 203 + <RichText value={descriptionRT} style={[a.text_md]} /> 204 204 </View> 205 205 ) : null} 206 206 </>
+1 -3
src/screens/StarterPack/StarterPackScreen.tsx
··· 458 458 </ProfileSubpageHeader> 459 459 {!hasSession || richText || joinedAllTimeCount >= 25 ? ( 460 460 <View style={[a.px_lg, a.pt_md, a.pb_sm, a.gap_md]}> 461 - {richText ? ( 462 - <RichText value={richText} style={[a.text_md, a.leading_snug]} /> 463 - ) : null} 461 + {richText ? <RichText value={richText} style={[a.text_md]} /> : null} 464 462 {!hasSession ? ( 465 463 <Button 466 464 label={_(msg`Join Bluesky`)}
+1 -1
src/screens/VideoFeed/index.tsx
··· 955 955 ]}> 956 956 <RichText 957 957 value={value} 958 - style={[a.text_sm, a.flex_1, a.leading_normal]} 958 + style={[a.text_sm, a.flex_1, a.leading_relaxed]} 959 959 authorHandle={authorHandle} 960 960 enableTags 961 961 numberOfLines={
+2 -2
src/view/com/util/images/ImageLayoutGrid.tsx
··· 1 1 import React from 'react' 2 - import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' 2 + import {type StyleProp, View, type ViewStyle} from 'react-native' 3 3 import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated' 4 4 import {type AppBskyEmbedImages} from '@atproto/api' 5 5 ··· 224 224 if (corners.includes('bottomRight')) { 225 225 styles.push({borderBottomRightRadius: 0}) 226 226 } 227 - return StyleSheet.flatten(styles) 227 + return styles 228 228 }
+3 -2
src/view/icons/Logo.tsx
··· 1 1 import React from 'react' 2 - import {StyleSheet, type TextProps} from 'react-native' 2 + import {type TextProps} from 'react-native' 3 3 import Svg, { 4 4 Defs, 5 5 LinearGradient, ··· 12 12 13 13 import {colors} from '#/lib/styles' 14 14 import {useKawaiiMode} from '#/state/preferences/kawaii' 15 + import {flatten} from '#/alf' 15 16 16 17 const ratio = 57 / 64 17 18 ··· 23 24 export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) { 24 25 const {fill, ...rest} = props 25 26 const gradient = fill === 'sky' 26 - const styles = StyleSheet.flatten(props.style) 27 + const styles = flatten(props.style) 27 28 const _fill = gradient ? 'url(#sky)' : fill || styles?.color || colors.blue3 28 29 // @ts-ignore it's fiiiiine 29 30 const size = parseInt(rest.width || 32)