forked from
jollywhoppers.com/witchsky.app
fork
Configure Feed
Select the types of activity you want to include in your feed.
Bluesky app fork with some witchin' additions 馃挮
fork
Configure Feed
Select the types of activity you want to include in your feed.
1import React from 'react'
2import {View} from 'react-native'
3import {
4 type AppBskyActorDefs,
5 AppBskyFeedGetAuthorFeed,
6 AtUri,
7} from '@atproto/api'
8import {msg as msgLingui, Trans} from '@lingui/macro'
9import {useLingui} from '@lingui/react'
10import {useNavigation} from '@react-navigation/native'
11
12import {usePalette} from '#/lib/hooks/usePalette'
13import {type NavigationProp} from '#/lib/routes/types'
14import {cleanError} from '#/lib/strings/errors'
15import {logger} from '#/logger'
16import {type FeedDescriptor} from '#/state/queries/post-feed'
17import {useRemoveFeedMutation} from '#/state/queries/preferences'
18import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
19import * as Prompt from '#/components/Prompt'
20import {EmptyState} from '../util/EmptyState'
21import {ErrorMessage} from '../util/error/ErrorMessage'
22import {Button} from '../util/forms/Button'
23import {Text} from '../util/text/Text'
24import * as Toast from '../util/Toast'
25
26export enum KnownError {
27 Block = 'Block',
28 FeedgenDoesNotExist = 'FeedgenDoesNotExist',
29 FeedgenMisconfigured = 'FeedgenMisconfigured',
30 FeedgenBadResponse = 'FeedgenBadResponse',
31 FeedgenOffline = 'FeedgenOffline',
32 FeedgenUnknown = 'FeedgenUnknown',
33 FeedSignedInOnly = 'FeedSignedInOnly',
34 FeedTooManyRequests = 'FeedTooManyRequests',
35 Unknown = 'Unknown',
36}
37
38export function PostFeedErrorMessage({
39 feedDesc,
40 error,
41 onPressTryAgain,
42 savedFeedConfig,
43}: {
44 feedDesc: FeedDescriptor
45 error?: Error
46 onPressTryAgain: () => void
47 savedFeedConfig?: AppBskyActorDefs.SavedFeed
48}) {
49 const {_: _l} = useLingui()
50 const knownError = React.useMemo(
51 () => detectKnownError(feedDesc, error),
52 [feedDesc, error],
53 )
54
55 if (
56 typeof knownError !== 'undefined' &&
57 knownError !== KnownError.Unknown &&
58 feedDesc.startsWith('feedgen')
59 ) {
60 return (
61 <FeedgenErrorMessage
62 feedDesc={feedDesc}
63 knownError={knownError}
64 rawError={error}
65 savedFeedConfig={savedFeedConfig}
66 />
67 )
68 }
69
70 if (knownError === KnownError.Block) {
71 return (
72 <EmptyState
73 icon={WarningIcon}
74 iconSize="2xl"
75 message={_l(msgLingui`Posts hidden`)}
76 style={{paddingVertical: 40}}
77 />
78 )
79 }
80
81 return (
82 <ErrorMessage
83 message={cleanError(error)}
84 onPressTryAgain={onPressTryAgain}
85 />
86 )
87}
88
89function FeedgenErrorMessage({
90 feedDesc,
91 knownError,
92 rawError,
93 savedFeedConfig,
94}: {
95 feedDesc: FeedDescriptor
96 knownError: KnownError
97 rawError?: Error
98 savedFeedConfig?: AppBskyActorDefs.SavedFeed
99}) {
100 const pal = usePalette('default')
101 const {_: _l} = useLingui()
102 const navigation = useNavigation<NavigationProp>()
103 const msg = React.useMemo(
104 () =>
105 ({
106 [KnownError.Unknown]: '',
107 [KnownError.Block]: '',
108 [KnownError.FeedgenDoesNotExist]: _l(
109 msgLingui`Hmm, we're having trouble finding this feed. It may have been deleted.`,
110 ),
111 [KnownError.FeedgenMisconfigured]: _l(
112 msgLingui`Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue.`,
113 ),
114 [KnownError.FeedgenBadResponse]: _l(
115 msgLingui`Hmm, the feed server gave a bad response. Please let the feed owner know about this issue.`,
116 ),
117 [KnownError.FeedgenOffline]: _l(
118 msgLingui`Hmm, the feed server appears to be offline. Please let the feed owner know about this issue.`,
119 ),
120 [KnownError.FeedSignedInOnly]: _l(
121 msgLingui`This content is not viewable without a Bluesky account.`,
122 ),
123 [KnownError.FeedgenUnknown]: _l(
124 msgLingui`Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue.`,
125 ),
126 [KnownError.FeedTooManyRequests]: _l(
127 msgLingui`This feed is currently receiving high traffic and is temporarily unavailable. Please try again later.`,
128 ),
129 })[knownError],
130 [_l, knownError],
131 )
132 const [__, uri] = feedDesc.split('|')
133 const [ownerDid] = safeParseFeedgenUri(uri)
134 const removePromptControl = Prompt.usePromptControl()
135 const {mutateAsync: removeFeed} = useRemoveFeedMutation()
136
137 const onViewProfile = React.useCallback(() => {
138 navigation.navigate('Profile', {name: ownerDid})
139 }, [navigation, ownerDid])
140
141 const onPressRemoveFeed = React.useCallback(() => {
142 removePromptControl.open()
143 }, [removePromptControl])
144
145 const onRemoveFeed = React.useCallback(async () => {
146 try {
147 if (!savedFeedConfig) return
148 await removeFeed(savedFeedConfig)
149 } catch (err) {
150 Toast.show(
151 _l(
152 msgLingui`There was an issue removing this feed. Please check your internet connection and try again.`,
153 ),
154 'exclamation-circle',
155 )
156 logger.error('Failed to remove feed', {message: err})
157 }
158 }, [removeFeed, _l, savedFeedConfig])
159
160 const cta = React.useMemo(() => {
161 switch (knownError) {
162 case KnownError.FeedSignedInOnly: {
163 return null
164 }
165 case KnownError.FeedgenDoesNotExist:
166 case KnownError.FeedgenMisconfigured:
167 case KnownError.FeedgenBadResponse:
168 case KnownError.FeedgenOffline:
169 case KnownError.FeedgenUnknown: {
170 return (
171 <View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}>
172 {knownError === KnownError.FeedgenDoesNotExist &&
173 savedFeedConfig && (
174 <Button
175 type="inverted"
176 label={_l(msgLingui`Remove feed`)}
177 onPress={onRemoveFeed}
178 />
179 )}
180 <Button
181 type="default-light"
182 label={_l(msgLingui`View profile`)}
183 onPress={onViewProfile}
184 />
185 </View>
186 )
187 }
188 }
189 }, [knownError, onViewProfile, onRemoveFeed, _l, savedFeedConfig])
190
191 return (
192 <>
193 <View
194 style={[
195 pal.border,
196 pal.viewLight,
197 {
198 borderTopWidth: 1,
199 paddingHorizontal: 20,
200 paddingVertical: 18,
201 gap: 12,
202 },
203 ]}>
204 <Text style={pal.text}>{msg}</Text>
205
206 {rawError?.message && (
207 <Text style={pal.textLight}>
208 <Trans>Message from server: {rawError.message}</Trans>
209 </Text>
210 )}
211
212 {cta}
213 </View>
214
215 <Prompt.Basic
216 control={removePromptControl}
217 title={_l(msgLingui`Remove feed?`)}
218 description={_l(msgLingui`Remove this feed from your saved feeds`)}
219 onConfirm={onPressRemoveFeed}
220 confirmButtonCta={_l(msgLingui`Remove`)}
221 confirmButtonColor="negative"
222 />
223 </>
224 )
225}
226
227function safeParseFeedgenUri(uri: string): [string, string] {
228 try {
229 const urip = new AtUri(uri)
230 return [urip.hostname, urip.rkey]
231 } catch {
232 return ['', '']
233 }
234}
235
236function detectKnownError(
237 feedDesc: FeedDescriptor,
238 error: any,
239): KnownError | undefined {
240 if (!error) {
241 return undefined
242 }
243 if (
244 error instanceof AppBskyFeedGetAuthorFeed.BlockedActorError ||
245 error instanceof AppBskyFeedGetAuthorFeed.BlockedByActorError
246 ) {
247 return KnownError.Block
248 }
249
250 // check status codes
251 if (error?.status === 429) {
252 return KnownError.FeedTooManyRequests
253 }
254
255 // convert error to string and continue
256 if (typeof error !== 'string') {
257 error = error.toString()
258 }
259 if (error.includes(KnownError.FeedSignedInOnly)) {
260 return KnownError.FeedSignedInOnly
261 }
262 if (!feedDesc.startsWith('feedgen')) {
263 return KnownError.Unknown
264 }
265 if (error.includes('could not find feed')) {
266 return KnownError.FeedgenDoesNotExist
267 }
268 if (error.includes('feed unavailable')) {
269 return KnownError.FeedgenOffline
270 }
271 if (error.includes('invalid did document')) {
272 return KnownError.FeedgenMisconfigured
273 }
274 if (error.includes('could not resolve did document')) {
275 return KnownError.FeedgenMisconfigured
276 }
277 if (
278 error.includes('invalid feed generator service details in did document')
279 ) {
280 return KnownError.FeedgenMisconfigured
281 }
282 if (error.includes('invalid response')) {
283 return KnownError.FeedgenBadResponse
284 }
285 return KnownError.FeedgenUnknown
286}