split out missingfeed
This commit is contained in:
@@ -8,29 +8,20 @@ import {
|
|||||||
import {msg, Plural, Trans} from '@lingui/macro'
|
import {msg, Plural, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
import {isNative, isWeb} from '#/platform/detection'
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
|
||||||
import {
|
import {
|
||||||
type FeedSourceInfo,
|
type FeedSourceInfo,
|
||||||
getFeedTypeFromUri,
|
|
||||||
hydrateFeedGenerator,
|
hydrateFeedGenerator,
|
||||||
hydrateList,
|
hydrateList,
|
||||||
useFeedSourceInfoQuery,
|
useFeedSourceInfoQuery,
|
||||||
} from '#/state/queries/feed'
|
} from '#/state/queries/feed'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
|
||||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme} 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 {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
import * as ProfileCard from '#/components/ProfileCard'
|
|
||||||
import {RichText} from '#/components/RichText'
|
import {RichText} from '#/components/RichText'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
import {MissingFeed} from './MissingFeed'
|
||||||
|
|
||||||
type FeedSourceCardProps = {
|
type FeedSourceCardProps = {
|
||||||
feedUri: string
|
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>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user