Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at cope-settings-sync 196 lines 5.9 kB view raw
1import {useCallback, useMemo} from 'react' 2import {type StyleProp, View, type ViewStyle} from 'react-native' 3import {Image} from 'expo-image' 4import {type AppBskyEmbedExternal} from '@atproto/api' 5import {msg} from '@lingui/core/macro' 6import {useLingui} from '@lingui/react' 7 8import {parseAltFromGIFDescription} from '#/lib/gif-alt-text' 9import {useHaptics} from '#/lib/haptics' 10import {shareUrl} from '#/lib/sharing' 11import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' 12import {toNiceDomain} from '#/lib/strings/url-helpers' 13import {useExternalEmbedsPrefs} from '#/state/preferences' 14import {useHighQualityImages} from '#/state/preferences/high-quality-images' 15import { 16 applyImageTransforms, 17 useImageCdnHost, 18} from '#/state/preferences/image-cdn-host' 19import {atoms as a, useTheme} from '#/alf' 20import {Divider} from '#/components/Divider' 21import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' 22import {Link} from '#/components/Link' 23import {Text} from '#/components/Typography' 24import {IS_NATIVE} from '#/env' 25import {ExternalGif} from './ExternalGif' 26import {ExternalPlayer} from './ExternalPlayer' 27import {GifEmbed} from './Gif' 28 29export const ExternalEmbed = ({ 30 link, 31 onOpen, 32 style, 33 hideAlt, 34}: { 35 link: AppBskyEmbedExternal.ViewExternal 36 onOpen?: () => void 37 style?: StyleProp<ViewStyle> 38 hideAlt?: boolean 39}) => { 40 const {_} = useLingui() 41 const t = useTheme() 42 const playHaptic = useHaptics() 43 const externalEmbedPrefs = useExternalEmbedsPrefs() 44 const highQualityImages = useHighQualityImages() 45 const imageCdnHost = useImageCdnHost() 46 const niceUrl = toNiceDomain(link.uri) 47 const imageUri = link.thumb 48 ? applyImageTransforms(link.thumb, { 49 imageCdnHost, 50 highQualityImages, 51 }) 52 : undefined 53 const embedPlayerParams = useMemo(() => { 54 const params = parseEmbedPlayerFromUrl(link.uri) 55 56 if (params && externalEmbedPrefs?.[params.source] !== 'hide') { 57 return params 58 } 59 }, [link.uri, externalEmbedPrefs]) 60 const hasMedia = Boolean(imageUri || embedPlayerParams) 61 62 const onPress = useCallback(() => { 63 playHaptic('Light') 64 onOpen?.() 65 }, [playHaptic, onOpen]) 66 67 const onShareExternal = useCallback(() => { 68 if (link.uri && IS_NATIVE) { 69 playHaptic('Heavy') 70 shareUrl(link.uri) 71 } 72 }, [link.uri, playHaptic]) 73 74 if ( 75 embedPlayerParams?.source === 'tenor' || 76 embedPlayerParams?.source === 'klipy' 77 ) { 78 const parsedAlt = parseAltFromGIFDescription(link.description) 79 return ( 80 <View style={style}> 81 <GifEmbed 82 params={embedPlayerParams} 83 thumb={link.thumb} 84 altText={parsedAlt.alt} 85 isPreferredAltText={parsedAlt.isPreferred} 86 hideAlt={hideAlt} 87 /> 88 </View> 89 ) 90 } 91 92 return ( 93 <Link 94 label={link.title || _(msg`Open link to ${niceUrl}`)} 95 to={link.uri} 96 shouldProxy={true} 97 onPress={onPress} 98 onLongPress={onShareExternal}> 99 {({hovered}) => ( 100 <View 101 style={[ 102 a.transition_color, 103 a.flex_col, 104 a.rounded_md, 105 a.overflow_hidden, 106 a.w_full, 107 a.border, 108 style, 109 hovered 110 ? t.atoms.border_contrast_high 111 : t.atoms.border_contrast_low, 112 ]}> 113 {imageUri && !embedPlayerParams ? ( 114 <Image 115 style={[a.aspect_card]} 116 source={{uri: imageUri}} 117 accessibilityIgnoresInvertColors 118 loading="lazy" 119 /> 120 ) : undefined} 121 122 {embedPlayerParams?.isGif ? ( 123 <ExternalGif link={link} params={embedPlayerParams} /> 124 ) : embedPlayerParams ? ( 125 <ExternalPlayer link={link} params={embedPlayerParams} /> 126 ) : undefined} 127 128 <View 129 style={[ 130 a.flex_1, 131 a.pt_sm, 132 {gap: 3}, 133 hasMedia && a.border_t, 134 hovered 135 ? t.atoms.border_contrast_high 136 : t.atoms.border_contrast_low, 137 ]}> 138 <View style={[{gap: 3}, a.pb_xs, a.px_md]}> 139 {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( 140 <Text 141 emoji 142 numberOfLines={3} 143 style={[a.text_md, a.font_semi_bold, a.leading_snug]}> 144 {link.title || link.uri} 145 </Text> 146 )} 147 {link.description ? ( 148 <Text 149 emoji 150 numberOfLines={imageUri ? 2 : 4} 151 style={[a.text_sm, a.leading_snug]}> 152 {link.description} 153 </Text> 154 ) : undefined} 155 </View> 156 <View style={[a.px_md]}> 157 <Divider /> 158 <View 159 style={[ 160 a.flex_row, 161 a.align_center, 162 a.gap_2xs, 163 a.pb_sm, 164 { 165 paddingTop: 6, // off menu 166 }, 167 ]}> 168 <Globe 169 size="xs" 170 style={[ 171 a.transition_color, 172 hovered 173 ? t.atoms.text_contrast_medium 174 : t.atoms.text_contrast_low, 175 ]} 176 /> 177 <Text 178 numberOfLines={1} 179 style={[ 180 a.transition_color, 181 a.text_xs, 182 a.leading_snug, 183 hovered 184 ? t.atoms.text_contrast_high 185 : t.atoms.text_contrast_medium, 186 ]}> 187 {toNiceDomain(link.uri)} 188 </Text> 189 </View> 190 </View> 191 </View> 192 </View> 193 )} 194 </Link> 195 ) 196}