733 lines
22 KiB
TypeScript
733 lines
22 KiB
TypeScript
import React from 'react'
|
|
import {View} from 'react-native'
|
|
import {Image} from 'expo-image'
|
|
import {
|
|
AppBskyGraphDefs,
|
|
AppBskyGraphGetList,
|
|
AppBskyGraphStarterpack,
|
|
AtUri,
|
|
ModerationOpts,
|
|
RichText as RichTextAPI,
|
|
} from '@atproto/api'
|
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
|
import {msg, Trans} from '@lingui/macro'
|
|
import {useLingui} from '@lingui/react'
|
|
import {useNavigation} from '@react-navigation/native'
|
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
|
import {
|
|
InfiniteData,
|
|
UseInfiniteQueryResult,
|
|
useQueryClient,
|
|
} from '@tanstack/react-query'
|
|
|
|
import {cleanError} from '#/lib/strings/errors'
|
|
import {logger} from '#/logger'
|
|
import {useDeleteStarterPackMutation} from '#/state/queries/starter-packs'
|
|
import {batchedUpdates} from 'lib/batchedUpdates'
|
|
import {HITSLOP_20} from 'lib/constants'
|
|
import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links'
|
|
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
|
|
import {logEvent} from 'lib/statsig/statsig'
|
|
import {
|
|
createStarterPackUri,
|
|
getStarterPackOgCard,
|
|
} from 'lib/strings/starter-pack'
|
|
import {isWeb} from 'platform/detection'
|
|
import {updateProfileShadow} from 'state/cache/profile-shadow'
|
|
import {useModerationOpts} from 'state/preferences/moderation-opts'
|
|
import {useListMembersQuery} from 'state/queries/list-members'
|
|
import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link'
|
|
import {useResolveDidQuery} from 'state/queries/resolve-uri'
|
|
import {useShortenLink} from 'state/queries/shorten-link'
|
|
import {useStarterPackQuery} from 'state/queries/starter-packs'
|
|
import {useAgent, useSession} from 'state/session'
|
|
import {useSetActiveStarterPack} from 'state/shell/starter-pack'
|
|
import * as Toast from '#/view/com/util/Toast'
|
|
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
|
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
|
import {CenteredView} from 'view/com/util/Views'
|
|
import {bulkWriteFollows} from '#/screens/Onboarding/util'
|
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
|
import {useDialogControl} from '#/components/Dialog'
|
|
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox'
|
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
|
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
|
import {Pencil_Stroke2_Corner0_Rounded as Pencil} from '#/components/icons/Pencil'
|
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
|
import {ListMaybePlaceholder} from '#/components/Lists'
|
|
import {Loader} from '#/components/Loader'
|
|
import * as Menu from '#/components/Menu'
|
|
import * as Prompt from '#/components/Prompt'
|
|
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
|
import {RichText} from '#/components/RichText'
|
|
import {FeedsList} from '#/components/StarterPack/Main/FeedsList'
|
|
import {PostsList} from '#/components/StarterPack/Main/PostsList'
|
|
import {ProfilesList} from '#/components/StarterPack/Main/ProfilesList'
|
|
import {QrCodeDialog} from '#/components/StarterPack/QrCodeDialog'
|
|
import {ShareDialog} from '#/components/StarterPack/ShareDialog'
|
|
import {Text} from '#/components/Typography'
|
|
|
|
type StarterPackScreeProps = NativeStackScreenProps<
|
|
CommonNavigatorParams,
|
|
'StarterPack'
|
|
>
|
|
type StarterPackScreenShortProps = NativeStackScreenProps<
|
|
CommonNavigatorParams,
|
|
'StarterPackShort'
|
|
>
|
|
|
|
export function StarterPackScreen({route}: StarterPackScreeProps) {
|
|
return <StarterPackAuthCheck routeParams={route.params} />
|
|
}
|
|
|
|
export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
|
|
const {_} = useLingui()
|
|
const {
|
|
data: resolvedStarterPack,
|
|
isLoading,
|
|
isError,
|
|
} = useResolvedStarterPackShortLink({
|
|
code: route.params.code,
|
|
})
|
|
|
|
if (isLoading || isError || !resolvedStarterPack) {
|
|
return (
|
|
<ListMaybePlaceholder
|
|
isLoading={isLoading}
|
|
isError={isError}
|
|
errorMessage={_(msg`That starter pack could not be found.`)}
|
|
emptyMessage={_(msg`That starter pack could not be found.`)}
|
|
/>
|
|
)
|
|
}
|
|
return <StarterPackAuthCheck routeParams={resolvedStarterPack} />
|
|
}
|
|
|
|
export function StarterPackAuthCheck({
|
|
routeParams,
|
|
}: {
|
|
routeParams: StarterPackScreeProps['route']['params']
|
|
}) {
|
|
const navigation = useNavigation<NavigationProp>()
|
|
const setActiveStarterPack = useSetActiveStarterPack()
|
|
const {currentAccount} = useSession()
|
|
|
|
React.useEffect(() => {
|
|
if (currentAccount) return
|
|
|
|
const uri = createStarterPackUri({
|
|
did: routeParams.name,
|
|
rkey: routeParams.rkey,
|
|
})
|
|
|
|
if (!uri) return
|
|
setActiveStarterPack({
|
|
uri,
|
|
})
|
|
|
|
navigation.goBack()
|
|
}, [routeParams, currentAccount, navigation, setActiveStarterPack])
|
|
|
|
if (!currentAccount) return null
|
|
|
|
return <StarterPackScreenInner routeParams={routeParams} />
|
|
}
|
|
|
|
export function StarterPackScreenInner({
|
|
routeParams,
|
|
}: {
|
|
routeParams: StarterPackScreeProps['route']['params']
|
|
}) {
|
|
const {name, rkey} = routeParams
|
|
const {_} = useLingui()
|
|
const {currentAccount} = useSession()
|
|
|
|
const moderationOpts = useModerationOpts()
|
|
const {
|
|
data: did,
|
|
isLoading: isLoadingDid,
|
|
isError: isErrorDid,
|
|
} = useResolveDidQuery(name)
|
|
const {
|
|
data: starterPack,
|
|
isLoading: isLoadingStarterPack,
|
|
isError: isErrorStarterPack,
|
|
} = useStarterPackQuery({did, rkey})
|
|
const listMembersQuery = useListMembersQuery(starterPack?.list?.uri, 50)
|
|
|
|
const isValid =
|
|
starterPack &&
|
|
(starterPack.list || starterPack?.creator?.did === currentAccount?.did) &&
|
|
AppBskyGraphDefs.validateStarterPackView(starterPack) &&
|
|
AppBskyGraphStarterpack.validateRecord(starterPack.record)
|
|
|
|
if (!did || !starterPack || !isValid || !moderationOpts) {
|
|
return (
|
|
<ListMaybePlaceholder
|
|
isLoading={
|
|
isLoadingDid ||
|
|
isLoadingStarterPack ||
|
|
listMembersQuery.isLoading ||
|
|
!moderationOpts
|
|
}
|
|
isError={isErrorDid || isErrorStarterPack || !isValid}
|
|
errorMessage={_(msg`That starter pack could not be found.`)}
|
|
emptyMessage={_(msg`That starter pack could not be found.`)}
|
|
/>
|
|
)
|
|
}
|
|
|
|
if (!starterPack.list && starterPack.creator.did === currentAccount?.did) {
|
|
return <InvalidStarterPack rkey={rkey} />
|
|
}
|
|
|
|
return (
|
|
<StarterPackScreenLoaded
|
|
starterPack={starterPack}
|
|
routeParams={routeParams}
|
|
listMembersQuery={listMembersQuery}
|
|
moderationOpts={moderationOpts}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function StarterPackScreenLoaded({
|
|
starterPack,
|
|
routeParams,
|
|
listMembersQuery,
|
|
moderationOpts,
|
|
}: {
|
|
starterPack: AppBskyGraphDefs.StarterPackView
|
|
routeParams: StarterPackScreeProps['route']['params']
|
|
listMembersQuery: UseInfiniteQueryResult<
|
|
InfiniteData<AppBskyGraphGetList.OutputSchema>
|
|
>
|
|
moderationOpts: ModerationOpts
|
|
}) {
|
|
const showPeopleTab = Boolean(starterPack.list)
|
|
const showFeedsTab = Boolean(starterPack.feeds?.length)
|
|
const showPostsTab = Boolean(starterPack.list)
|
|
|
|
const tabs = [
|
|
...(showPeopleTab ? ['People'] : []),
|
|
...(showFeedsTab ? ['Feeds'] : []),
|
|
...(showPostsTab ? ['Posts'] : []),
|
|
]
|
|
|
|
const qrCodeDialogControl = useDialogControl()
|
|
const shareDialogControl = useDialogControl()
|
|
|
|
const shortenLink = useShortenLink()
|
|
const [link, setLink] = React.useState<string>()
|
|
const [imageLoaded, setImageLoaded] = React.useState(false)
|
|
|
|
React.useEffect(() => {
|
|
logEvent('starterPack:opened', {
|
|
starterPack: starterPack.uri,
|
|
})
|
|
}, [starterPack.uri])
|
|
|
|
const onOpenShareDialog = React.useCallback(() => {
|
|
const rkey = new AtUri(starterPack.uri).rkey
|
|
shortenLink(makeStarterPackLink(starterPack.creator.did, rkey)).then(
|
|
res => {
|
|
setLink(res.url)
|
|
},
|
|
)
|
|
Image.prefetch(getStarterPackOgCard(starterPack))
|
|
.then(() => {
|
|
setImageLoaded(true)
|
|
})
|
|
.catch(() => {
|
|
setImageLoaded(true)
|
|
})
|
|
shareDialogControl.open()
|
|
}, [shareDialogControl, shortenLink, starterPack])
|
|
|
|
React.useEffect(() => {
|
|
if (routeParams.new) {
|
|
onOpenShareDialog()
|
|
}
|
|
}, [onOpenShareDialog, routeParams.new, shareDialogControl])
|
|
|
|
return (
|
|
<CenteredView style={[a.h_full_vh]}>
|
|
<View style={isWeb ? {minHeight: '100%'} : {height: '100%'}}>
|
|
<PagerWithHeader
|
|
items={tabs}
|
|
isHeaderReady={true}
|
|
renderHeader={() => (
|
|
<Header
|
|
starterPack={starterPack}
|
|
routeParams={routeParams}
|
|
onOpenShareDialog={onOpenShareDialog}
|
|
/>
|
|
)}>
|
|
{showPeopleTab
|
|
? ({headerHeight, scrollElRef}) => (
|
|
<ProfilesList
|
|
// Validated above
|
|
listUri={starterPack!.list!.uri}
|
|
headerHeight={headerHeight}
|
|
// @ts-expect-error
|
|
scrollElRef={scrollElRef}
|
|
listMembersQuery={listMembersQuery}
|
|
moderationOpts={moderationOpts}
|
|
/>
|
|
)
|
|
: null}
|
|
{showFeedsTab
|
|
? ({headerHeight, scrollElRef}) => (
|
|
<FeedsList
|
|
// @ts-expect-error ?
|
|
feeds={starterPack?.feeds}
|
|
headerHeight={headerHeight}
|
|
// @ts-expect-error
|
|
scrollElRef={scrollElRef}
|
|
/>
|
|
)
|
|
: null}
|
|
{showPostsTab
|
|
? ({headerHeight, scrollElRef}) => (
|
|
<PostsList
|
|
// Validated above
|
|
listUri={starterPack!.list!.uri}
|
|
headerHeight={headerHeight}
|
|
// @ts-expect-error
|
|
scrollElRef={scrollElRef}
|
|
moderationOpts={moderationOpts}
|
|
/>
|
|
)
|
|
: null}
|
|
</PagerWithHeader>
|
|
</View>
|
|
|
|
<QrCodeDialog
|
|
control={qrCodeDialogControl}
|
|
starterPack={starterPack}
|
|
link={link}
|
|
/>
|
|
<ShareDialog
|
|
control={shareDialogControl}
|
|
qrDialogControl={qrCodeDialogControl}
|
|
starterPack={starterPack}
|
|
link={link}
|
|
imageLoaded={imageLoaded}
|
|
/>
|
|
</CenteredView>
|
|
)
|
|
}
|
|
|
|
function Header({
|
|
starterPack,
|
|
routeParams,
|
|
onOpenShareDialog,
|
|
}: {
|
|
starterPack: AppBskyGraphDefs.StarterPackView
|
|
routeParams: StarterPackScreeProps['route']['params']
|
|
onOpenShareDialog: () => void
|
|
}) {
|
|
const {_} = useLingui()
|
|
const t = useTheme()
|
|
const {currentAccount} = useSession()
|
|
const agent = useAgent()
|
|
const queryClient = useQueryClient()
|
|
|
|
const [isProcessing, setIsProcessing] = React.useState(false)
|
|
|
|
const {record, creator} = starterPack
|
|
const isOwn = creator?.did === currentAccount?.did
|
|
const joinedAllTimeCount = starterPack.joinedAllTimeCount ?? 0
|
|
|
|
const onFollowAll = async () => {
|
|
if (!starterPack.list) return
|
|
|
|
setIsProcessing(true)
|
|
|
|
try {
|
|
const list = await agent.app.bsky.graph.getList({
|
|
list: starterPack.list.uri,
|
|
})
|
|
const dids = list.data.items
|
|
.filter(li => !li.subject.viewer?.following)
|
|
.map(li => li.subject.did)
|
|
|
|
const followUris = await bulkWriteFollows(agent, dids)
|
|
|
|
batchedUpdates(() => {
|
|
for (let did of dids) {
|
|
updateProfileShadow(queryClient, did, {
|
|
followingUri: followUris.get(did),
|
|
})
|
|
}
|
|
})
|
|
|
|
logEvent('starterPack:followAll', {
|
|
logContext: 'StarterPackProfilesList',
|
|
starterPack: starterPack.uri,
|
|
count: dids.length,
|
|
})
|
|
Toast.show(_(msg`All accounts have been followed!`))
|
|
} catch (e) {
|
|
Toast.show(_(msg`An error occurred while trying to follow all`))
|
|
} finally {
|
|
setIsProcessing(false)
|
|
}
|
|
}
|
|
|
|
if (!AppBskyGraphStarterpack.isRecord(record)) {
|
|
return null
|
|
}
|
|
|
|
const richText = record.description
|
|
? new RichTextAPI({
|
|
text: record.description,
|
|
facets: record.descriptionFacets,
|
|
})
|
|
: undefined
|
|
|
|
return (
|
|
<>
|
|
<ProfileSubpageHeader
|
|
isLoading={false}
|
|
href={makeProfileLink(creator)}
|
|
title={record.name}
|
|
isOwner={isOwn}
|
|
avatar={undefined}
|
|
creator={creator}
|
|
avatarType="starter-pack">
|
|
<View style={[a.flex_row, a.gap_sm, a.align_center]}>
|
|
{isOwn ? (
|
|
<Button
|
|
label={_(msg`Share this starter pack`)}
|
|
hitSlop={HITSLOP_20}
|
|
variant="solid"
|
|
color="primary"
|
|
size="small"
|
|
onPress={onOpenShareDialog}>
|
|
<ButtonText>
|
|
<Trans>Share</Trans>
|
|
</ButtonText>
|
|
</Button>
|
|
) : (
|
|
<Button
|
|
label={_(msg`Follow all`)}
|
|
variant="solid"
|
|
color="primary"
|
|
size="small"
|
|
disabled={isProcessing}
|
|
onPress={onFollowAll}>
|
|
<ButtonText>
|
|
<Trans>Follow all</Trans>
|
|
{isProcessing && <Loader size="xs" />}
|
|
</ButtonText>
|
|
</Button>
|
|
)}
|
|
<OverflowMenu
|
|
routeParams={routeParams}
|
|
starterPack={starterPack}
|
|
onOpenShareDialog={onOpenShareDialog}
|
|
/>
|
|
</View>
|
|
</ProfileSubpageHeader>
|
|
{richText || joinedAllTimeCount >= 25 ? (
|
|
<View style={[a.px_lg, a.pt_md, a.pb_sm, a.gap_md]}>
|
|
{richText ? (
|
|
<RichText value={richText} style={[a.text_md, a.leading_snug]} />
|
|
) : null}
|
|
{joinedAllTimeCount >= 25 ? (
|
|
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
|
<FontAwesomeIcon
|
|
icon="arrow-trend-up"
|
|
size={12}
|
|
color={t.atoms.text_contrast_medium.color}
|
|
/>
|
|
<Text
|
|
style={[a.font_bold, a.text_sm, t.atoms.text_contrast_medium]}>
|
|
<Trans>
|
|
{starterPack.joinedAllTimeCount || 0} people have used this
|
|
starter pack!
|
|
</Trans>
|
|
</Text>
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
) : null}
|
|
</>
|
|
)
|
|
}
|
|
|
|
function OverflowMenu({
|
|
starterPack,
|
|
routeParams,
|
|
onOpenShareDialog,
|
|
}: {
|
|
starterPack: AppBskyGraphDefs.StarterPackView
|
|
routeParams: StarterPackScreeProps['route']['params']
|
|
onOpenShareDialog: () => void
|
|
}) {
|
|
const t = useTheme()
|
|
const {_} = useLingui()
|
|
const {gtMobile} = useBreakpoints()
|
|
const {currentAccount} = useSession()
|
|
const reportDialogControl = useReportDialogControl()
|
|
const deleteDialogControl = useDialogControl()
|
|
const navigation = useNavigation<NavigationProp>()
|
|
|
|
const {
|
|
mutate: deleteStarterPack,
|
|
isPending: isDeletePending,
|
|
error: deleteError,
|
|
} = useDeleteStarterPackMutation({
|
|
onSuccess: () => {
|
|
logEvent('starterPack:delete', {})
|
|
deleteDialogControl.close(() => {
|
|
if (navigation.canGoBack()) {
|
|
navigation.popToTop()
|
|
} else {
|
|
navigation.navigate('Home')
|
|
}
|
|
})
|
|
},
|
|
onError: e => {
|
|
logger.error('Failed to delete starter pack', {safeMessage: e})
|
|
},
|
|
})
|
|
|
|
const isOwn = starterPack.creator.did === currentAccount?.did
|
|
|
|
const onDeleteStarterPack = async () => {
|
|
if (!starterPack.list) {
|
|
logger.error(`Unable to delete starterpack because list is missing`)
|
|
return
|
|
}
|
|
|
|
deleteStarterPack({
|
|
rkey: routeParams.rkey,
|
|
listUri: starterPack.list.uri,
|
|
})
|
|
logEvent('starterPack:delete', {})
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Menu.Root>
|
|
<Menu.Trigger label={_(msg`Repost or quote post`)}>
|
|
{({props}) => (
|
|
<Button
|
|
{...props}
|
|
testID="headerDropdownBtn"
|
|
label={_(msg`Open starter pack menu`)}
|
|
hitSlop={HITSLOP_20}
|
|
variant="solid"
|
|
color="secondary"
|
|
size="small"
|
|
shape="round">
|
|
<ButtonIcon icon={Ellipsis} />
|
|
</Button>
|
|
)}
|
|
</Menu.Trigger>
|
|
<Menu.Outer style={{minWidth: 170}}>
|
|
{isOwn ? (
|
|
<>
|
|
<Menu.Item
|
|
label={_(msg`Edit starter pack`)}
|
|
testID="editStarterPackLinkBtn"
|
|
onPress={() => {
|
|
navigation.navigate('StarterPackEdit', {
|
|
rkey: routeParams.rkey,
|
|
})
|
|
}}>
|
|
<Menu.ItemText>
|
|
<Trans>Edit</Trans>
|
|
</Menu.ItemText>
|
|
<Menu.ItemIcon icon={Pencil} position="right" />
|
|
</Menu.Item>
|
|
<Menu.Item
|
|
label={_(msg`Delete starter pack`)}
|
|
testID="deleteStarterPackBtn"
|
|
onPress={() => {
|
|
deleteDialogControl.open()
|
|
}}>
|
|
<Menu.ItemText>
|
|
<Trans>Delete</Trans>
|
|
</Menu.ItemText>
|
|
<Menu.ItemIcon icon={Trash} position="right" />
|
|
</Menu.Item>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Menu.Group>
|
|
<Menu.Item
|
|
label={_(msg`Share`)}
|
|
testID="shareStarterPackLinkBtn"
|
|
onPress={onOpenShareDialog}>
|
|
<Menu.ItemText>
|
|
<Trans>Share link</Trans>
|
|
</Menu.ItemText>
|
|
<Menu.ItemIcon icon={ArrowOutOfBox} position="right" />
|
|
</Menu.Item>
|
|
</Menu.Group>
|
|
|
|
<Menu.Item
|
|
label={_(msg`Report starter pack`)}
|
|
onPress={reportDialogControl.open}>
|
|
<Menu.ItemText>
|
|
<Trans>Report starter pack</Trans>
|
|
</Menu.ItemText>
|
|
<Menu.ItemIcon icon={CircleInfo} position="right" />
|
|
</Menu.Item>
|
|
</>
|
|
)}
|
|
</Menu.Outer>
|
|
</Menu.Root>
|
|
|
|
{starterPack.list && (
|
|
<ReportDialog
|
|
control={reportDialogControl}
|
|
params={{
|
|
type: 'starterpack',
|
|
uri: starterPack.uri,
|
|
cid: starterPack.cid,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
<Prompt.Outer control={deleteDialogControl}>
|
|
<Prompt.TitleText>
|
|
<Trans>Delete starter pack?</Trans>
|
|
</Prompt.TitleText>
|
|
<Prompt.DescriptionText>
|
|
<Trans>Are you sure you want delete this starter pack?</Trans>
|
|
</Prompt.DescriptionText>
|
|
{deleteError && (
|
|
<View
|
|
style={[
|
|
a.flex_row,
|
|
a.gap_sm,
|
|
a.rounded_sm,
|
|
a.p_md,
|
|
a.mb_lg,
|
|
a.border,
|
|
t.atoms.border_contrast_medium,
|
|
t.atoms.bg_contrast_25,
|
|
]}>
|
|
<View style={[a.flex_1, a.gap_2xs]}>
|
|
<Text style={[a.font_bold]}>
|
|
<Trans>Unable to delete</Trans>
|
|
</Text>
|
|
<Text style={[a.leading_snug]}>{cleanError(deleteError)}</Text>
|
|
</View>
|
|
<CircleInfo size="sm" fill={t.palette.negative_400} />
|
|
</View>
|
|
)}
|
|
<Prompt.Actions>
|
|
<Button
|
|
variant="solid"
|
|
color="negative"
|
|
size={gtMobile ? 'small' : 'medium'}
|
|
label={_(msg`Yes, delete this starter pack`)}
|
|
onPress={onDeleteStarterPack}>
|
|
<ButtonText>
|
|
<Trans>Delete</Trans>
|
|
</ButtonText>
|
|
{isDeletePending && <ButtonIcon icon={Loader} />}
|
|
</Button>
|
|
<Prompt.Cancel />
|
|
</Prompt.Actions>
|
|
</Prompt.Outer>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function InvalidStarterPack({rkey}: {rkey: string}) {
|
|
const {_} = useLingui()
|
|
const t = useTheme()
|
|
const navigation = useNavigation<NavigationProp>()
|
|
const {gtMobile} = useBreakpoints()
|
|
const [isProcessing, setIsProcessing] = React.useState(false)
|
|
|
|
const goBack = () => {
|
|
if (navigation.canGoBack()) {
|
|
navigation.goBack()
|
|
} else {
|
|
navigation.replace('Home')
|
|
}
|
|
}
|
|
|
|
const {mutate: deleteStarterPack} = useDeleteStarterPackMutation({
|
|
onSuccess: () => {
|
|
setIsProcessing(false)
|
|
goBack()
|
|
},
|
|
onError: e => {
|
|
setIsProcessing(false)
|
|
logger.error('Failed to delete invalid starter pack', {safeMessage: e})
|
|
Toast.show(_(msg`Failed to delete starter pack`))
|
|
},
|
|
})
|
|
|
|
return (
|
|
<CenteredView
|
|
style={[
|
|
a.flex_1,
|
|
a.align_center,
|
|
a.gap_5xl,
|
|
!gtMobile && a.justify_between,
|
|
t.atoms.border_contrast_low,
|
|
{paddingTop: 175, paddingBottom: 110},
|
|
]}
|
|
sideBorders={true}>
|
|
<View style={[a.w_full, a.align_center, a.gap_lg]}>
|
|
<Text style={[a.font_bold, a.text_3xl]}>
|
|
<Trans>Starter pack is invalid</Trans>
|
|
</Text>
|
|
<Text
|
|
style={[
|
|
a.text_md,
|
|
a.text_center,
|
|
t.atoms.text_contrast_high,
|
|
{lineHeight: 1.4},
|
|
gtMobile ? {width: 450} : [a.w_full, a.px_lg],
|
|
]}>
|
|
<Trans>
|
|
The starter pack that you are trying to view is invalid. You may
|
|
delete this starter pack instead.
|
|
</Trans>
|
|
</Text>
|
|
</View>
|
|
<View style={[a.gap_md, gtMobile ? {width: 350} : [a.w_full, a.px_lg]]}>
|
|
<Button
|
|
variant="solid"
|
|
color="primary"
|
|
label={_(msg`Delete starter pack`)}
|
|
size="large"
|
|
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}
|
|
disabled={isProcessing}
|
|
onPress={() => {
|
|
setIsProcessing(true)
|
|
deleteStarterPack({rkey})
|
|
}}>
|
|
<ButtonText>
|
|
<Trans>Delete</Trans>
|
|
</ButtonText>
|
|
{isProcessing && <Loader size="xs" color="white" />}
|
|
</Button>
|
|
<Button
|
|
variant="solid"
|
|
color="secondary"
|
|
label={_(msg`Return to previous page`)}
|
|
size="large"
|
|
style={[a.rounded_sm, a.overflow_hidden, {paddingVertical: 10}]}
|
|
disabled={isProcessing}
|
|
onPress={goBack}>
|
|
<ButtonText>
|
|
<Trans>Go Back</Trans>
|
|
</ButtonText>
|
|
</Button>
|
|
</View>
|
|
</CenteredView>
|
|
)
|
|
}
|