Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 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}