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