split out missingfeed

This commit is contained in:
Samuel Newman
2025-07-09 09:28:12 +03:00
parent 399a9c25e0
commit b18a8a41d3
2 changed files with 224 additions and 201 deletions
+2 -201
View File
@@ -8,29 +8,20 @@ import {
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
import {isNative, isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {
type FeedSourceInfo,
getFeedTypeFromUri,
hydrateFeedGenerator,
hydrateList,
useFeedSourceInfoQuery,
} from '#/state/queries/feed'
import {useProfileQuery} from '#/state/queries/profile'
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {UserAvatar} from '#/view/com/util/UserAvatar'
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 {atoms as a, useTheme} from '#/alf'
import {Link} from '#/components/Link'
import * as ProfileCard from '#/components/ProfileCard'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
import {MissingFeed} from './MissingFeed'
type FeedSourceCardProps = {
feedUri: string
@@ -227,193 +218,3 @@ export function FeedSourceCardLoaded({
)
}
}
function MissingFeed({
style,
hideTopBorder,
uri,
error,
}: {
style?: StyleProp<ViewStyle>
hideTopBorder?: boolean
uri: string
error?: unknown
}) {
const t = useTheme()
const {_} = useLingui()
const atUri = new AtUri(uri)
const {data: profile, isError: isProfileError} = useProfileQuery({
did: atUri.host,
})
const moderationOpts = useModerationOpts()
const control = Dialog.useDialogControl()
const type = getFeedTypeFromUri(uri)
return (
<>
<Button
label={
type === 'feed'
? _(msg`Could not connect to custom feed`)
: _(msg`Deleted list`)
}
accessibilityHint={_(msg`Tap for more information`)}
onPress={control.open}
style={[
a.flex_1,
a.p_lg,
a.gap_md,
!hideTopBorder && !a.border_t,
t.atoms.border_contrast_low,
a.justify_start,
style,
]}>
<View style={[a.flex_row, a.align_center]}>
<View
style={[
{width: 36, height: 36},
t.atoms.bg_contrast_25,
a.rounded_sm,
a.mr_md,
a.align_center,
a.justify_center,
]}>
<WarningIcon size="lg" />
</View>
<View style={[a.flex_1]}>
<Text
emoji
style={[a.text_sm, a.font_bold, a.leading_snug, a.italic]}
numberOfLines={1}>
{type === 'feed' ? (
<Trans>Feed unavailable</Trans>
) : (
<Trans>Deleted list</Trans>
)}
</Text>
<Text
style={[
a.text_sm,
t.atoms.text_contrast_medium,
a.leading_snug,
a.italic,
]}
numberOfLines={1}>
{isWeb ? (
<Trans>Click for information</Trans>
) : (
<Trans>Tap for information</Trans>
)}
</Text>
</View>
</View>
</Button>
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`Unavailable feed information`)}
style={web({maxWidth: 500})}>
<View style={[a.gap_sm]}>
<Text style={[a.font_heavy, a.text_2xl]}>
{type === 'feed' ? (
<Trans>Could not connect to feed service</Trans>
) : (
<Trans>Deleted list</Trans>
)}
</Text>
<Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
{type === 'feed' ? (
<Trans>
We could not connect to the service that provides this custom
feed. It may be temporarily unavailable and experiencing
issues, or permanently unavailable.
</Trans>
) : (
<Trans>
We could not find this list. It was probably deleted.
</Trans>
)}
</Text>
<Divider style={[a.my_md]} />
<Text style={[a.font_bold, t.atoms.text_contrast_high]}>
{type === 'feed' ? (
<Trans>Feed creator</Trans>
) : (
<Trans>List creator</Trans>
)}
</Text>
{profile && moderationOpts && (
<View style={[a.w_full, a.align_start]}>
<ProfileCard.Link
profile={profile}
onPress={() => control.close()}>
<ProfileCard.Header>
<ProfileCard.Avatar
profile={profile}
moderationOpts={moderationOpts}
disabledPreview
/>
<ProfileCard.NameAndHandle
profile={profile}
moderationOpts={moderationOpts}
/>
</ProfileCard.Header>
</ProfileCard.Link>
</View>
)}
{isProfileError && (
<Text
style={[
t.atoms.text_contrast_high,
a.italic,
a.text_center,
a.w_full,
]}>
<Trans>Could not find profile</Trans>
</Text>
)}
{type === 'feed' && (
<>
<Text
style={[a.font_bold, t.atoms.text_contrast_high, a.mt_md]}>
<Trans>Feed identifier</Trans>
</Text>
<Text style={[a.text_md, t.atoms.text_contrast_high, a.italic]}>
{atUri.rkey}
</Text>
</>
)}
{error instanceof Error && (
<>
<Text
style={[a.font_bold, t.atoms.text_contrast_high, a.mt_md]}>
<Trans>Error message</Trans>
</Text>
<Text style={[a.text_md, t.atoms.text_contrast_high, a.italic]}>
{cleanError(error.message)}
</Text>
</>
)}
</View>
{isNative && (
<Button
label={_(msg`Close`)}
onPress={() => control.close()}
size="small"
variant="solid"
color="secondary"
style={[a.mt_5xl]}>
<ButtonText>
<Trans>Close</Trans>
</ButtonText>
</Button>
)}
<Dialog.Close />
</Dialog.ScrollableInner>
</Dialog.Outer>
</>
)
}
+222
View File
@@ -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<ViewStyle>
hideTopBorder?: boolean
uri: string
error?: unknown
}) {
const t = useTheme()
const {_} = useLingui()
const control = Dialog.useDialogControl()
const type = getFeedTypeFromUri(uri)
return (
<>
<Button
label={
type === 'feed'
? _(msg`Could not connect to custom feed`)
: _(msg`Deleted list`)
}
accessibilityHint={_(msg`Tap for more information`)}
onPress={control.open}
style={[
a.flex_1,
a.p_lg,
a.gap_md,
!hideTopBorder && !a.border_t,
t.atoms.border_contrast_low,
a.justify_start,
style,
]}>
<View style={[a.flex_row, a.align_center]}>
<View
style={[
{width: 36, height: 36},
t.atoms.bg_contrast_25,
a.rounded_sm,
a.mr_md,
a.align_center,
a.justify_center,
]}>
<WarningIcon size="lg" />
</View>
<View style={[a.flex_1]}>
<Text
emoji
style={[a.text_sm, a.font_bold, a.leading_snug, a.italic]}
numberOfLines={1}>
{type === 'feed' ? (
<Trans>Feed unavailable</Trans>
) : (
<Trans>Deleted list</Trans>
)}
</Text>
<Text
style={[
a.text_sm,
t.atoms.text_contrast_medium,
a.leading_snug,
a.italic,
]}
numberOfLines={1}>
{isWeb ? (
<Trans>Click for information</Trans>
) : (
<Trans>Tap for information</Trans>
)}
</Text>
</View>
</View>
</Button>
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
<Dialog.Handle />
<DialogInner uri={uri} type={type} error={error} />
</Dialog.Outer>
</>
)
}
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 (
<Dialog.ScrollableInner
label={
type === 'feed'
? _(msg`Unavailable feed information`)
: _(msg`Deleted list`)
}
style={web({maxWidth: 500})}>
<View style={[a.gap_sm]}>
<Text style={[a.font_heavy, a.text_2xl]}>
{type === 'feed' ? (
<Trans>Could not connect to feed service</Trans>
) : (
<Trans>Deleted list</Trans>
)}
</Text>
<Text style={[t.atoms.text_contrast_high, a.leading_snug]}>
{type === 'feed' ? (
<Trans>
We could not connect to the service that provides this custom
feed. It may be temporarily unavailable and experiencing issues,
or permanently unavailable.
</Trans>
) : (
<Trans>We could not find this list. It was probably deleted.</Trans>
)}
</Text>
<Divider style={[a.my_md]} />
<Text style={[a.font_bold, t.atoms.text_contrast_high]}>
{type === 'feed' ? (
<Trans>Feed creator</Trans>
) : (
<Trans>List creator</Trans>
)}
</Text>
{profile && moderationOpts && (
<View style={[a.w_full, a.align_start]}>
<ProfileCard.Link profile={profile} onPress={() => control.close()}>
<ProfileCard.Header>
<ProfileCard.Avatar
profile={profile}
moderationOpts={moderationOpts}
disabledPreview
/>
<ProfileCard.NameAndHandle
profile={profile}
moderationOpts={moderationOpts}
/>
</ProfileCard.Header>
</ProfileCard.Link>
</View>
)}
{isProfileError && (
<Text
style={[
t.atoms.text_contrast_high,
a.italic,
a.text_center,
a.w_full,
]}>
<Trans>Could not find profile</Trans>
</Text>
)}
{type === 'feed' && (
<>
<Text style={[a.font_bold, t.atoms.text_contrast_high, a.mt_md]}>
<Trans>Feed identifier</Trans>
</Text>
<Text style={[a.text_md, t.atoms.text_contrast_high, a.italic]}>
{atUri.rkey}
</Text>
</>
)}
{error instanceof Error && (
<>
<Text style={[a.font_bold, t.atoms.text_contrast_high, a.mt_md]}>
<Trans>Error message</Trans>
</Text>
<Text style={[a.text_md, t.atoms.text_contrast_high, a.italic]}>
{cleanError(error.message)}
</Text>
</>
)}
</View>
{isNative && (
<Button
label={_(msg`Close`)}
onPress={() => control.close()}
size="small"
variant="solid"
color="secondary"
style={[a.mt_5xl]}>
<ButtonText>
<Trans>Close</Trans>
</ButtonText>
</Button>
)}
<Dialog.Close />
</Dialog.ScrollableInner>
)
}