mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

at tooltip 169 lines 4.0 kB view raw
1import React from 'react' 2import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' 3import {Image} from 'expo-image' 4import {AppBskyFeedDefs} from '@atproto/api' 5import {Trans} from '@lingui/macro' 6 7import {isTenorGifUri} from '#/lib/strings/embed-player' 8import {atoms as a, useTheme} from '#/alf' 9import {MediaInsetBorder} from '#/components/MediaInsetBorder' 10import {Text} from '#/components/Typography' 11import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' 12import * as bsky from '#/types/bsky' 13 14/** 15 * Streamlined MediaPreview component which just handles images, gifs, and videos 16 */ 17export function Embed({ 18 embed, 19 style, 20}: { 21 embed: AppBskyFeedDefs.PostView['embed'] 22 style?: StyleProp<ViewStyle> 23}) { 24 const e = bsky.post.parseEmbed(embed) 25 26 if (!e) return null 27 28 if (e.type === 'images') { 29 return ( 30 <Outer style={style}> 31 {e.view.images.map(image => ( 32 <ImageItem 33 key={image.thumb} 34 thumbnail={image.thumb} 35 alt={image.alt} 36 /> 37 ))} 38 </Outer> 39 ) 40 } else if (e.type === 'link') { 41 if (!e.view.external.thumb) return null 42 if (!isTenorGifUri(e.view.external.uri)) return null 43 return ( 44 <Outer style={style}> 45 <GifItem 46 thumbnail={e.view.external.thumb} 47 alt={e.view.external.title} 48 /> 49 </Outer> 50 ) 51 } else if (e.type === 'video') { 52 return ( 53 <Outer style={style}> 54 <VideoItem thumbnail={e.view.thumbnail} alt={e.view.alt} /> 55 </Outer> 56 ) 57 } else if ( 58 e.type === 'post_with_media' && 59 // ignore further "nested" RecordWithMedia 60 e.media.type !== 'post_with_media' && 61 // ignore any unknowns 62 e.media.view !== null 63 ) { 64 return <Embed embed={e.media.view} style={style} /> 65 } 66 67 return null 68} 69 70export function Outer({ 71 children, 72 style, 73}: { 74 children?: React.ReactNode 75 style?: StyleProp<ViewStyle> 76}) { 77 return <View style={[a.flex_row, a.gap_xs, style]}>{children}</View> 78} 79 80export function ImageItem({ 81 thumbnail, 82 alt, 83 children, 84}: { 85 thumbnail: string 86 alt?: string 87 children?: React.ReactNode 88}) { 89 const t = useTheme() 90 return ( 91 <View style={[a.relative, a.flex_1, {aspectRatio: 1, maxWidth: 100}]}> 92 <Image 93 key={thumbnail} 94 source={{uri: thumbnail}} 95 style={[a.flex_1, a.rounded_xs, t.atoms.bg_contrast_25]} 96 contentFit="cover" 97 accessible={true} 98 accessibilityIgnoresInvertColors 99 accessibilityHint={alt} 100 accessibilityLabel="" 101 /> 102 <MediaInsetBorder style={[a.rounded_xs]} /> 103 {children} 104 </View> 105 ) 106} 107 108export function GifItem({thumbnail, alt}: {thumbnail: string; alt?: string}) { 109 return ( 110 <ImageItem thumbnail={thumbnail} alt={alt}> 111 <View style={[a.absolute, a.inset_0, a.justify_center, a.align_center]}> 112 <PlayButtonIcon size={24} /> 113 </View> 114 <View style={styles.altContainer}> 115 <Text style={styles.alt}> 116 <Trans>GIF</Trans> 117 </Text> 118 </View> 119 </ImageItem> 120 ) 121} 122 123export function VideoItem({ 124 thumbnail, 125 alt, 126}: { 127 thumbnail?: string 128 alt?: string 129}) { 130 if (!thumbnail) { 131 return ( 132 <View 133 style={[ 134 {backgroundColor: 'black'}, 135 a.flex_1, 136 {aspectRatio: 1, maxWidth: 100}, 137 a.justify_center, 138 a.align_center, 139 ]}> 140 <PlayButtonIcon size={24} /> 141 </View> 142 ) 143 } 144 return ( 145 <ImageItem thumbnail={thumbnail} alt={alt}> 146 <View style={[a.absolute, a.inset_0, a.justify_center, a.align_center]}> 147 <PlayButtonIcon size={24} /> 148 </View> 149 </ImageItem> 150 ) 151} 152 153const styles = StyleSheet.create({ 154 altContainer: { 155 backgroundColor: 'rgba(0, 0, 0, 0.75)', 156 borderRadius: 6, 157 paddingHorizontal: 6, 158 paddingVertical: 3, 159 position: 'absolute', 160 right: 5, 161 bottom: 5, 162 zIndex: 2, 163 }, 164 alt: { 165 color: 'white', 166 fontSize: 7, 167 fontWeight: '600', 168 }, 169})