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

Configure Feed

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

at main 212 lines 5.7 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import {Image} from 'expo-image' 4import {AppBskyGraphStarterpack, AtUri} from '@atproto/api' 5import {msg} from '@lingui/core/macro' 6import {useLingui} from '@lingui/react' 7import {Plural, Trans} from '@lingui/react/macro' 8import {useQueryClient} from '@tanstack/react-query' 9 10import {sanitizeHandle} from '#/lib/strings/handles' 11import {getStarterPackOgCard} from '#/lib/strings/starter-pack' 12import {precacheResolvedUri} from '#/state/queries/resolve-uri' 13import {precacheStarterPack} from '#/state/queries/starter-packs' 14import {useSession} from '#/state/session' 15import {atoms as a, useTheme} from '#/alf' 16import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack' 17import { 18 Link as BaseLink, 19 type LinkProps as BaseLinkProps, 20} from '#/components/Link' 21import {Text} from '#/components/Typography' 22import * as bsky from '#/types/bsky' 23 24export function Default({ 25 starterPack, 26}: { 27 starterPack?: bsky.starterPack.AnyStarterPackView 28}) { 29 if (!starterPack) return null 30 return ( 31 <Link starterPack={starterPack}> 32 <Card starterPack={starterPack} /> 33 </Link> 34 ) 35} 36 37export function Notification({ 38 starterPack, 39}: { 40 starterPack?: bsky.starterPack.AnyStarterPackView 41}) { 42 if (!starterPack) return null 43 return ( 44 <Link starterPack={starterPack}> 45 <Card starterPack={starterPack} noIcon={true} noDescription={true} /> 46 </Link> 47 ) 48} 49 50export function Card({ 51 starterPack, 52 noIcon, 53 noDescription, 54}: { 55 starterPack: bsky.starterPack.AnyStarterPackView 56 noIcon?: boolean 57 noDescription?: boolean 58}) { 59 const {record, creator, joinedAllTimeCount} = starterPack 60 61 const {_} = useLingui() 62 const t = useTheme() 63 const {currentAccount} = useSession() 64 65 if ( 66 !bsky.dangerousIsType<AppBskyGraphStarterpack.Record>( 67 record, 68 AppBskyGraphStarterpack.isRecord, 69 ) 70 ) { 71 return null 72 } 73 74 return ( 75 <View style={[a.w_full, a.gap_md]}> 76 <View style={[a.flex_row, a.gap_sm, a.w_full]}> 77 {!noIcon ? <StarterPackIcon width={40} gradient="sky" /> : null} 78 <View style={[a.flex_1]}> 79 <Text 80 emoji 81 style={[a.text_md, a.font_semi_bold, a.leading_snug]} 82 numberOfLines={2}> 83 {record.name} 84 </Text> 85 <Text 86 emoji 87 style={[a.leading_snug, t.atoms.text_contrast_medium]} 88 numberOfLines={1}> 89 {creator?.did === currentAccount?.did 90 ? _(msg`Starter pack by you`) 91 : _(msg`Starter pack by ${sanitizeHandle(creator.handle, '@')}`)} 92 </Text> 93 </View> 94 </View> 95 {!noDescription && record.description ? ( 96 <Text emoji numberOfLines={3} style={[a.leading_snug]}> 97 {record.description} 98 </Text> 99 ) : null} 100 {!!joinedAllTimeCount && joinedAllTimeCount >= 50 && ( 101 <Text style={[a.font_semi_bold, t.atoms.text_contrast_medium]}> 102 <Trans comment="Number of users (always at least 50) who have joined Bluesky using a specific starter pack"> 103 <Plural value={joinedAllTimeCount} other="# users have" /> joined! 104 </Trans> 105 </Text> 106 )} 107 </View> 108 ) 109} 110 111export function useStarterPackLink({ 112 view, 113}: { 114 view: bsky.starterPack.AnyStarterPackView 115}) { 116 const {_} = useLingui() 117 const qc = useQueryClient() 118 const {rkey, handleOrDid} = React.useMemo(() => { 119 const rkey = new AtUri(view.uri).rkey 120 const {creator} = view 121 return {rkey, handleOrDid: creator.handle || creator.did} 122 }, [view]) 123 const precache = () => { 124 precacheResolvedUri(qc, view.creator.handle, view.creator.did) 125 precacheStarterPack(qc, view) 126 } 127 128 return { 129 to: `/starter-pack/${handleOrDid}/${rkey}`, 130 label: bsky.dangerousIsType<AppBskyGraphStarterpack.Record>( 131 view.record, 132 AppBskyGraphStarterpack.isRecord, 133 ) 134 ? _(msg`Navigate to ${view.record.name}`) 135 : _(msg`Navigate to starter pack`), 136 precache, 137 } 138} 139 140export function Link({ 141 starterPack, 142 children, 143}: { 144 starterPack: bsky.starterPack.AnyStarterPackView 145 onPress?: () => void 146 children: BaseLinkProps['children'] 147}) { 148 const {_} = useLingui() 149 const queryClient = useQueryClient() 150 const {record} = starterPack 151 const {rkey, handleOrDid} = React.useMemo(() => { 152 const rkey = new AtUri(starterPack.uri).rkey 153 const {creator} = starterPack 154 return {rkey, handleOrDid: creator.handle || creator.did} 155 }, [starterPack]) 156 157 if ( 158 !bsky.dangerousIsType<AppBskyGraphStarterpack.Record>( 159 record, 160 AppBskyGraphStarterpack.isRecord, 161 ) 162 ) { 163 return null 164 } 165 166 return ( 167 <BaseLink 168 to={`/starter-pack/${handleOrDid}/${rkey}`} 169 label={_(msg`Navigate to ${record.name}`)} 170 onPress={() => { 171 precacheResolvedUri( 172 queryClient, 173 starterPack.creator.handle, 174 starterPack.creator.did, 175 ) 176 precacheStarterPack(queryClient, starterPack) 177 }} 178 style={[a.flex_col, a.align_start]}> 179 {children} 180 </BaseLink> 181 ) 182} 183 184export function Embed({ 185 starterPack, 186}: { 187 starterPack: bsky.starterPack.AnyStarterPackView 188}) { 189 const t = useTheme() 190 const imageUri = getStarterPackOgCard(starterPack) 191 192 return ( 193 <View 194 style={[ 195 a.border, 196 a.rounded_sm, 197 a.overflow_hidden, 198 t.atoms.border_contrast_low, 199 ]}> 200 <Link starterPack={starterPack}> 201 <Image 202 source={imageUri} 203 style={[a.w_full, a.aspect_card]} 204 accessibilityIgnoresInvertColors={true} 205 /> 206 <View style={[a.px_sm, a.py_md]}> 207 <Card starterPack={starterPack} /> 208 </View> 209 </Link> 210 </View> 211 ) 212}