Bluesky app fork with some witchin' additions 💫

[Embeds] stop adding tracking params to non-bsky.app links (#4167)

* don't add tracking params on non-bsky.app links

* validate facets

authored by samuel.fm and committed by

GitHub 334483ad 69f46848

+37 -19
+1 -1
bskyembed/.eslintrc
··· 15 15 "parserOptions": { 16 16 "sourceType": "module", 17 17 "ecmaVersion": "latest", 18 - "project": "./tsconfig.json" 18 + "project": "./bskyembed/tsconfig.json" 19 19 } 20 20 }
+3 -2
bskyembed/src/components/embed.tsx
··· 193 193 function Info({children}: {children: ComponentChildren}) { 194 194 return ( 195 195 <div className="w-full rounded-lg border py-2 px-2.5 flex-row flex gap-2 bg-neutral-50"> 196 - <img src={infoIcon as string} className="w-4 h-4 shrink-0 mt-0.5" /> 196 + <img src={infoIcon} className="w-4 h-4 shrink-0 mt-0.5" /> 197 197 <p className="text-sm text-textLight">{children}</p> 198 198 </div> 199 199 ) ··· 293 293 return ( 294 294 <Link 295 295 href={content.external.uri} 296 - className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch"> 296 + className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch" 297 + disableTracking> 297 298 {content.external.thumb && ( 298 299 <img 299 300 src={content.external.thumb}
+5 -3
bskyembed/src/components/link.tsx
··· 3 3 export function Link({ 4 4 href, 5 5 className, 6 + disableTracking, 6 7 ...props 7 8 }: { 8 9 href: string 9 10 className?: string 11 + disableTracking?: boolean 10 12 } & h.JSX.HTMLAttributes<HTMLAnchorElement>) { 11 13 const searchParam = new URLSearchParams(window.location.search) 12 14 const ref_url = searchParam.get('ref_url') ··· 19 21 20 22 return ( 21 23 <a 22 - href={`${ 23 - href.startsWith('http') ? href : `https://bsky.app${href}` 24 - }?${newSearchParam.toString()}`} 24 + href={`${href.startsWith('http') ? href : `https://bsky.app${href}`}${ 25 + disableTracking ? '' : `?${newSearchParam.toString()}` 26 + }`} 25 27 target="_blank" 26 28 rel="noopener noreferrer nofollow" 27 29 onClick={evt => evt.stopPropagation()}
+24 -9
bskyembed/src/components/post.tsx
··· 1 - import {AppBskyFeedDefs, AppBskyFeedPost, RichText} from '@atproto/api' 1 + import { 2 + AppBskyFeedDefs, 3 + AppBskyFeedPost, 4 + AppBskyRichtextFacet, 5 + RichText, 6 + } from '@atproto/api' 2 7 import {h} from 'preact' 3 8 4 9 import replyIcon from '../../assets/bubble_filled_stroke2_corner2_rounded.svg' ··· 56 61 <Link 57 62 href={href} 58 63 className="transition-transform hover:scale-110 shrink-0 self-start"> 59 - <img src={logo as string} className="h-8" /> 64 + <img src={logo} className="h-8" /> 60 65 </Link> 61 66 </div> 62 67 <PostContent record={record} /> ··· 71 76 <div className="border-t w-full pt-2.5 flex items-center gap-5 text-sm cursor-pointer"> 72 77 {!!post.likeCount && ( 73 78 <div className="flex items-center gap-2 cursor-pointer"> 74 - <img src={likeIcon as string} className="w-5 h-5" /> 79 + <img src={likeIcon} className="w-5 h-5" /> 75 80 <p className="font-bold text-neutral-500 mb-px"> 76 81 {post.likeCount} 77 82 </p> ··· 79 84 )} 80 85 {!!post.repostCount && ( 81 86 <div className="flex items-center gap-2 cursor-pointer"> 82 - <img src={repostIcon as string} className="w-5 h-5" /> 87 + <img src={repostIcon} className="w-5 h-5" /> 83 88 <p className="font-bold text-neutral-500 mb-px"> 84 89 {post.repostCount} 85 90 </p> 86 91 </div> 87 92 )} 88 93 <div className="flex items-center gap-2 cursor-pointer"> 89 - <img src={replyIcon as string} className="w-5 h-5" /> 94 + <img src={replyIcon} className="w-5 h-5" /> 90 95 <p className="font-bold text-neutral-500 mb-px">Reply</p> 91 96 </div> 92 97 <div className="flex-1" /> ··· 118 123 119 124 let counter = 0 120 125 for (const segment of rt.segments()) { 121 - if (segment.isLink() && segment.link) { 126 + if ( 127 + segment.link && 128 + AppBskyRichtextFacet.validateLink(segment.link).success 129 + ) { 122 130 richText.push( 123 131 <Link 124 132 key={counter} 125 133 href={segment.link.uri} 126 - className="text-blue-400 hover:underline"> 134 + className="text-blue-400 hover:underline" 135 + disableTracking={!segment.link.uri.startsWith('https://bsky.app')}> 127 136 {segment.text} 128 137 </Link>, 129 138 ) 130 - } else if (segment.isMention() && segment.mention) { 139 + } else if ( 140 + segment.mention && 141 + AppBskyRichtextFacet.validateMention(segment.mention).success 142 + ) { 131 143 richText.push( 132 144 <Link 133 145 key={counter} ··· 136 148 {segment.text} 137 149 </Link>, 138 150 ) 139 - } else if (segment.isTag() && segment.tag) { 151 + } else if ( 152 + segment.tag && 153 + AppBskyRichtextFacet.validateTag(segment.tag).success 154 + ) { 140 155 richText.push( 141 156 <Link 142 157 key={counter}
+2 -2
bskyembed/src/screens/landing.tsx
··· 112 112 <Link 113 113 href="https://bsky.social/about" 114 114 className="transition-transform hover:scale-110"> 115 - <img src={logo as string} className="h-10" /> 115 + <img src={logo} className="h-10" /> 116 116 </Link> 117 117 118 118 <h1 className="text-4xl font-bold text-center">Embed a Bluesky Post</h1> ··· 125 125 placeholder={DEFAULT_POST} 126 126 /> 127 127 128 - <img src={arrowBottom as string} className="w-6" /> 128 + <img src={arrowBottom} className="w-6" /> 129 129 130 130 {loading ? ( 131 131 <Skeleton />
+2 -2
bskyembed/src/screens/post.tsx
··· 52 52 <Link 53 53 href={href} 54 54 className="transition-transform hover:scale-110 absolute top-4 right-4"> 55 - <img src={logo as string} className="h-6" /> 55 + <img src={logo} className="h-6" /> 56 56 </Link> 57 57 <div className="w-full py-12 gap-4 flex flex-col items-center"> 58 58 <p className="max-w-80 text-center w-full text-textLight"> ··· 75 75 <Link 76 76 href="https://bsky.app/" 77 77 className="transition-transform hover:scale-110 absolute top-4 right-4"> 78 - <img src={logo as string} className="h-6" /> 78 + <img src={logo} className="h-6" /> 79 79 </Link> 80 80 <p className="my-16 text-center w-full text-textLight"> 81 81 Post not found, it may have been deleted.