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({