Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix layout shift when liking a post on Android (#10190)

authored by

Samuel Newman and committed by
GitHub
a169bd86 8bd6d9d1

+76 -245
+2 -3
src/App.native.tsx
··· 16 16 17 17 import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder' 18 18 import {QueryProvider} from '#/lib/react-query' 19 - import {s} from '#/lib/styles' 20 19 import {ThemeProvider} from '#/lib/ThemeContext' 21 20 import {Provider as TranslateOnDeviceProvider} from '#/lib/translation' 22 21 import I18nProvider from '#/locale/i18nProvider' ··· 58 57 import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' 59 58 import {TestCtrls} from '#/view/com/testing/TestCtrls' 60 59 import {Shell} from '#/view/shell' 61 - import {ThemeProvider as Alf} from '#/alf' 60 + import {atoms as a, ThemeProvider as Alf} from '#/alf' 62 61 import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 63 62 import {Provider as ContextMenuProvider} from '#/components/ContextMenu' 64 63 import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' ··· 175 174 <EmailVerificationProvider> 176 175 <HideBottomBarBorderProvider> 177 176 <GestureHandlerRootView 178 - style={s.h100pct}> 177 + style={a.h_full}> 179 178 <GlobalGestureEventsProvider> 180 179 <IntentDialogProvider> 181 180 <TranslateOnDeviceProvider>
+1
src/analytics/index.tsx
··· 110 110 }, 111 111 }, 112 112 }) 113 + Context.displayName = 'AnalyticsContext' 113 114 114 115 /** 115 116 * Ensures that deviceId is set and migrated from legacy storage. Handled on
+3 -4
src/components/PostControls/BookmarkButton.tsx
··· 136 136 <PostControlButton 137 137 testID="postBookmarkBtn" 138 138 big={big} 139 + active={isBookmarked} 140 + activeColor={t.palette.primary_500} 139 141 label={ 140 142 isBookmarked 141 143 ? _(msg`Remove from saved posts`) ··· 143 145 } 144 146 onPress={onHandlePress} 145 147 hitSlop={hitSlop}> 146 - <PostControlButtonIcon 147 - fill={isBookmarked ? t.palette.primary_500 : undefined} 148 - icon={isBookmarked ? BookmarkFilled : Bookmark} 149 - /> 148 + <PostControlButtonIcon icon={isBookmarked ? BookmarkFilled : Bookmark} /> 150 149 </PostControlButton> 151 150 ) 152 151 })
+3
src/components/PostControls/PostControlButton.tsx
··· 130 130 <Text 131 131 style={[ 132 132 color, 133 + a.user_select_none, 133 134 big ? a.text_md : a.text_sm, 134 135 active && a.font_semi_bold, 136 + // prevent layout shift on android 137 + {includeFontPadding: false, textAlignVertical: 'center'}, 135 138 style, 136 139 ]} 137 140 {...props}
+11 -4
src/components/PostControls/index.tsx
··· 24 24 ProgressGuideAction, 25 25 useProgressGuideControls, 26 26 } from '#/state/shell/progress-guide' 27 - import {atoms as a, useBreakpoints} from '#/alf' 27 + import {atoms as a, useBreakpoints, useTheme} from '#/alf' 28 28 import {Reply as Bubble} from '#/components/icons/Reply' 29 29 import {useFormatPostStatCount} from '#/components/PostControls/util' 30 30 import * as Skele from '#/components/Skeleton' ··· 74 74 forceGoogleTranslate?: boolean 75 75 }): React.ReactNode => { 76 76 const ax = useAnalytics() 77 + const t = useTheme() 77 78 const {t: l} = useLingui() 78 79 const {openComposer} = useOpenComposer() 79 80 const {feedDescriptor} = useFeedFeedbackContext() ··· 270 271 <PostControlButton 271 272 testID="likeBtn" 272 273 big={big} 274 + active={Boolean(post.viewer?.like)} 275 + activeColor={t.palette.pink} 273 276 onPress={() => requireAuth(() => onPressToggleLike())} 274 277 label={ 275 278 post.viewer?.like ··· 296 299 hasBeenToggled={hasLikeIconBeenToggled} 297 300 /> 298 301 <CountWheel 299 - likeCount={post.likeCount ?? 0} 300 - big={big} 301 - isLiked={Boolean(post.viewer?.like)} 302 + count={post.likeCount ?? 0} 303 + isToggled={Boolean(post.viewer?.like)} 302 304 hasBeenToggled={hasLikeIconBeenToggled} 305 + renderCount={({count}) => ( 306 + <PostControlButtonText> 307 + {formatPostStatCount(count)} 308 + </PostControlButtonText> 309 + )} 303 310 /> 304 311 </PostControlButton> 305 312 </View>
+20 -46
src/lib/custom-animations/CountWheel.tsx
··· 8 8 } from 'react-native-reanimated' 9 9 10 10 import {decideShouldRoll} from '#/lib/custom-animations/util' 11 - import {s} from '#/lib/styles' 12 - import {Text} from '#/view/com/util/text/Text' 13 - import {atoms as a, useTheme} from '#/alf' 14 - import {useFormatPostStatCount} from '#/components/PostControls/util' 11 + import {atoms as a} from '#/alf' 15 12 16 13 const animationConfig = { 17 14 duration: 400, ··· 87 84 } 88 85 89 86 export function CountWheel({ 90 - likeCount, 91 - big, 92 - isLiked, 87 + count, 88 + isToggled, 93 89 hasBeenToggled, 90 + renderCount, 94 91 }: { 95 - likeCount: number 96 - big?: boolean 97 - isLiked: boolean 92 + count: number 93 + isToggled: boolean 98 94 hasBeenToggled: boolean 95 + renderCount: (props: {count: number}) => React.ReactNode 99 96 }) { 100 - const t = useTheme() 101 97 const shouldAnimate = !useReducedMotion() && hasBeenToggled 102 - const shouldRoll = decideShouldRoll(isLiked, likeCount) 98 + const shouldRoll = decideShouldRoll(isToggled, count) 103 99 104 100 // Incrementing the key will cause the `Animated.View` to re-render, with the newly selected entering/exiting 105 101 // animation 106 102 // The initial entering/exiting animations will get skipped, since these will happen on screen mounts and would 107 103 // be unnecessary 108 104 const [key, setKey] = useState(0) 109 - const [prevCount, setPrevCount] = useState(likeCount) 110 - const prevIsLiked = useRef(isLiked) 111 - const formatPostStatCount = useFormatPostStatCount() 112 - const formattedCount = formatPostStatCount(likeCount) 113 - const formattedPrevCount = formatPostStatCount(prevCount) 105 + const [prevCount, setPrevCount] = useState(count) 106 + const prevIsToggled = useRef(isToggled) 114 107 115 108 useEffect(() => { 116 - if (isLiked === prevIsLiked.current) { 109 + if (isToggled === prevIsToggled.current) { 117 110 return 118 111 } 119 112 120 - const newPrevCount = isLiked ? likeCount - 1 : likeCount + 1 113 + const newPrevCount = isToggled ? count - 1 : count + 1 121 114 setKey(prev => prev + 1) 122 115 setPrevCount(newPrevCount) 123 - prevIsLiked.current = isLiked 124 - }, [isLiked, likeCount]) 116 + prevIsToggled.current = isToggled 117 + }, [isToggled, count]) 125 118 126 119 const enteringAnimation = 127 120 shouldAnimate && shouldRoll 128 - ? isLiked 121 + ? isToggled 129 122 ? EnteringUp 130 123 : EnteringDown 131 124 : undefined 132 125 const exitingAnimation = 133 126 shouldAnimate && shouldRoll 134 - ? isLiked 127 + ? isToggled 135 128 ? ExitingUp 136 129 : ExitingDown 137 130 : undefined 138 131 139 132 return ( 140 133 <LayoutAnimationConfig skipEntering skipExiting> 141 - {likeCount > 0 ? ( 134 + {count > 0 ? ( 142 135 <View style={[a.justify_center]}> 143 136 <Animated.View entering={enteringAnimation} key={key}> 144 - <Text 145 - testID="likeCount" 146 - style={[ 147 - big ? a.text_md : a.text_sm, 148 - a.user_select_none, 149 - isLiked 150 - ? [a.font_semi_bold, s.likeColor] 151 - : {color: t.palette.contrast_500}, 152 - ]}> 153 - {formattedCount} 154 - </Text> 137 + {renderCount({count})} 155 138 </Animated.View> 156 - {shouldAnimate && (likeCount > 1 || !isLiked) ? ( 139 + {shouldAnimate && (count > 1 || !isToggled) ? ( 157 140 <Animated.View 158 141 entering={exitingAnimation} 159 142 // Add 2 to the key so there are never duplicates 160 143 key={key + 2} 161 144 style={[a.absolute, {width: 50, opacity: 0}]} 162 145 aria-disabled={true}> 163 - <Text 164 - style={[ 165 - big ? a.text_md : a.text_sm, 166 - a.user_select_none, 167 - isLiked 168 - ? [a.font_semi_bold, s.likeColor] 169 - : {color: t.palette.contrast_500}, 170 - ]}> 171 - {formattedPrevCount} 172 - </Text> 146 + {renderCount({count: prevCount})} 173 147 </Animated.View> 174 148 ) : null} 175 149 </View>
+19 -46
src/lib/custom-animations/CountWheel.web.tsx
··· 3 3 import {useReducedMotion} from 'react-native-reanimated' 4 4 5 5 import {decideShouldRoll} from '#/lib/custom-animations/util' 6 - import {s} from '#/lib/styles' 7 - import {Text} from '#/view/com/util/text/Text' 8 - import {atoms as a, useTheme} from '#/alf' 9 - import {useFormatPostStatCount} from '#/components/PostControls/util' 10 6 11 7 const animationConfig = { 12 8 duration: 400, ··· 35 31 ] 36 32 37 33 export function CountWheel({ 38 - likeCount, 39 - big, 40 - isLiked, 34 + count, 35 + isToggled, 41 36 hasBeenToggled, 37 + renderCount, 42 38 }: { 43 - likeCount: number 44 - big?: boolean 45 - isLiked: boolean 39 + count: number 40 + isToggled: boolean 46 41 hasBeenToggled: boolean 42 + renderCount: (props: {count: number}) => React.ReactNode 47 43 }) { 48 - const t = useTheme() 49 44 const shouldAnimate = !useReducedMotion() && hasBeenToggled 50 - const shouldRoll = decideShouldRoll(isLiked, likeCount) 45 + const shouldRoll = decideShouldRoll(isToggled, count) 51 46 52 47 const countView = useRef<HTMLDivElement>(null) 53 48 const prevCountView = useRef<HTMLDivElement>(null) 54 49 55 - const [prevCount, setPrevCount] = useState(likeCount) 56 - const prevIsLiked = useRef(isLiked) 57 - const formatPostStatCount = useFormatPostStatCount() 58 - const formattedCount = formatPostStatCount(likeCount) 59 - const formattedPrevCount = formatPostStatCount(prevCount) 50 + const [prevCount, setPrevCount] = useState(count) 51 + const prevIsToggled = useRef(isToggled) 60 52 61 53 useEffect(() => { 62 - if (isLiked === prevIsLiked.current) { 54 + if (isToggled === prevIsToggled.current) { 63 55 return 64 56 } 65 57 66 - const newPrevCount = isLiked ? likeCount - 1 : likeCount + 1 58 + const newPrevCount = isToggled ? count - 1 : count + 1 67 59 if (shouldAnimate && shouldRoll) { 68 60 countView.current?.animate?.( 69 - isLiked ? enteringUpKeyframe : enteringDownKeyframe, 61 + isToggled ? enteringUpKeyframe : enteringDownKeyframe, 70 62 animationConfig, 71 63 ) 72 64 prevCountView.current?.animate?.( 73 - isLiked ? exitingUpKeyframe : exitingDownKeyframe, 65 + isToggled ? exitingUpKeyframe : exitingDownKeyframe, 74 66 animationConfig, 75 67 ) 76 68 setPrevCount(newPrevCount) 77 69 } 78 - prevIsLiked.current = isLiked 79 - }, [isLiked, likeCount, shouldAnimate, shouldRoll]) 70 + prevIsToggled.current = isToggled 71 + }, [isToggled, count, shouldAnimate, shouldRoll]) 80 72 81 - if (likeCount < 1) { 73 + if (count < 1) { 82 74 return null 83 75 } 84 76 ··· 87 79 <View 88 80 // @ts-expect-error is div 89 81 ref={countView}> 90 - <Text 91 - testID="likeCount" 92 - style={[ 93 - big ? a.text_md : a.text_sm, 94 - a.user_select_none, 95 - isLiked 96 - ? [a.font_semi_bold, s.likeColor] 97 - : {color: t.palette.contrast_500}, 98 - ]}> 99 - {formattedCount} 100 - </Text> 82 + {renderCount({count})} 101 83 </View> 102 - {shouldAnimate && (likeCount > 1 || !isLiked) ? ( 84 + {shouldAnimate && (count > 1 || !isToggled) ? ( 103 85 <View 104 86 style={{position: 'absolute', opacity: 0}} 105 87 aria-disabled={true} 106 88 // @ts-expect-error is div 107 89 ref={prevCountView}> 108 - <Text 109 - style={[ 110 - big ? a.text_md : a.text_sm, 111 - a.user_select_none, 112 - isLiked 113 - ? [a.font_semi_bold, s.likeColor] 114 - : {color: t.palette.contrast_500}, 115 - ]}> 116 - {formattedPrevCount} 117 - </Text> 90 + {renderCount({count: prevCount})} 118 91 </View> 119 92 ) : null} 120 93 </View>
+2 -3
src/lib/custom-animations/LikeIcon.tsx
··· 5 5 useReducedMotion, 6 6 } from 'react-native-reanimated' 7 7 8 - import {s} from '#/lib/styles' 9 8 import {useTheme} from '#/alf' 10 9 import { 11 10 Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, ··· 86 85 {isLiked ? ( 87 86 <Animated.View 88 87 entering={shouldAnimate ? keyframe.duration(300) : undefined}> 89 - <HeartIconFilled style={s.likeColor} width={size} /> 88 + <HeartIconFilled style={{color: t.palette.pink}} width={size} /> 90 89 </Animated.View> 91 90 ) : ( 92 91 <HeartIconOutline ··· 100 99 entering={circle1Keyframe.duration(300)} 101 100 style={{ 102 101 position: 'absolute', 103 - backgroundColor: s.likeColor.color, 102 + backgroundColor: t.palette.pink, 104 103 top: 0, 105 104 left: 0, 106 105 width: size,
+2 -3
src/lib/custom-animations/LikeIcon.web.tsx
··· 2 2 import {View} from 'react-native' 3 3 import {useReducedMotion} from 'react-native-reanimated' 4 4 5 - import {s} from '#/lib/styles' 6 5 import {useTheme} from '#/alf' 7 6 import { 8 7 Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, ··· 74 73 {isLiked ? ( 75 74 // @ts-expect-error is div 76 75 <View ref={likeIconRef}> 77 - <HeartIconFilled style={s.likeColor} width={size} /> 76 + <HeartIconFilled style={{color: t.palette.pink}} width={size} /> 78 77 </View> 79 78 ) : ( 80 79 <HeartIconOutline ··· 87 86 ref={circle1Ref} 88 87 style={{ 89 88 position: 'absolute', 90 - backgroundColor: s.likeColor.color, 89 + backgroundColor: t.palette.pink, 91 90 top: 0, 92 91 left: 0, 93 92 width: size,
+1 -124
src/lib/styles.ts
··· 1 - import { 2 - Dimensions, 3 - type StyleProp, 4 - StyleSheet, 5 - type TextStyle, 6 - } from 'react-native' 1 + import {type StyleProp, StyleSheet, type TextStyle} from 'react-native' 7 2 8 3 import {IS_WEB} from '#/env' 9 4 import {type Theme, type TypographyVariant} from './ThemeContext' ··· 61 56 green5: '#082b03', 62 57 63 58 unreadNotifBg: '#ebf6ff', 64 - brandBlue: '#0066FF', 65 - like: '#ec4899', 66 - } 67 - 68 - export const gradients = { 69 - blueLight: {start: '#5A71FA', end: colors.blue3}, // buttons 70 - blue: {start: '#5E55FB', end: colors.blue3}, // fab 71 - blueDark: {start: '#5F45E0', end: colors.blue3}, // avis, banner 72 59 } 73 60 74 61 /** ··· 78 65 // helpers 79 66 footerSpacer: {height: 100}, 80 67 contentContainer: {paddingBottom: 200}, 81 - contentContainerExtra: {paddingBottom: 300}, 82 - border0: {borderWidth: 0}, 83 - border1: {borderWidth: 1}, 84 - borderTop1: {borderTopWidth: 1}, 85 - borderRight1: {borderRightWidth: 1}, 86 - borderBottom1: {borderBottomWidth: 1}, 87 - borderLeft1: {borderLeftWidth: 1}, 88 - hidden: {display: 'none'}, 89 - dimmed: {opacity: 0.5}, 90 - 91 - // font weights 92 - fw600: {fontWeight: '600'}, 93 - bold: {fontWeight: '600'}, 94 - fw500: {fontWeight: '600'}, 95 - semiBold: {fontWeight: '600'}, 96 - fw400: {fontWeight: '400'}, 97 - normal: {fontWeight: '400'}, 98 - fw300: {fontWeight: '400'}, 99 - light: {fontWeight: '400'}, 100 - 101 - // text decoration 102 - underline: {textDecorationLine: 'underline'}, 103 - 104 - // font variants 105 - tabularNum: {fontVariant: ['tabular-nums']}, 106 - 107 - // font sizes 108 - f9: {fontSize: 9}, 109 - f10: {fontSize: 10}, 110 - f11: {fontSize: 11}, 111 - f12: {fontSize: 12}, 112 - f13: {fontSize: 13}, 113 - f14: {fontSize: 14}, 114 - f15: {fontSize: 15}, 115 - f16: {fontSize: 16}, 116 - f17: {fontSize: 17}, 117 - f18: {fontSize: 18}, 118 - 119 - // line heights 120 - ['lh13-1']: {lineHeight: 13}, 121 - ['lh13-1.3']: {lineHeight: 16.9}, // 1.3 of 13px 122 - ['lh14-1']: {lineHeight: 14}, 123 - ['lh14-1.3']: {lineHeight: 18.2}, // 1.3 of 14px 124 - ['lh15-1']: {lineHeight: 15}, 125 - ['lh15-1.3']: {lineHeight: 19.5}, // 1.3 of 15px 126 - ['lh16-1']: {lineHeight: 16}, 127 - ['lh16-1.3']: {lineHeight: 20.8}, // 1.3 of 16px 128 - ['lh17-1']: {lineHeight: 17}, 129 - ['lh17-1.3']: {lineHeight: 22.1}, // 1.3 of 17px 130 - ['lh18-1']: {lineHeight: 18}, 131 - ['lh18-1.3']: {lineHeight: 23.4}, // 1.3 of 18px 132 68 133 69 // margins 134 70 mr2: {marginRight: 2}, ··· 171 107 pb20: {paddingBottom: 20}, 172 108 px5: {paddingHorizontal: 5}, 173 109 174 - // flex 175 - flexRow: {flexDirection: 'row'}, 176 - flexCol: {flexDirection: 'column'}, 177 - flex1: {flex: 1}, 178 - flexGrow1: {flexGrow: 1}, 179 - alignCenter: {alignItems: 'center'}, 180 - alignBaseline: {alignItems: 'baseline'}, 181 - justifyCenter: {justifyContent: 'center'}, 182 - 183 - // position 184 - absolute: {position: 'absolute'}, 185 - 186 110 // dimensions 187 - w100pct: {width: '100%'}, 188 - h100pct: {height: '100%'}, 189 111 hContentRegion: IS_WEB ? {minHeight: '100%'} : {height: '100%'}, 190 - window: { 191 - width: Dimensions.get('window').width, 192 - height: Dimensions.get('window').height, 193 - }, 194 112 195 113 // text align 196 - textLeft: {textAlign: 'left'}, 197 114 textCenter: {textAlign: 'center'}, 198 - textRight: {textAlign: 'right'}, 199 115 200 116 // colors 201 117 white: {color: colors.white}, 202 118 black: {color: colors.black}, 203 - 204 - gray1: {color: colors.gray1}, 205 - gray2: {color: colors.gray2}, 206 - gray3: {color: colors.gray3}, 207 - gray4: {color: colors.gray4}, 208 - gray5: {color: colors.gray5}, 209 - 210 - blue1: {color: colors.blue1}, 211 - blue2: {color: colors.blue2}, 212 - blue3: {color: colors.blue3}, 213 - blue4: {color: colors.blue4}, 214 - blue5: {color: colors.blue5}, 215 - 216 - red1: {color: colors.red1}, 217 - red2: {color: colors.red2}, 218 - red3: {color: colors.red3}, 219 - red4: {color: colors.red4}, 220 - red5: {color: colors.red5}, 221 - 222 - pink1: {color: colors.pink1}, 223 - pink2: {color: colors.pink2}, 224 - pink3: {color: colors.pink3}, 225 - pink4: {color: colors.pink4}, 226 - pink5: {color: colors.pink5}, 227 - 228 - purple1: {color: colors.purple1}, 229 - purple2: {color: colors.purple2}, 230 - purple3: {color: colors.purple3}, 231 - purple4: {color: colors.purple4}, 232 - purple5: {color: colors.purple5}, 233 - 234 - green1: {color: colors.green1}, 235 - green2: {color: colors.green2}, 236 - green3: {color: colors.green3}, 237 - green4: {color: colors.green4}, 238 - green5: {color: colors.green5}, 239 - 240 - brandBlue: {color: colors.brandBlue}, 241 - likeColor: {color: colors.like}, 242 119 }) 243 120 244 121 export function lh(
+1 -1
src/view/com/lightbox/ImageViewing/index.tsx
··· 582 582 {altText ? ( 583 583 <View accessibilityRole="button" style={styles.footerText}> 584 584 <Text 585 - style={[s.gray3]} 585 + style={{color: colors.gray3}} 586 586 numberOfLines={isAltExpanded ? undefined : 3} 587 587 selectable 588 588 onPress={() => {
+1 -1
src/view/com/modals/UserAddRemoveLists.tsx
··· 202 202 <View style={styles.listItemContent}> 203 203 <Text 204 204 type="lg" 205 - style={[s.bold, pal.text]} 205 + style={[{fontWeight: '600'}, pal.text]} 206 206 numberOfLines={1} 207 207 lineHeight={1.2}> 208 208 {sanitizeDisplayName(list.name)}
+1 -1
src/view/com/notifications/NotificationFeedItem.tsx
··· 272 272 <HeartIconFilled 273 273 size="xl" 274 274 style={[ 275 - s.likeColor, 275 + {color: t.palette.pink}, 276 276 // {position: 'relative', top: -4} 277 277 ]} 278 278 />
+2 -2
src/view/com/util/LoadingPlaceholder.tsx
··· 60 60 }, 61 61 ]} 62 62 /> 63 - <View style={[s.flex1]}> 63 + <View style={[a.flex_1]}> 64 64 <LoadingPlaceholder width={100} height={6} style={{marginBottom: 10}} /> 65 65 <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> 66 66 <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> ··· 238 238 height={36} 239 239 style={[styles.avatar, {borderRadius: 8}]} 240 240 /> 241 - <View style={[s.flex1]}> 241 + <View style={[a.flex_1]}> 242 242 <LoadingPlaceholder width={100} height={8} style={[s.mt5, s.mb10]} /> 243 243 <LoadingPlaceholder width={120} height={8} /> 244 244 </View>
+7 -7
src/view/screens/Debug.tsx
··· 174 174 } 175 175 return ( 176 176 <View style={s.p10}> 177 - <View style={s.flexRow}> 177 + <View style={{flexDirection: 'row'}}> 178 178 <Button onPress={triggerPush} label="Trigger Push" /> 179 179 <Button onPress={triggerToast} label="Trigger Toast" /> 180 180 <Button onPress={triggerToast2} label="Trigger Toast 2" /> ··· 187 187 const defaultPal = usePalette('default') 188 188 const pal = usePalette(palette) 189 189 return ( 190 - <View style={[pal.view, pal.border, s.p10, s.mb5, s.border1]}> 190 + <View style={[pal.view, pal.border, s.p10, s.mb5, {borderWidth: 1}]}> 191 191 <Text style={[pal.text]}>{palette} colors</Text> 192 192 <Text style={[pal.textLight]}>Light text</Text> 193 193 <Text style={[pal.link]}>Link text</Text> ··· 343 343 const buttonStyles = {marginRight: 5} 344 344 return ( 345 345 <View style={[defaultPal.view]}> 346 - <View style={[s.flexRow, s.mb5]}> 346 + <View style={[{flexDirection: 'row'}, s.mb5]}> 347 347 <Button type="primary" label="Primary solid" style={buttonStyles} /> 348 348 <Button type="secondary" label="Secondary solid" style={buttonStyles} /> 349 349 </View> 350 - <View style={[s.flexRow, s.mb5]}> 350 + <View style={[{flexDirection: 'row'}, s.mb5]}> 351 351 <Button type="default" label="Default solid" style={buttonStyles} /> 352 352 <Button type="inverted" label="Inverted solid" style={buttonStyles} /> 353 353 </View> 354 - <View style={s.flexRow}> 354 + <View style={{flexDirection: 'row'}}> 355 355 <Button 356 356 type="primary-outline" 357 357 label="Primary outline" ··· 363 363 style={buttonStyles} 364 364 /> 365 365 </View> 366 - <View style={s.flexRow}> 366 + <View style={{flexDirection: 'row'}}> 367 367 <Button 368 368 type="primary-light" 369 369 label="Primary light" ··· 375 375 style={buttonStyles} 376 376 /> 377 377 </View> 378 - <View style={s.flexRow}> 378 + <View style={{flexDirection: 'row'}}> 379 379 <Button 380 380 type="default-light" 381 381 label="Default light"