deer social fork for personal usage. but you might see a use idk. github mirror

[Embed] Starter pack embed embed (#4935)

* update @atproto/api

* add starter pack embed

* update depreciated BskyAgent to AtpAgent

* unrelated, but avoid direct import of type

* nits

* rm commented out code

---------

Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>

authored by samuel.fm Samuel Newman and committed by GitHub b6fa0d2d 21e214c2

Changed files
+147 -49
bskyembed
src
components
StarterPack
+3 -2
bskyembed/.eslintrc
··· 10 10 ], 11 11 "rules": { 12 12 "simple-import-sort/imports": "warn", 13 - "simple-import-sort/exports": "warn" 13 + "simple-import-sort/exports": "warn", 14 + 'no-else-return': 'off' 14 15 }, 15 16 "parserOptions": { 16 17 "sourceType": "module", 17 18 "ecmaVersion": "latest", 18 19 "project": "./bskyembed/tsconfig.json" 19 20 } 20 - } 21 + }
+1
bskyembed/assets/starterPack.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><defs><linearGradient x1="0" y1="0" x2="100%" y2="0" gradientTransform="rotate(45)" id="sky_V5w1FF_xb91wVQ_1euhBX"><stop offset="0" stop-color="#0A7AFF"></stop><stop offset="1" stop-color="#59B9FF"></stop></linearGradient></defs><path fill="url(#sky_V5w1FF_xb91wVQ_1euhBX)" fill-rule="evenodd" clip-rule="evenodd" d="M11.26 5.227 5.02 6.899c-.734.197-1.17.95-.973 1.685l1.672 6.24c.197.734.951 1.17 1.685.973l6.24-1.672c.734-.197 1.17-.951.973-1.685L12.945 6.2a1.375 1.375 0 0 0-1.685-.973Zm-6.566.459a2.632 2.632 0 0 0-1.86 3.223l1.672 6.24a2.632 2.632 0 0 0 3.223 1.861l6.24-1.672a2.631 2.631 0 0 0 1.861-3.223l-1.672-6.24a2.632 2.632 0 0 0-3.223-1.861l-6.24 1.672Z"></path><path fill="url(#sky_V5w1FF_xb91wVQ_1euhBX)" fill-rule="evenodd" clip-rule="evenodd" d="M15.138 18.411a4.606 4.606 0 1 0 0-9.211 4.606 4.606 0 0 0 0 9.211Zm0 1.257a5.862 5.862 0 1 0 0-11.724 5.862 5.862 0 0 0 0 11.724Z"></path></svg>
+1 -1
bskyembed/package.json
··· 9 9 "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx src" 10 10 }, 11 11 "dependencies": { 12 - "@atproto/api": "^0.12.2", 12 + "@atproto/api": "0.13.1", 13 13 "@preact/preset-vite": "^2.8.2", 14 14 "@vitejs/plugin-legacy": "^5.3.2", 15 15 "preact": "^10.4.8",
+76 -16
bskyembed/src/components/embed.tsx
··· 6 6 AppBskyFeedDefs, 7 7 AppBskyFeedPost, 8 8 AppBskyGraphDefs, 9 + AppBskyGraphStarterpack, 9 10 AppBskyLabelerDefs, 11 + AtUri, 10 12 } from '@atproto/api' 11 13 import {ComponentChildren, h} from 'preact' 12 14 import {useMemo} from 'preact/hooks' 13 15 14 16 import infoIcon from '../../assets/circleInfo_stroke2_corner0_rounded.svg' 17 + import starterPackIcon from '../../assets/starterPack.svg' 15 18 import {CONTENT_LABELS, labelsToInfo} from '../labels' 16 19 import {getRkey} from '../utils' 17 20 import {Link} from './link' ··· 105 108 // Case 3.2: List 106 109 if (AppBskyGraphDefs.isListView(record)) { 107 110 return ( 108 - <GenericWithImage 111 + <GenericWithImageEmbed 109 112 image={record.avatar} 110 113 title={record.name} 111 114 href={`/profile/${record.creator.did}/lists/${getRkey(record)}`} ··· 122 125 // Case 3.3: Feed 123 126 if (AppBskyFeedDefs.isGeneratorView(record)) { 124 127 return ( 125 - <GenericWithImage 128 + <GenericWithImageEmbed 126 129 image={record.avatar} 127 130 title={record.displayName} 128 131 href={`/profile/${record.creator.did}/feed/${getRkey(record)}`} ··· 134 137 135 138 // Case 3.4: Labeler 136 139 if (AppBskyLabelerDefs.isLabelerView(record)) { 137 - return ( 138 - <GenericWithImage 139 - image={record.creator.avatar} 140 - title={record.creator.displayName || record.creator.handle} 141 - href={`/profile/${record.creator.did}`} 142 - subtitle="Labeler" 143 - description={`Liked by ${record.likeCount ?? 0} users`} 144 - /> 145 - ) 140 + // Embed type does not exist in the app, so show nothing 141 + return null 146 142 } 147 143 148 - // Case 3.5: Post not found 144 + // Case 3.5: Starter pack 145 + if (AppBskyGraphDefs.isStarterPackViewBasic(record)) { 146 + return <StarterPackEmbed content={record} /> 147 + } 148 + 149 + // Case 3.6: Post not found 149 150 if (AppBskyEmbedRecord.isViewNotFound(record)) { 150 151 return <Info>Quoted post not found, it may have been deleted.</Info> 151 152 } 152 153 153 - // Case 3.6: Post blocked 154 + // Case 3.7: Post blocked 154 155 if (AppBskyEmbedRecord.isViewBlocked(record)) { 155 156 return <Info>The quoted post is blocked.</Info> 156 157 } 157 158 158 - throw new Error('Unknown embed type') 159 + // Unknown embed type 160 + return null 159 161 } 160 162 161 163 // Case 4: Record with media ··· 182 184 ) 183 185 } 184 186 185 - throw new Error('Unsupported embed type') 187 + // Unknown embed type 188 + return null 186 189 } catch (err) { 187 190 return ( 188 191 <Info>{err instanceof Error ? err.message : 'An error occurred'}</Info> ··· 314 317 ) 315 318 } 316 319 317 - function GenericWithImage({ 320 + function GenericWithImageEmbed({ 318 321 title, 319 322 subtitle, 320 323 href, ··· 350 353 </Link> 351 354 ) 352 355 } 356 + 357 + function StarterPackEmbed({ 358 + content, 359 + }: { 360 + content: AppBskyGraphDefs.StarterPackViewBasic 361 + }) { 362 + if (!AppBskyGraphStarterpack.isRecord(content.record)) { 363 + return null 364 + } 365 + 366 + const starterPackHref = getStarterPackHref(content) 367 + const imageUri = getStarterPackImage(content) 368 + 369 + return ( 370 + <Link 371 + href={starterPackHref} 372 + className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch"> 373 + <img src={imageUri} className="aspect-[1.91/1] object-cover" /> 374 + <div className="py-3 px-4"> 375 + <div className="flex space-x-2 items-center"> 376 + <img src={starterPackIcon} className="w-10 h-10" /> 377 + <div> 378 + <p className="font-semibold leading-[21px]"> 379 + {content.record.name} 380 + </p> 381 + <p className="text-sm text-textLight line-clamp-2 leading-[18px]"> 382 + Starter pack by{' '} 383 + {content.creator.displayName || `@${content.creator.handle}`} 384 + </p> 385 + </div> 386 + </div> 387 + {content.record.description && ( 388 + <p className="text-sm mt-1">{content.record.description}</p> 389 + )} 390 + {!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && ( 391 + <p className="text-sm font-semibold text-textLight mt-1"> 392 + {content.joinedAllTimeCount} users have joined! 393 + </p> 394 + )} 395 + </div> 396 + </Link> 397 + ) 398 + } 399 + 400 + // from #/lib/strings/starter-pack.ts 401 + function getStarterPackImage(starterPack: AppBskyGraphDefs.StarterPackView) { 402 + const rkey = new AtUri(starterPack.uri).rkey 403 + return `https://ogcard.cdn.bsky.app/start/${starterPack.creator.did}/${rkey}` 404 + } 405 + 406 + function getStarterPackHref( 407 + starterPack: AppBskyGraphDefs.StarterPackViewBasic, 408 + ) { 409 + const rkey = new AtUri(starterPack.uri).rkey 410 + const handleOrDid = starterPack.creator.handle || starterPack.creator.did 411 + return `/starter-pack/${handleOrDid}/${rkey}` 412 + }
+4 -1
bskyembed/src/components/post.tsx
··· 132 132 key={counter} 133 133 href={segment.link.uri} 134 134 className="text-blue-400 hover:underline" 135 - disableTracking={!segment.link.uri.startsWith('https://bsky.app')}> 135 + disableTracking={ 136 + !segment.link.uri.startsWith('https://bsky.app') && 137 + !segment.link.uri.startsWith('https://go.bsky.app') 138 + }> 136 139 {segment.text} 137 140 </Link>, 138 141 )
+2 -2
bskyembed/src/screens/post.tsx
··· 1 1 import '../index.css' 2 2 3 - import {AppBskyFeedDefs, BskyAgent} from '@atproto/api' 3 + import {AppBskyFeedDefs, AtpAgent} from '@atproto/api' 4 4 import {h, render} from 'preact' 5 5 6 6 import logo from '../../assets/logo.svg' ··· 12 12 const root = document.getElementById('app') 13 13 if (!root) throw new Error('No root element') 14 14 15 - const agent = new BskyAgent({ 15 + const agent = new AtpAgent({ 16 16 service: 'https://public.api.bsky.app', 17 17 }) 18 18
+46 -20
bskyembed/yarn.lock
··· 20 20 "@jridgewell/gen-mapping" "^0.3.5" 21 21 "@jridgewell/trace-mapping" "^0.3.24" 22 22 23 - "@atproto/api@^0.12.2": 24 - version "0.12.2" 25 - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.2.tgz#5df6d4f60dea0395c84fdebd9e81a7e853edf130" 26 - integrity sha512-UVzCiDZH2j0wrr/O8nb1edD5cYLVqB5iujueXUCbHS3rAwIxgmyLtA3Hzm2QYsGPo/+xsIg1fNvpq9rNT6KWUA== 23 + "@atproto/api@0.13.1": 24 + version "0.13.1" 25 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.1.tgz#fbf4306e4465d5467aaf031308c1b47dcc8039d0" 26 + integrity sha512-DL3iBfavn8Nnl48FmnAreQB0k0cIkW531DJ5JAHUCQZo10Nq0ZLk2/WFxcs0KuBG5wuLnGUdo+Y6/GQPVq8dYw== 27 27 dependencies: 28 28 "@atproto/common-web" "^0.3.0" 29 - "@atproto/lexicon" "^0.4.0" 29 + "@atproto/lexicon" "^0.4.1" 30 30 "@atproto/syntax" "^0.3.0" 31 - "@atproto/xrpc" "^0.5.0" 31 + "@atproto/xrpc" "^0.6.0" 32 + await-lock "^2.2.2" 32 33 multiformats "^9.9.0" 33 34 tlds "^1.234.0" 34 35 ··· 42 43 uint8arrays "3.0.0" 43 44 zod "^3.21.4" 44 45 45 - "@atproto/lexicon@^0.4.0": 46 - version "0.4.0" 47 - resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.0.tgz#63e8829945d80c25524882caa8ed27b1151cc576" 48 - integrity sha512-RvCBKdSI4M8qWm5uTNz1z3R2yIvIhmOsMuleOj8YR6BwRD+QbtUBy3l+xQ7iXf4M5fdfJFxaUNa6Ty0iRwdKqQ== 46 + "@atproto/lexicon@^0.4.1": 47 + version "0.4.1" 48 + resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.1.tgz#19155210570a2fafbcc7d4f655d9b813948e72a0" 49 + integrity sha512-bzyr+/VHXLQWbumViX5L7h1NKQObfs8Z+XZJl43OUK8nYFUI4e/sW1IZKRNfw7Wvi5YVNK+J+yP3DWIBZhkCYA== 49 50 dependencies: 50 51 "@atproto/common-web" "^0.3.0" 51 52 "@atproto/syntax" "^0.3.0" 52 53 iso-datestring-validator "^2.2.2" 53 54 multiformats "^9.9.0" 54 - zod "^3.21.4" 55 + zod "^3.23.8" 55 56 56 57 "@atproto/syntax@^0.3.0": 57 58 version "0.3.0" 58 59 resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.0.tgz#fafa2dbea9add37253005cb663e7373e05e618b3" 59 60 integrity sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA== 60 61 61 - "@atproto/xrpc@^0.5.0": 62 - version "0.5.0" 63 - resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.5.0.tgz#dacbfd8f7b13f0ab5bd56f8fdd4b460e132a6032" 64 - integrity sha512-swu+wyOLvYW4l3n+VAuJbHcPcES+tin2Lsrp8Bw5aIXIICiuFn1YMFlwK9JwVUzTH21Py1s1nHEjr4CJeElJog== 62 + "@atproto/xrpc@^0.6.0": 63 + version "0.6.0" 64 + resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.0.tgz#668c3262e67e2afa65951ea79a03bfe3720ddf5c" 65 + integrity sha512-5BbhBTv5j6MC3iIQ4+vYxQE7nLy2dDGQ+LYJrH8PptOCUdq0Pwg6aRccQ3y52kUZlhE/mzOTZ8Ngiy9pSAyfVQ== 65 66 dependencies: 66 - "@atproto/lexicon" "^0.4.0" 67 - zod "^3.21.4" 67 + "@atproto/lexicon" "^0.4.1" 68 + zod "^3.23.8" 68 69 69 70 "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": 70 71 version "7.24.2" ··· 1709 1710 integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== 1710 1711 dependencies: 1711 1712 possible-typed-array-names "^1.0.0" 1713 + 1714 + await-lock@^2.2.2: 1715 + version "2.2.2" 1716 + resolved "https://registry.yarnpkg.com/await-lock/-/await-lock-2.2.2.tgz#a95a9b269bfd2f69d22b17a321686f551152bcef" 1717 + integrity sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw== 1712 1718 1713 1719 babel-plugin-polyfill-corejs2@^0.4.10: 1714 1720 version "0.4.10" ··· 3730 3736 resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-1.0.0-pre2.tgz#46a83a79f1b287807e9aaafc6a5dd8bcde626f9c" 3731 3737 integrity sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A== 3732 3738 3733 - "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: 3734 - name string-width-cjs 3739 + "string-width-cjs@npm:string-width@^4.2.0": 3740 + version "4.2.3" 3741 + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 3742 + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 3743 + dependencies: 3744 + emoji-regex "^8.0.0" 3745 + is-fullwidth-code-point "^3.0.0" 3746 + strip-ansi "^6.0.1" 3747 + 3748 + string-width@^4.1.0: 3735 3749 version "4.2.3" 3736 3750 resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 3737 3751 integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== ··· 3795 3809 define-properties "^1.2.1" 3796 3810 es-object-atoms "^1.0.0" 3797 3811 3798 - "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: 3812 + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": 3813 + version "6.0.1" 3814 + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 3815 + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 3816 + dependencies: 3817 + ansi-regex "^5.0.1" 3818 + 3819 + strip-ansi@^6.0.0, strip-ansi@^6.0.1: 3799 3820 version "6.0.1" 3800 3821 resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 3801 3822 integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== ··· 4192 4213 version "3.22.4" 4193 4214 resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" 4194 4215 integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== 4216 + 4217 + zod@^3.23.8: 4218 + version "3.23.8" 4219 + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" 4220 + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
+14 -7
src/components/StarterPack/StarterPackCard.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 3 import {Image} from 'expo-image' 4 - import {AppBskyGraphStarterpack, AtUri} from '@atproto/api' 5 - import {StarterPackViewBasic} from '@atproto/api/dist/client/types/app/bsky/graph/defs' 4 + import {AppBskyGraphDefs, AppBskyGraphStarterpack, AtUri} from '@atproto/api' 6 5 import {msg, Trans} from '@lingui/macro' 7 6 import {useLingui} from '@lingui/react' 8 7 import {useQueryClient} from '@tanstack/react-query' ··· 17 16 import {BaseLink} from '#/components/Link' 18 17 import {Text} from '#/components/Typography' 19 18 20 - export function Default({starterPack}: {starterPack?: StarterPackViewBasic}) { 19 + export function Default({ 20 + starterPack, 21 + }: { 22 + starterPack?: AppBskyGraphDefs.StarterPackViewBasic 23 + }) { 21 24 if (!starterPack) return null 22 25 return ( 23 26 <Link starterPack={starterPack}> ··· 29 32 export function Notification({ 30 33 starterPack, 31 34 }: { 32 - starterPack?: StarterPackViewBasic 35 + starterPack?: AppBskyGraphDefs.StarterPackViewBasic 33 36 }) { 34 37 if (!starterPack) return null 35 38 return ( ··· 44 47 noIcon, 45 48 noDescription, 46 49 }: { 47 - starterPack: StarterPackViewBasic 50 + starterPack: AppBskyGraphDefs.StarterPackViewBasic 48 51 noIcon?: boolean 49 52 noDescription?: boolean 50 53 }) { ··· 94 97 starterPack, 95 98 children, 96 99 }: { 97 - starterPack: StarterPackViewBasic 100 + starterPack: AppBskyGraphDefs.StarterPackViewBasic 98 101 onPress?: () => void 99 102 children: React.ReactNode 100 103 }) { ··· 129 132 ) 130 133 } 131 134 132 - export function Embed({starterPack}: {starterPack: StarterPackViewBasic}) { 135 + export function Embed({ 136 + starterPack, 137 + }: { 138 + starterPack: AppBskyGraphDefs.StarterPackViewBasic 139 + }) { 133 140 const t = useTheme() 134 141 const imageUri = getStarterPackOgCard(starterPack) 135 142