From 7004e2ac248197afeaeb2e23c1c0acd7fd683ea1 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 9 Jul 2025 09:29:48 +0300 Subject: [PATCH] Better dead feed handling (#8579) * add space between content hider * add handling for feeds that fail to load * cleanError, in case of network funkiness * handle deleted lists * split out missingfeed --- src/components/Divider.tsx | 11 +- src/view/com/feeds/FeedSourceCard.tsx | 32 +++- src/view/com/feeds/MissingFeed.tsx | 222 +++++++++++++++++++++++ src/view/com/util/LoadingPlaceholder.tsx | 2 +- 4 files changed, 253 insertions(+), 14 deletions(-) create mode 100644 src/view/com/feeds/MissingFeed.tsx diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index e4891aacb1..ec7a7356ad 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -1,18 +1,11 @@ import {View} from 'react-native' -import {atoms as a, flatten, useTheme, ViewStyleProp} from '#/alf' +import {atoms as a, useTheme, type ViewStyleProp} from '#/alf' export function Divider({style}: ViewStyleProp) { const t = useTheme() return ( - + ) } diff --git a/src/view/com/feeds/FeedSourceCard.tsx b/src/view/com/feeds/FeedSourceCard.tsx index 4b2db8cf63..18e2807a84 100644 --- a/src/view/com/feeds/FeedSourceCard.tsx +++ b/src/view/com/feeds/FeedSourceCard.tsx @@ -21,6 +21,7 @@ import {atoms as a, useTheme} from '#/alf' import {Link} from '#/components/Link' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' +import {MissingFeed} from './MissingFeed' type FeedSourceCardProps = { feedUri: string @@ -49,7 +50,7 @@ export function FeedSourceCard({ } else { feed = hydrateList(feedData) } - return + return } else { return } @@ -59,14 +60,22 @@ export function FeedSourceCardWithoutData({ feedUri, ...props }: Omit) { - const {data: feed} = useFeedSourceInfoQuery({ + const {data: feed, error} = useFeedSourceInfoQuery({ uri: feedUri, }) - return + return ( + + ) } export function FeedSourceCardLoaded({ + feedUri, feed, style, showDescription = false, @@ -74,7 +83,9 @@ export function FeedSourceCardLoaded({ showMinimalPlaceholder, hideTopBorder, link = true, + error, }: { + feedUri: string feed?: FeedSourceInfo style?: StyleProp showDescription?: boolean @@ -82,6 +93,7 @@ export function FeedSourceCardLoaded({ showMinimalPlaceholder?: boolean hideTopBorder?: boolean link?: boolean + error?: unknown }) { const t = useTheme() const {_} = useLingui() @@ -92,7 +104,18 @@ export function FeedSourceCardLoaded({ * This state also captures the scenario where a feed can't load for whatever * reason. */ - if (!feed) + if (!feed) { + if (error) { + return ( + + ) + } + return ( ) + } const inner = ( <> diff --git a/src/view/com/feeds/MissingFeed.tsx b/src/view/com/feeds/MissingFeed.tsx new file mode 100644 index 0000000000..3d281a7316 --- /dev/null +++ b/src/view/com/feeds/MissingFeed.tsx @@ -0,0 +1,222 @@ +import {type StyleProp, View, type ViewStyle} from 'react-native' +import {AtUri} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {cleanError} from '#/lib/strings/errors' +import {isNative, isWeb} from '#/platform/detection' +import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {getFeedTypeFromUri} from '#/state/queries/feed' +import {useProfileQuery} from '#/state/queries/profile' +import {atoms as a, useTheme, web} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {Divider} from '#/components/Divider' +import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' +import * as ProfileCard from '#/components/ProfileCard' +import {Text} from '#/components/Typography' + +export function MissingFeed({ + style, + hideTopBorder, + uri, + error, +}: { + style?: StyleProp + hideTopBorder?: boolean + uri: string + error?: unknown +}) { + const t = useTheme() + const {_} = useLingui() + const control = Dialog.useDialogControl() + + const type = getFeedTypeFromUri(uri) + + return ( + <> + + + + + + + + ) +} + +function DialogInner({ + uri, + type, + error, +}: { + uri: string + type: 'feed' | 'list' + error: unknown +}) { + const control = Dialog.useDialogContext() + const t = useTheme() + const {_} = useLingui() + const atUri = new AtUri(uri) + const {data: profile, isError: isProfileError} = useProfileQuery({ + did: atUri.host, + }) + const moderationOpts = useModerationOpts() + + return ( + + + + {type === 'feed' ? ( + Could not connect to feed service + ) : ( + Deleted list + )} + + + {type === 'feed' ? ( + + We could not connect to the service that provides this custom + feed. It may be temporarily unavailable and experiencing issues, + or permanently unavailable. + + ) : ( + We could not find this list. It was probably deleted. + )} + + + + {type === 'feed' ? ( + Feed creator + ) : ( + List creator + )} + + {profile && moderationOpts && ( + + control.close()}> + + + + + + + )} + {isProfileError && ( + + Could not find profile + + )} + {type === 'feed' && ( + <> + + Feed identifier + + + {atUri.rkey} + + + )} + {error instanceof Error && ( + <> + + Error message + + + {cleanError(error.message)} + + + )} + + {isNative && ( + + )} + + + ) +} diff --git a/src/view/com/util/LoadingPlaceholder.tsx b/src/view/com/util/LoadingPlaceholder.tsx index 486f353f01..1b454598b8 100644 --- a/src/view/com/util/LoadingPlaceholder.tsx +++ b/src/view/com/util/LoadingPlaceholder.tsx @@ -243,7 +243,7 @@ export function FeedLoadingPlaceholder({