forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2import {type StyleProp, View, type ViewStyle} from 'react-native'
3
4import {cleanError} from '#/lib/strings/errors'
5import {
6 useResolveGifQuery,
7 useResolveLinkQuery,
8} from '#/state/queries/resolve-link'
9import {type Gif} from '#/state/queries/tenor'
10import {ExternalEmbedRemoveBtn} from '#/view/com/composer/ExternalEmbedRemoveBtn'
11import {atoms as a, useTheme} from '#/alf'
12import {Loader} from '#/components/Loader'
13import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed'
14import {ModeratedFeedEmbed} from '#/components/Post/Embed/FeedEmbed'
15import {ModeratedListEmbed} from '#/components/Post/Embed/ListEmbed'
16import {Embed as StarterPackEmbed} from '#/components/StarterPack/StarterPackCard'
17import {Text} from '#/components/Typography'
18
19export const ExternalEmbedGif = ({
20 onRemove,
21 gif,
22}: {
23 onRemove: () => void
24 gif: Gif
25}) => {
26 const t = useTheme()
27 const {data, error} = useResolveGifQuery(gif)
28 const linkInfo = React.useMemo(
29 () =>
30 data && {
31 title: data.title ?? data.uri,
32 uri: data.uri,
33 description: data.description ?? '',
34 thumb: data.thumb?.source.path,
35 },
36 [data],
37 )
38
39 const loadingStyle: ViewStyle = {
40 aspectRatio: gif.media_formats.gif.dims[0] / gif.media_formats.gif.dims[1],
41 width: '100%',
42 }
43
44 return (
45 <View style={[a.overflow_hidden, t.atoms.border_contrast_medium]}>
46 {linkInfo ? (
47 <View style={{pointerEvents: 'auto'}}>
48 <ExternalEmbed link={linkInfo} hideAlt />
49 </View>
50 ) : error ? (
51 <Container style={[a.align_start, a.p_md, a.gap_xs]}>
52 <Text numberOfLines={1} style={t.atoms.text_contrast_high}>
53 {gif.url}
54 </Text>
55 <Text numberOfLines={2} style={[{color: t.palette.negative_400}]}>
56 {cleanError(error)}
57 </Text>
58 </Container>
59 ) : (
60 <Container style={loadingStyle}>
61 <Loader size="xl" />
62 </Container>
63 )}
64 <ExternalEmbedRemoveBtn onRemove={onRemove} />
65 </View>
66 )
67}
68
69export const ExternalEmbedLink = ({
70 uri,
71 hasQuote,
72 onRemove,
73}: {
74 uri: string
75 hasQuote: boolean
76 onRemove: () => void
77}) => {
78 const t = useTheme()
79 const {data, error} = useResolveLinkQuery(uri)
80 const linkComponent = React.useMemo(() => {
81 if (data) {
82 if (data.type === 'external') {
83 return (
84 <ExternalEmbed
85 link={{
86 title: data.title || uri,
87 uri,
88 description: data.description,
89 thumb: data.thumb?.source.path,
90 }}
91 hideAlt
92 />
93 )
94 } else if (data.kind === 'feed') {
95 return (
96 <ModeratedFeedEmbed
97 embed={{
98 type: 'feed',
99 view: {
100 $type: 'app.bsky.feed.defs#generatorView',
101 ...data.view,
102 },
103 }}
104 />
105 )
106 } else if (data.kind === 'list') {
107 return (
108 <ModeratedListEmbed
109 embed={{
110 type: 'list',
111 view: {
112 $type: 'app.bsky.graph.defs#listView',
113 ...data.view,
114 },
115 }}
116 />
117 )
118 } else if (data.kind === 'starter-pack') {
119 return <StarterPackEmbed starterPack={data.view} />
120 }
121 }
122 }, [data, uri])
123
124 if (data?.type === 'record' && hasQuote) {
125 // This is not currently supported by the data model so don't preview it.
126 return null
127 }
128
129 return (
130 <View style={[a.mb_xl, a.overflow_hidden, t.atoms.border_contrast_medium]}>
131 {linkComponent ? (
132 <View style={{pointerEvents: 'none'}}>{linkComponent}</View>
133 ) : error ? (
134 <Container style={[a.align_start, a.p_md, a.gap_xs]}>
135 <Text numberOfLines={1} style={t.atoms.text_contrast_high}>
136 {uri}
137 </Text>
138 <Text numberOfLines={2} style={[{color: t.palette.negative_400}]}>
139 {cleanError(error)}
140 </Text>
141 </Container>
142 ) : (
143 <Container>
144 <Loader size="xl" />
145 </Container>
146 )}
147 <ExternalEmbedRemoveBtn onRemove={onRemove} />
148 </View>
149 )
150}
151
152function Container({
153 style,
154 children,
155}: {
156 style?: StyleProp<ViewStyle>
157 children: React.ReactNode
158}) {
159 const t = useTheme()
160 return (
161 <View
162 style={[
163 a.rounded_sm,
164 a.border,
165 a.align_center,
166 a.justify_center,
167 a.py_5xl,
168 t.atoms.bg_contrast_25,
169 t.atoms.border_contrast_medium,
170 style,
171 ]}>
172 {children}
173 </View>
174 )
175}