An ATproto social media client -- with an independent Appview.

Increase rounding for all embeds (#5421)

* tweak image styles

* fix reply image preview and covert to atoms

* increase rounding on media inset border

* decrease gap on desktop

* fix inset styles

* increase rounding on embeds to `md`

* Couple edge cases

---------

Co-authored-by: Eric Bailey <git@esb.lol>

authored by samuel.fm

Eric Bailey and committed by
GitHub
79a2f8d5 c85a271e

+223 -160
+1 -1
src/components/MediaInsetBorder.tsx
··· 24 24 return ( 25 25 <Fill 26 26 style={[ 27 - a.rounded_sm, 27 + a.rounded_md, 28 28 a.border, 29 29 opaque 30 30 ? [t.atoms.border_contrast_low]
+67 -90
src/view/com/composer/ComposerReplyTo.tsx
··· 16 16 import {QuoteEmbed} from 'view/com/util/post-embeds/QuoteEmbed' 17 17 import {Text} from 'view/com/util/text/Text' 18 18 import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' 19 - import {useTheme} from '#/alf' 19 + import {atoms as a, useTheme} from '#/alf' 20 20 21 21 export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { 22 22 const t = useTheme() ··· 122 122 showFull: boolean 123 123 }) { 124 124 return ( 125 - <View 126 - style={{ 127 - width: 65, 128 - flexDirection: 'column', 129 - alignItems: 'center', 130 - }}> 131 - <View style={styles.imagesContainer}> 132 - {(images.length === 1 && ( 133 - <Image 134 - source={{uri: images[0].thumb}} 135 - style={styles.singleImage} 136 - cachePolicy="memory-disk" 137 - accessibilityIgnoresInvertColors 138 - /> 125 + <View style={[styles.imagesContainer, a.mx_xs]}> 126 + {(images.length === 1 && ( 127 + <Image 128 + source={{uri: images[0].thumb}} 129 + style={[a.flex_1]} 130 + cachePolicy="memory-disk" 131 + accessibilityIgnoresInvertColors 132 + /> 133 + )) || 134 + (images.length === 2 && ( 135 + <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 136 + <Image 137 + source={{uri: images[0].thumb}} 138 + style={[a.flex_1]} 139 + cachePolicy="memory-disk" 140 + accessibilityIgnoresInvertColors 141 + /> 142 + <Image 143 + source={{uri: images[1].thumb}} 144 + style={[a.flex_1]} 145 + cachePolicy="memory-disk" 146 + accessibilityIgnoresInvertColors 147 + /> 148 + </View> 149 + )) || 150 + (images.length === 3 && ( 151 + <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 152 + <Image 153 + source={{uri: images[0].thumb}} 154 + style={[a.flex_1]} 155 + cachePolicy="memory-disk" 156 + accessibilityIgnoresInvertColors 157 + /> 158 + <View style={[a.flex_1, a.gap_2xs]}> 159 + <Image 160 + source={{uri: images[1].thumb}} 161 + style={[a.flex_1]} 162 + cachePolicy="memory-disk" 163 + accessibilityIgnoresInvertColors 164 + /> 165 + <Image 166 + source={{uri: images[2].thumb}} 167 + style={[a.flex_1]} 168 + cachePolicy="memory-disk" 169 + accessibilityIgnoresInvertColors 170 + /> 171 + </View> 172 + </View> 139 173 )) || 140 - (images.length === 2 && ( 141 - <View style={[styles.imagesInner, styles.imagesRow]}> 174 + (images.length === 4 && ( 175 + <View style={[a.flex_1, a.gap_2xs]}> 176 + <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 142 177 <Image 143 178 source={{uri: images[0].thumb}} 144 - style={styles.doubleImageTall} 179 + style={[a.flex_1]} 145 180 cachePolicy="memory-disk" 146 181 accessibilityIgnoresInvertColors 147 182 /> 148 183 <Image 149 184 source={{uri: images[1].thumb}} 150 - style={styles.doubleImageTall} 185 + style={[a.flex_1]} 151 186 cachePolicy="memory-disk" 152 187 accessibilityIgnoresInvertColors 153 188 /> 154 189 </View> 155 - )) || 156 - (images.length === 3 && ( 157 - <View style={[styles.imagesInner, styles.imagesRow]}> 190 + <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 191 + <Image 192 + source={{uri: images[2].thumb}} 193 + style={[a.flex_1]} 194 + cachePolicy="memory-disk" 195 + accessibilityIgnoresInvertColors 196 + /> 158 197 <Image 159 - source={{uri: images[0].thumb}} 160 - style={styles.doubleImageTall} 198 + source={{uri: images[3].thumb}} 199 + style={[a.flex_1]} 161 200 cachePolicy="memory-disk" 162 201 accessibilityIgnoresInvertColors 163 202 /> 164 - <View style={styles.imagesInner}> 165 - <Image 166 - source={{uri: images[1].thumb}} 167 - style={styles.doubleImage} 168 - cachePolicy="memory-disk" 169 - accessibilityIgnoresInvertColors 170 - /> 171 - <Image 172 - source={{uri: images[2].thumb}} 173 - style={styles.doubleImage} 174 - cachePolicy="memory-disk" 175 - accessibilityIgnoresInvertColors 176 - /> 177 - </View> 178 203 </View> 179 - )) || 180 - (images.length === 4 && ( 181 - <View style={styles.imagesInner}> 182 - <View style={[styles.imagesInner, styles.imagesRow]}> 183 - <Image 184 - source={{uri: images[0].thumb}} 185 - style={styles.doubleImage} 186 - cachePolicy="memory-disk" 187 - accessibilityIgnoresInvertColors 188 - /> 189 - <Image 190 - source={{uri: images[1].thumb}} 191 - style={styles.doubleImage} 192 - cachePolicy="memory-disk" 193 - accessibilityIgnoresInvertColors 194 - /> 195 - </View> 196 - <View style={[styles.imagesInner, styles.imagesRow]}> 197 - <Image 198 - source={{uri: images[2].thumb}} 199 - style={styles.doubleImage} 200 - cachePolicy="memory-disk" 201 - accessibilityIgnoresInvertColors 202 - /> 203 - <Image 204 - source={{uri: images[3].thumb}} 205 - style={styles.doubleImage} 206 - cachePolicy="memory-disk" 207 - accessibilityIgnoresInvertColors 208 - /> 209 - </View> 210 - </View> 211 - ))} 212 - </View> 204 + </View> 205 + ))} 213 206 </View> 214 207 ) 215 208 } ··· 240 233 borderRadius: 6, 241 234 overflow: 'hidden', 242 235 marginTop: 2, 243 - }, 244 - imagesInner: { 245 - gap: 2, 246 - }, 247 - imagesRow: { 248 - flexDirection: 'row', 249 - }, 250 - singleImage: { 251 - width: 65, 252 - height: 65, 253 - }, 254 - doubleImageTall: { 255 - width: 32.5, 256 - height: 65, 257 - }, 258 - doubleImage: { 259 - width: 32.5, 260 - height: 32.5, 236 + height: 64, 237 + width: 64, 261 238 }, 262 239 })
+2 -2
src/view/com/util/images/AutoSizedImage.tsx
··· 88 88 <View 89 89 style={[ 90 90 a.h_full, 91 - a.rounded_sm, 91 + a.rounded_md, 92 92 a.overflow_hidden, 93 93 t.atoms.bg_contrast_25, 94 94 fullBleed ? a.w_full : {aspectRatio}, ··· 219 219 accessibilityHint={_(msg`Tap to view full image`)} 220 220 style={[ 221 221 a.w_full, 222 - a.rounded_sm, 222 + a.rounded_md, 223 223 a.overflow_hidden, 224 224 t.atoms.bg_contrast_25, 225 225 {aspectRatio: max},
+10 -9
src/view/com/util/images/Gallery.tsx
··· 1 - import React, {ComponentProps, FC} from 'react' 2 - import {Pressable, View} from 'react-native' 3 - import {Image} from 'expo-image' 1 + import React from 'react' 2 + import {Pressable, StyleProp, View, ViewStyle} from 'react-native' 3 + import {Image, ImageStyle} from 'expo-image' 4 4 import {AppBskyEmbedImages} from '@atproto/api' 5 5 import {msg} from '@lingui/macro' 6 6 import {useLingui} from '@lingui/react' ··· 13 13 14 14 type EventFunction = (index: number) => void 15 15 16 - interface GalleryItemProps { 16 + interface Props { 17 17 images: AppBskyEmbedImages.ViewImage[] 18 18 index: number 19 19 onPress?: EventFunction 20 20 onLongPress?: EventFunction 21 21 onPressIn?: EventFunction 22 - imageStyle?: ComponentProps<typeof Image>['style'] 22 + imageStyle?: StyleProp<ImageStyle> 23 23 viewContext?: PostEmbedViewContext 24 + insetBorderStyle?: StyleProp<ViewStyle> 24 25 } 25 26 26 - export const GalleryItem: FC<GalleryItemProps> = ({ 27 + export function GalleryItem({ 27 28 images, 28 29 index, 29 30 imageStyle, ··· 31 32 onPressIn, 32 33 onLongPress, 33 34 viewContext, 34 - }) => { 35 + insetBorderStyle, 36 + }: Props) { 35 37 const t = useTheme() 36 38 const {_} = useLingui() 37 39 const largeAltBadge = useLargeAltBadgeEnabled() ··· 47 49 onLongPress={onLongPress ? () => onLongPress(index) : undefined} 48 50 style={[ 49 51 a.flex_1, 50 - a.rounded_sm, 51 52 a.overflow_hidden, 52 53 t.atoms.bg_contrast_25, 53 54 imageStyle, ··· 63 64 accessibilityHint="" 64 65 accessibilityIgnoresInvertColors 65 66 /> 66 - <MediaInsetBorder /> 67 + <MediaInsetBorder style={insetBorderStyle} /> 67 68 </Pressable> 68 69 {hasAlt && !hideBadges ? ( 69 70 <View
+109 -21
src/view/com/util/images/ImageLayoutGrid.tsx
··· 1 1 import React from 'react' 2 - import {StyleProp, View, ViewStyle} from 'react-native' 2 + import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' 3 3 import {AppBskyEmbedImages} from '@atproto/api' 4 4 5 5 import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' ··· 22 22 ? gtMobile 23 23 ? a.gap_xs 24 24 : a.gap_2xs 25 - : gtMobile 26 - ? a.gap_sm 27 25 : a.gap_xs 28 26 const count = props.images.length 29 - const aspectRatio = count === 2 ? 2 : count === 3 ? 1.5 : 1 27 + let aspectRatio 28 + switch (count) { 29 + case 2: 30 + aspectRatio = 2 31 + break 32 + case 3: 33 + aspectRatio = 2 34 + break 35 + case 4: 36 + aspectRatio = undefined 37 + break 38 + } 30 39 return ( 31 40 <View style={style}> 32 - <View style={[gap, {aspectRatio}]}> 41 + <View style={[gap, a.rounded_md, a.overflow_hidden, {aspectRatio}]}> 33 42 <ImageLayoutGridInner {...props} gap={gap} /> 34 43 </View> 35 44 </View> ··· 54 63 return ( 55 64 <View style={[a.flex_1, a.flex_row, gap]}> 56 65 <View style={[a.flex_1, {aspectRatio: 1}]}> 57 - <GalleryItem {...props} index={0} /> 66 + <GalleryItem 67 + {...props} 68 + index={0} 69 + insetBorderStyle={noCorners(['topRight', 'bottomRight'])} 70 + /> 58 71 </View> 59 72 <View style={[a.flex_1, {aspectRatio: 1}]}> 60 - <GalleryItem {...props} index={1} /> 73 + <GalleryItem 74 + {...props} 75 + index={1} 76 + insetBorderStyle={noCorners(['topLeft', 'bottomLeft'])} 77 + /> 61 78 </View> 62 79 </View> 63 80 ) ··· 65 82 case 3: 66 83 return ( 67 84 <View style={[a.flex_1, a.flex_row, gap]}> 68 - <View style={{flex: 2}}> 69 - <GalleryItem {...props} index={0} /> 85 + <View style={[a.flex_1]}> 86 + <GalleryItem 87 + {...props} 88 + index={0} 89 + insetBorderStyle={noCorners(['topRight', 'bottomRight'])} 90 + /> 70 91 </View> 71 92 <View style={[a.flex_1, gap]}> 72 - <View style={[a.flex_1, {aspectRatio: 1}]}> 73 - <GalleryItem {...props} index={1} /> 93 + <View style={[a.flex_1]}> 94 + <GalleryItem 95 + {...props} 96 + index={1} 97 + insetBorderStyle={noCorners([ 98 + 'topLeft', 99 + 'bottomLeft', 100 + 'bottomRight', 101 + ])} 102 + /> 74 103 </View> 75 - <View style={[a.flex_1, {aspectRatio: 1}]}> 76 - <GalleryItem {...props} index={2} /> 104 + <View style={[a.flex_1]}> 105 + <GalleryItem 106 + {...props} 107 + index={2} 108 + insetBorderStyle={noCorners([ 109 + 'topLeft', 110 + 'bottomLeft', 111 + 'topRight', 112 + ])} 113 + /> 77 114 </View> 78 115 </View> 79 116 </View> ··· 83 120 return ( 84 121 <> 85 122 <View style={[a.flex_row, gap]}> 86 - <View style={[a.flex_1, {aspectRatio: 1}]}> 87 - <GalleryItem {...props} index={0} /> 123 + <View style={[a.flex_1, {aspectRatio: 1.5}]}> 124 + <GalleryItem 125 + {...props} 126 + index={0} 127 + insetBorderStyle={noCorners([ 128 + 'bottomLeft', 129 + 'topRight', 130 + 'bottomRight', 131 + ])} 132 + /> 88 133 </View> 89 - <View style={[a.flex_1, {aspectRatio: 1}]}> 90 - <GalleryItem {...props} index={1} /> 134 + <View style={[a.flex_1, {aspectRatio: 1.5}]}> 135 + <GalleryItem 136 + {...props} 137 + index={1} 138 + insetBorderStyle={noCorners([ 139 + 'topLeft', 140 + 'bottomLeft', 141 + 'bottomRight', 142 + ])} 143 + /> 91 144 </View> 92 145 </View> 93 146 <View style={[a.flex_row, gap]}> 94 - <View style={[a.flex_1, {aspectRatio: 1}]}> 95 - <GalleryItem {...props} index={2} /> 147 + <View style={[a.flex_1, {aspectRatio: 1.5}]}> 148 + <GalleryItem 149 + {...props} 150 + index={2} 151 + insetBorderStyle={noCorners([ 152 + 'topLeft', 153 + 'topRight', 154 + 'bottomRight', 155 + ])} 156 + /> 96 157 </View> 97 - <View style={[a.flex_1, {aspectRatio: 1}]}> 98 - <GalleryItem {...props} index={3} /> 158 + <View style={[a.flex_1, {aspectRatio: 1.5}]}> 159 + <GalleryItem 160 + {...props} 161 + index={3} 162 + insetBorderStyle={noCorners([ 163 + 'topLeft', 164 + 'bottomLeft', 165 + 'topRight', 166 + ])} 167 + /> 99 168 </View> 100 169 </View> 101 170 </> ··· 105 174 return null 106 175 } 107 176 } 177 + 178 + function noCorners( 179 + corners: ('topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight')[], 180 + ) { 181 + const styles: StyleProp<ViewStyle>[] = [] 182 + if (corners.includes('topLeft')) { 183 + styles.push({borderTopLeftRadius: 0}) 184 + } 185 + if (corners.includes('topRight')) { 186 + styles.push({borderTopRightRadius: 0}) 187 + } 188 + if (corners.includes('bottomLeft')) { 189 + styles.push({borderBottomLeftRadius: 0}) 190 + } 191 + if (corners.includes('bottomRight')) { 192 + styles.push({borderBottomRightRadius: 0}) 193 + } 194 + return StyleSheet.flatten(styles) 195 + }
+1 -1
src/view/com/util/post-embeds/ExternalGifEmbed.tsx
··· 117 117 style={[ 118 118 {height: imageDims.height}, 119 119 styles.gifContainer, 120 - a.rounded_sm, 120 + a.rounded_md, 121 121 a.overflow_hidden, 122 122 { 123 123 borderBottomLeftRadius: 0,
+6 -6
src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
··· 59 59 } 60 60 61 61 return ( 62 - <View style={[a.flex_col, a.w_full]}> 62 + <View style={[a.flex_col, a.rounded_md, a.w_full]}> 63 63 <LinkWrapper link={link} onOpen={onOpen} style={style}> 64 64 {imageUri && !embedPlayerParams ? ( 65 65 <View> 66 66 <Image 67 67 style={{ 68 68 aspectRatio: 1.91, 69 - borderTopRightRadius: 8, 70 - borderTopLeftRadius: 8, 69 + borderTopRightRadius: a.rounded_md.borderRadius, 70 + borderTopLeftRadius: a.rounded_md.borderRadius, 71 71 }} 72 72 source={{uri: imageUri}} 73 73 accessibilityIgnoresInvertColors ··· 101 101 a.py_sm, 102 102 t.atoms.border_contrast_low, 103 103 { 104 - borderBottomRightRadius: 8, 105 - borderBottomLeftRadius: 8, 104 + borderBottomRightRadius: a.rounded_md.borderRadius, 105 + borderBottomLeftRadius: a.rounded_md.borderRadius, 106 106 paddingHorizontal: isMobile ? 10 : 14, 107 107 }, 108 - !imageUri && !embedPlayerParams && [a.border, a.rounded_sm], 108 + !imageUri && !embedPlayerParams && [a.border, a.rounded_md], 109 109 ]}> 110 110 <Text 111 111 type="sm"
+7 -7
src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx
··· 229 229 collapsable={false} 230 230 style={[ 231 231 aspect, 232 - a.rounded_sm, 232 + a.rounded_md, 233 233 a.overflow_hidden, 234 234 { 235 235 borderBottomLeftRadius: 0, ··· 245 245 /> 246 246 <Fill 247 247 style={[ 248 - a.rounded_sm, 248 + a.rounded_md, 249 249 t.name === 'light' ? t.atoms.bg_contrast_975 : t.atoms.bg, 250 250 { 251 251 borderBottomLeftRadius: 0, ··· 267 267 ) : ( 268 268 <Fill 269 269 style={[ 270 - a.rounded_sm, 270 + a.rounded_md, 271 271 { 272 272 backgroundColor: 273 273 t.name === 'light' ? t.palette.contrast_975 : 'black', ··· 304 304 305 305 const styles = StyleSheet.create({ 306 306 topRadius: { 307 - borderTopLeftRadius: 8, 308 - borderTopRightRadius: 8, 307 + borderTopLeftRadius: a.rounded_md.borderRadius, 308 + borderTopRightRadius: a.rounded_md.borderRadius, 309 309 }, 310 310 overlayContainer: { 311 311 flex: 1, ··· 319 319 zIndex: 3, 320 320 }, 321 321 webview: { 322 - borderTopRightRadius: 8, 323 - borderTopLeftRadius: 8, 322 + borderTopRightRadius: a.rounded_md.borderRadius, 323 + borderTopLeftRadius: a.rounded_md.borderRadius, 324 324 backgroundColor: 'transparent', 325 325 }, 326 326 gifContainer: {
+4 -4
src/view/com/util/post-embeds/GifEmbed.tsx
··· 53 53 a.inset_0, 54 54 a.w_full, 55 55 a.h_full, 56 - a.rounded_sm, 56 + a.rounded_md, 57 57 { 58 58 zIndex: 2, 59 59 backgroundColor: !isLoaded ··· 117 117 ) 118 118 119 119 return ( 120 - <View style={[a.rounded_sm, a.overflow_hidden, a.mt_sm, style]}> 120 + <View style={[a.rounded_md, a.overflow_hidden, a.mt_sm, style]}> 121 121 <View 122 122 style={[ 123 - a.rounded_sm, 123 + a.rounded_md, 124 124 a.overflow_hidden, 125 125 {aspectRatio: params.dimensions!.width / params.dimensions!.height}, 126 126 ]}> ··· 132 132 <GifView 133 133 source={params.playerUri} 134 134 placeholderSource={link.thumb} 135 - style={[a.flex_1, a.rounded_sm]} 135 + style={[a.flex_1, a.rounded_md]} 136 136 autoplay={!autoplayDisabled} 137 137 onPlayerStateChange={onPlayerStateChange} 138 138 ref={playerRef}
+8 -8
src/view/com/util/post-embeds/QuoteEmbed.tsx
··· 219 219 return ( 220 220 <ContentHider 221 221 modui={moderation?.ui('contentList')} 222 - style={[styles.container, a.border, t.atoms.border_contrast_low, style]} 222 + style={[ 223 + a.rounded_md, 224 + a.p_md, 225 + a.mt_sm, 226 + a.border, 227 + t.atoms.border_contrast_low, 228 + style, 229 + ]} 223 230 childContainerStyle={[a.pt_sm]}> 224 231 <Link 225 232 hoverStyle={{borderColor: pal.colors.borderLinkHover}} ··· 293 300 } 294 301 295 302 const styles = StyleSheet.create({ 296 - container: { 297 - borderRadius: 8, 298 - marginTop: 8, 299 - paddingVertical: 12, 300 - paddingHorizontal: 12, 301 - borderWidth: StyleSheet.hairlineWidth, 302 - }, 303 303 errorContainer: { 304 304 flexDirection: 'row', 305 305 alignItems: 'center',
+2 -2
src/view/com/util/post-embeds/VideoEmbed.tsx
··· 40 40 <View 41 41 style={[ 42 42 a.w_full, 43 - a.rounded_sm, 43 + a.rounded_md, 44 44 a.overflow_hidden, 45 45 {aspectRatio}, 46 46 {backgroundColor: 'black'}, 47 - a.my_xs, 47 + a.mt_xs, 48 48 ]}> 49 49 <ErrorBoundary renderError={renderError} key={key}> 50 50 <InnerWrapper embed={embed} />
+2 -2
src/view/com/util/post-embeds/VideoEmbed.web.tsx
··· 66 66 {aspectRatio}, 67 67 {backgroundColor: 'black'}, 68 68 a.relative, 69 - a.rounded_sm, 70 - a.my_xs, 69 + a.rounded_md, 70 + a.mt_xs, 71 71 ]}> 72 72 <div 73 73 ref={ref}
+1 -1
src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx
··· 78 78 }, [embed.playlist]) 79 79 80 80 return ( 81 - <View style={[a.flex_1, a.rounded_sm, a.overflow_hidden]}> 81 + <View style={[a.flex_1, a.rounded_md, a.overflow_hidden]}> 82 82 <div ref={containerRef} style={{height: '100%', width: '100%'}}> 83 83 <figure style={{margin: 0, position: 'absolute', inset: 0}}> 84 84 <video
+3 -6
src/view/com/util/post-embeds/index.tsx
··· 138 138 const image = images[0] 139 139 return ( 140 140 <ContentHider modui={moderation?.ui('contentMedia')}> 141 - <View style={[styles.container, style]}> 141 + <View style={[a.mt_sm, style]}> 142 142 <AutoSizedImage 143 143 crop={ 144 144 viewContext === PostEmbedViewContext.ThreadHighlighted ··· 162 162 163 163 return ( 164 164 <ContentHider modui={moderation?.ui('contentMedia')}> 165 - <View style={[styles.container, style]}> 165 + <View style={[a.mt_sm, style]}> 166 166 <ImageLayoutGrid 167 167 images={embed.images} 168 168 onPress={_openLightbox} ··· 184 184 <ExternalLinkEmbed 185 185 link={link} 186 186 onOpen={onOpen} 187 - style={[styles.container, style]} 187 + style={[a.mt_sm, style]} 188 188 /> 189 189 </ContentHider> 190 190 ) ··· 247 247 } 248 248 249 249 const styles = StyleSheet.create({ 250 - container: { 251 - marginTop: 8, 252 - }, 253 250 altContainer: { 254 251 backgroundColor: 'rgba(0, 0, 0, 0.75)', 255 252 borderRadius: 6,