Bluesky app fork with some witchin' additions 💫

Better starterpack embed (#4659)

authored by hailey.at and committed by

GitHub 878b0476 da4dfeb9

+103 -32
+4
src/lib/strings/starter-pack.ts
··· 99 99 }): string | null { 100 100 return new AtUri(`at://${did}/app.bsky.graph.starterpack/${rkey}`).toString() 101 101 } 102 + 103 + export function startUriToStarterPackUri(uri: string) { 104 + return uri.replace('/start/', '/starter-pack/') 105 + }
+11
src/lib/strings/url-helpers.ts
··· 2 2 import psl from 'psl' 3 3 import TLDs from 'tlds' 4 4 5 + import {logger} from '#/logger' 5 6 import {BSKY_SERVICE} from 'lib/constants' 6 7 import {isInvalidHandle} from 'lib/strings/handles' 7 8 ··· 285 286 const sanitizedPath = path.replace(BSKY_APP_HOST, '').replace(/^\/+/, '') 286 287 return `${BSKY_APP_HOST.replace(/\/$/, '')}/${sanitizedPath}` 287 288 } 289 + 290 + export function isShortLink(url: string): boolean { 291 + try { 292 + const urlp = new URL(url) 293 + return urlp.host === 'go.bsky.app' 294 + } catch (e) { 295 + logger.error('Failed to parse possible short link', {safeMessage: e}) 296 + return false 297 + } 298 + }
+13
src/view/com/composer/useExternalLinkFetch.ts
··· 12 12 getPostAsQuote, 13 13 } from 'lib/link-meta/bsky' 14 14 import {getLinkMeta} from 'lib/link-meta/link-meta' 15 + import {resolveShortLink} from 'lib/link-meta/resolve-short-link' 15 16 import {downloadAndResize} from 'lib/media/manip' 16 17 import { 17 18 isBskyCustomFeedUrl, 18 19 isBskyListUrl, 19 20 isBskyPostUrl, 21 + isShortLink, 20 22 } from 'lib/strings/url-helpers' 21 23 import {ImageModel} from 'state/models/media/image' 22 24 import {ComposerOpts} from 'state/shell/composer' ··· 94 96 setExtLink(undefined) 95 97 }, 96 98 ) 99 + } else if (isShortLink(extLink.uri)) { 100 + if (isShortLink(extLink.uri)) { 101 + resolveShortLink(extLink.uri).then(res => { 102 + if (res && res !== extLink.uri) { 103 + setExtLink({ 104 + uri: res, 105 + isLoading: true, 106 + }) 107 + } 108 + }) 109 + } 97 110 } else { 98 111 getLinkMeta(agent, extLink.uri).then(meta => { 99 112 if (aborted) {
+47 -29
src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
··· 2 2 import {StyleProp, View, ViewStyle} from 'react-native' 3 3 import {Image} from 'expo-image' 4 4 import {AppBskyEmbedExternal} from '@atproto/api' 5 + import {msg} from '@lingui/macro' 6 + import {useLingui} from '@lingui/react' 5 7 6 8 import {usePalette} from 'lib/hooks/usePalette' 7 9 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 8 10 import {shareUrl} from 'lib/sharing' 9 11 import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player' 12 + import { 13 + getStarterPackOgCard, 14 + parseStarterPackUri, 15 + } from 'lib/strings/starter-pack' 10 16 import {toNiceDomain} from 'lib/strings/url-helpers' 11 17 import {isNative} from 'platform/detection' 12 18 import {useExternalEmbedsPrefs} from 'state/preferences' ··· 28 34 style?: StyleProp<ViewStyle> 29 35 hideAlt?: boolean 30 36 }) => { 37 + const {_} = useLingui() 31 38 const pal = usePalette('default') 32 39 const {isMobile} = useWebMediaQueries() 33 40 const externalEmbedPrefs = useExternalEmbedsPrefs() 34 41 42 + const starterPackParsed = parseStarterPackUri(link.uri) 43 + const imageUri = starterPackParsed 44 + ? getStarterPackOgCard(starterPackParsed.name, starterPackParsed.rkey) 45 + : link.thumb 46 + 35 47 const embedPlayerParams = React.useMemo(() => { 36 48 const params = parseEmbedPlayerFromUrl(link.uri) 37 49 ··· 47 59 return ( 48 60 <View style={[a.flex_col, a.rounded_sm, a.overflow_hidden, a.mt_sm]}> 49 61 <LinkWrapper link={link} onOpen={onOpen} style={style}> 50 - {link.thumb && !embedPlayerParams ? ( 62 + {imageUri && !embedPlayerParams ? ( 51 63 <Image 52 64 style={{ 53 65 aspectRatio: 1.91, 54 66 borderTopRightRadius: 6, 55 67 borderTopLeftRadius: 6, 56 68 }} 57 - source={{uri: link.thumb}} 69 + source={{uri: imageUri}} 58 70 accessibilityIgnoresInvertColors 71 + accessibilityLabel={starterPackParsed ? link.title : undefined} 72 + accessibilityHint={ 73 + starterPackParsed ? _(msg`Navigate to starter pack`) : undefined 74 + } 59 75 /> 60 76 ) : undefined} 61 77 {embedPlayerParams?.isGif ? ( ··· 63 79 ) : embedPlayerParams ? ( 64 80 <ExternalPlayer link={link} params={embedPlayerParams} /> 65 81 ) : undefined} 66 - <View 67 - style={[ 68 - a.flex_1, 69 - a.py_sm, 70 - { 71 - paddingHorizontal: isMobile ? 10 : 14, 72 - }, 73 - ]}> 74 - <Text 75 - type="sm" 76 - numberOfLines={1} 77 - style={[pal.textLight, {marginVertical: 2}]}> 78 - {toNiceDomain(link.uri)} 79 - </Text> 80 - 81 - {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( 82 - <Text type="lg-bold" numberOfLines={3} style={[pal.text]}> 83 - {link.title || link.uri} 84 - </Text> 85 - )} 86 - {link.description ? ( 82 + {!starterPackParsed ? ( 83 + <View 84 + style={[ 85 + a.flex_1, 86 + a.py_sm, 87 + { 88 + paddingHorizontal: isMobile ? 10 : 14, 89 + }, 90 + ]}> 87 91 <Text 88 - type="md" 89 - numberOfLines={link.thumb ? 2 : 4} 90 - style={[pal.text, a.mt_xs]}> 91 - {link.description} 92 + type="sm" 93 + numberOfLines={1} 94 + style={[pal.textLight, {marginVertical: 2}]}> 95 + {toNiceDomain(link.uri)} 92 96 </Text> 93 - ) : undefined} 94 - </View> 97 + 98 + {!embedPlayerParams?.isGif && !embedPlayerParams?.dimensions && ( 99 + <Text type="lg-bold" numberOfLines={3} style={[pal.text]}> 100 + {link.title || link.uri} 101 + </Text> 102 + )} 103 + {link.description ? ( 104 + <Text 105 + type="md" 106 + numberOfLines={link.thumb ? 2 : 4} 107 + style={[pal.text, a.mt_xs]}> 108 + {link.description} 109 + </Text> 110 + ) : undefined} 111 + </View> 112 + ) : null} 95 113 </LinkWrapper> 96 114 </View> 97 115 )