Bluesky app fork with some witchin' additions 馃挮
at main 181 lines 4.8 kB view raw
1import {useMemo} from 'react' 2import {type StyleProp, View, type ViewStyle} from 'react-native' 3 4import {cleanError} from '#/lib/strings/errors' 5import { 6 useResolveGifQuery, 7 useResolveLinkQuery, 8} from '#/state/queries/resolve-link' 9import {type Gif} from '#/state/queries/tenor' 10import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn' 11import {atoms as a, useTheme} from '#/alf' 12import {Loader} from '#/components/Loader' 13import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed' 14import {ModeratedFeedEmbed} from '#/components/Post/Embed/FeedEmbed' 15import {ModeratedListEmbed} from '#/components/Post/Embed/ListEmbed' 16import {Embed as StarterPackEmbed} from '#/components/StarterPack/StarterPackCard' 17import {Text} from '#/components/Typography' 18 19export const ExternalEmbedGif = ({ 20 onRemove, 21 gif, 22}: { 23 onRemove: () => void 24 gif: Gif 25}) => { 26 const t = useTheme() 27 const {data, error} = useResolveGifQuery(gif) 28 const linkInfo = useMemo( 29 () => 30 data && { 31 title: data.title ?? data.uri, 32 uri: data.uri, 33 description: data.description ?? '', 34 thumb: data.thumb?.source.path, 35 }, 36 [data], 37 ) 38 39 const loadingStyle: ViewStyle = { 40 aspectRatio: (() => { 41 const dims = gif.media_formats.gif?.dims 42 if (dims && dims[0] > 0 && dims[1] > 0) { 43 return dims[0] / dims[1] 44 } 45 return 16 / 9 // Default aspect ratio 46 })(), 47 width: '100%', 48 } 49 50 return ( 51 <View style={[a.overflow_hidden, t.atoms.border_contrast_medium]}> 52 {linkInfo ? ( 53 <View style={{pointerEvents: 'auto'}}> 54 <ExternalEmbed link={linkInfo} hideAlt /> 55 </View> 56 ) : error ? ( 57 <Container style={[a.align_start, a.p_md, a.gap_xs]}> 58 <Text numberOfLines={1} style={t.atoms.text_contrast_high}> 59 {gif.url} 60 </Text> 61 <Text numberOfLines={2} style={[{color: t.palette.negative_400}]}> 62 {cleanError(error)} 63 </Text> 64 </Container> 65 ) : ( 66 <Container style={loadingStyle}> 67 <Loader size="xl" /> 68 </Container> 69 )} 70 <ExternalEmbedRemoveBtn onRemove={onRemove} /> 71 </View> 72 ) 73} 74 75export const ExternalEmbedLink = ({ 76 uri, 77 hasQuote, 78 onRemove, 79}: { 80 uri: string 81 hasQuote: boolean 82 onRemove: () => void 83}) => { 84 const t = useTheme() 85 const {data, error} = useResolveLinkQuery(uri) 86 const linkComponent = useMemo(() => { 87 if (data) { 88 if (data.type === 'external') { 89 return ( 90 <ExternalEmbed 91 link={{ 92 title: data.title || uri, 93 uri, 94 description: data.description, 95 thumb: data.thumb?.source.path, 96 }} 97 hideAlt 98 /> 99 ) 100 } else if (data.kind === 'feed') { 101 return ( 102 <ModeratedFeedEmbed 103 embed={{ 104 type: 'feed', 105 view: { 106 $type: 'app.bsky.feed.defs#generatorView', 107 ...data.view, 108 }, 109 }} 110 /> 111 ) 112 } else if (data.kind === 'list') { 113 return ( 114 <ModeratedListEmbed 115 embed={{ 116 type: 'list', 117 view: { 118 $type: 'app.bsky.graph.defs#listView', 119 ...data.view, 120 }, 121 }} 122 /> 123 ) 124 } else if (data.kind === 'starter-pack') { 125 return <StarterPackEmbed starterPack={data.view} /> 126 } 127 } 128 }, [data, uri]) 129 130 if (data?.type === 'record' && hasQuote) { 131 // This is not currently supported by the data model so don't preview it. 132 return null 133 } 134 135 return ( 136 <View style={[a.mb_xl, a.overflow_hidden, t.atoms.border_contrast_medium]}> 137 {linkComponent ? ( 138 <View style={{pointerEvents: 'none'}}>{linkComponent}</View> 139 ) : error ? ( 140 <Container style={[a.align_start, a.p_md, a.gap_xs]}> 141 <Text numberOfLines={1} style={t.atoms.text_contrast_high}> 142 {uri} 143 </Text> 144 <Text numberOfLines={2} style={[{color: t.palette.negative_400}]}> 145 {cleanError(error)} 146 </Text> 147 </Container> 148 ) : ( 149 <Container> 150 <Loader size="xl" /> 151 </Container> 152 )} 153 <ExternalEmbedRemoveBtn onRemove={onRemove} /> 154 </View> 155 ) 156} 157 158function Container({ 159 style, 160 children, 161}: { 162 style?: StyleProp<ViewStyle> 163 children: React.ReactNode 164}) { 165 const t = useTheme() 166 return ( 167 <View 168 style={[ 169 a.rounded_sm, 170 a.border, 171 a.align_center, 172 a.justify_center, 173 a.py_5xl, 174 t.atoms.bg_contrast_25, 175 t.atoms.border_contrast_medium, 176 style, 177 ]}> 178 {children} 179 </View> 180 ) 181}