Delete mutation, wip dropdown
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
@@ -8,6 +8,7 @@ import {useNavigation} from '@react-navigation/native'
|
|||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useDeleteStarterPackMutation} from '#/state/queries/starter-packs'
|
||||||
import {HITSLOP_20} from 'lib/constants'
|
import {HITSLOP_20} from 'lib/constants'
|
||||||
import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links'
|
import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links'
|
||||||
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
|
import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
|
||||||
@@ -23,25 +24,31 @@ import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
|
|||||||
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
||||||
import {CenteredView} from 'view/com/util/Views'
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
import {bulkWriteFollows} from '#/screens/Onboarding/util'
|
import {bulkWriteFollows} from '#/screens/Onboarding/util'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {DialogControlProps, useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox'
|
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as ArrowOutOfBox} from '#/components/icons/ArrowOutOfBox'
|
||||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
|
||||||
|
import {Pencil_Stroke2_Corner0_Rounded as Pencil} from '#/components/icons/Pencil'
|
||||||
import {QrCode_Stroke2_Corner0_Rounded as QrCode} from '#/components/icons/QrCode'
|
import {QrCode_Stroke2_Corner0_Rounded as QrCode} from '#/components/icons/QrCode'
|
||||||
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||||
import {ListMaybePlaceholder} from '#/components/Lists'
|
import {ListMaybePlaceholder} from '#/components/Lists'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import * as Menu from '#/components/Menu'
|
import * as Menu from '#/components/Menu'
|
||||||
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
|
||||||
import {FeedsList} from '#/components/StarterPack/Main/FeedsList'
|
import {FeedsList} from '#/components/StarterPack/Main/FeedsList'
|
||||||
import {ProfilesList} from '#/components/StarterPack/Main/ProfilesList'
|
import {ProfilesList} from '#/components/StarterPack/Main/ProfilesList'
|
||||||
import {QrCodeDialog} from '#/components/StarterPack/QrCodeDialog'
|
import {QrCodeDialog} from '#/components/StarterPack/QrCodeDialog'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export function StarterPackScreen({
|
type StarterPackScreeProps = NativeStackScreenProps<
|
||||||
route,
|
CommonNavigatorParams,
|
||||||
}: NativeStackScreenProps<CommonNavigatorParams, 'StarterPack'>) {
|
'StarterPack'
|
||||||
|
>
|
||||||
|
|
||||||
|
export function StarterPackScreen({route}: StarterPackScreeProps) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
|
||||||
const {name, rkey} = route.params
|
const {name, rkey} = route.params
|
||||||
@@ -83,7 +90,7 @@ export function StarterPackScreen({
|
|||||||
items={tabs}
|
items={tabs}
|
||||||
isHeaderReady={true}
|
isHeaderReady={true}
|
||||||
renderHeader={() => (
|
renderHeader={() => (
|
||||||
<Header starterPack={starterPack} name={name} rkey={rkey} />
|
<Header starterPack={starterPack} routeParams={route.params} />
|
||||||
)}>
|
)}>
|
||||||
{starterPack.list != null
|
{starterPack.list != null
|
||||||
? ({headerHeight, scrollElRef}) => (
|
? ({headerHeight, scrollElRef}) => (
|
||||||
@@ -117,12 +124,10 @@ export function StarterPackScreen({
|
|||||||
|
|
||||||
function Header({
|
function Header({
|
||||||
starterPack,
|
starterPack,
|
||||||
name,
|
routeParams,
|
||||||
rkey,
|
|
||||||
}: {
|
}: {
|
||||||
starterPack: AppBskyGraphDefs.StarterPackView
|
starterPack: AppBskyGraphDefs.StarterPackView
|
||||||
name: string
|
routeParams: StarterPackScreeProps['route']['params']
|
||||||
rkey: string
|
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
@@ -130,8 +135,6 @@ function Header({
|
|||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const qrCodeDialogControl = useDialogControl()
|
|
||||||
const reportDialogControl = useReportDialogControl()
|
|
||||||
|
|
||||||
const [isProcessing, setIsProcessing] = React.useState(false)
|
const [isProcessing, setIsProcessing] = React.useState(false)
|
||||||
|
|
||||||
@@ -191,7 +194,9 @@ function Header({
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
onPress={() => navigation.navigate('StarterPackEdit', {rkey})}>
|
onPress={() =>
|
||||||
|
navigation.navigate('StarterPackEdit', {rkey: routeParams.rkey})
|
||||||
|
}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Edit</Trans>
|
<Trans>Edit</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
@@ -210,13 +215,7 @@ function Header({
|
|||||||
</ButtonText>
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Dropdown
|
<OverflowMenu routeParams={routeParams} starterPack={starterPack} />
|
||||||
name={name}
|
|
||||||
rkey={rkey}
|
|
||||||
starterPack={starterPack}
|
|
||||||
qrCodeDialogControl={qrCodeDialogControl}
|
|
||||||
reportDialogControl={reportDialogControl}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
</ProfileSubpageHeader>
|
</ProfileSubpageHeader>
|
||||||
{record.description || joinedAllTimeCount >= 25 ? (
|
{record.description || joinedAllTimeCount >= 25 ? (
|
||||||
@@ -244,6 +243,136 @@ function Header({
|
|||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function OverflowMenu({
|
||||||
|
starterPack,
|
||||||
|
routeParams,
|
||||||
|
}: {
|
||||||
|
starterPack: AppBskyGraphDefs.StarterPackView
|
||||||
|
routeParams: StarterPackScreeProps['route']['params']
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
const qrCodeDialogControl = useDialogControl()
|
||||||
|
const reportDialogControl = useReportDialogControl()
|
||||||
|
const deleteDialogControl = useDialogControl()
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
const {
|
||||||
|
mutateAsync: deleteStarterPack,
|
||||||
|
isPending: isDeletePending,
|
||||||
|
error: deleteError,
|
||||||
|
} = useDeleteStarterPackMutation()
|
||||||
|
|
||||||
|
const isOwn = starterPack.creator.did === currentAccount?.did
|
||||||
|
|
||||||
|
const onDeleteStarterPack = async () => {
|
||||||
|
await deleteStarterPack({
|
||||||
|
rkey: routeParams.rkey,
|
||||||
|
// TODO need to fix types
|
||||||
|
listUri: starterPack.list!.uri,
|
||||||
|
})
|
||||||
|
|
||||||
|
logEvent('starterPack:delete', {})
|
||||||
|
|
||||||
|
deleteDialogControl.close(() => {
|
||||||
|
navigation.popToTop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 link`)}
|
||||||
|
testID="shareStarterPackLinkBtn"
|
||||||
|
onPress={() => {
|
||||||
|
logEvent('starterPack:share', {
|
||||||
|
starterPack: starterPack.uri,
|
||||||
|
shareType: 'link',
|
||||||
|
})
|
||||||
|
shareUrl(
|
||||||
|
makeStarterPackLink(routeParams.name, routeParams.rkey),
|
||||||
|
)
|
||||||
|
}}>
|
||||||
|
<Menu.ItemText>
|
||||||
|
<Trans>Share link</Trans>
|
||||||
|
</Menu.ItemText>
|
||||||
|
<Menu.ItemIcon icon={ArrowOutOfBox} position="right" />
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
label={_(msg`Create QR code`)}
|
||||||
|
testID="createQRCodeBtn"
|
||||||
|
onPress={qrCodeDialogControl.open}>
|
||||||
|
<Menu.ItemText>
|
||||||
|
<Trans>Create QR code</Trans>
|
||||||
|
</Menu.ItemText>
|
||||||
|
<Menu.ItemIcon icon={QrCode} 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>
|
||||||
|
|
||||||
<QrCodeDialog control={qrCodeDialogControl} starterPack={starterPack} />
|
<QrCodeDialog control={qrCodeDialogControl} starterPack={starterPack} />
|
||||||
<ReportDialog
|
<ReportDialog
|
||||||
control={reportDialogControl}
|
control={reportDialogControl}
|
||||||
@@ -253,123 +382,35 @@ function Header({
|
|||||||
cid: starterPack.list!.cid,
|
cid: starterPack.list!.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>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
{deleteError && (
|
||||||
|
<View>
|
||||||
|
<Text>{deleteError.toString()}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Prompt.Outer>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Dropdown({
|
|
||||||
name,
|
|
||||||
rkey,
|
|
||||||
starterPack,
|
|
||||||
qrCodeDialogControl,
|
|
||||||
reportDialogControl,
|
|
||||||
}: {
|
|
||||||
name: string
|
|
||||||
rkey: string
|
|
||||||
starterPack: AppBskyGraphDefs.StarterPackView
|
|
||||||
qrCodeDialogControl: DialogControlProps
|
|
||||||
reportDialogControl: DialogControlProps
|
|
||||||
}) {
|
|
||||||
const {_} = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
const {currentAccount} = useSession()
|
|
||||||
|
|
||||||
const isOwn = starterPack.creator.did === currentAccount?.did
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu.Root>
|
|
||||||
<Menu.Trigger label={_(msg`Repost or quote post`)}>
|
|
||||||
{({props, state}) => (
|
|
||||||
<>
|
|
||||||
{isOwn ? (
|
|
||||||
<Pressable
|
|
||||||
style={[
|
|
||||||
a.px_lg,
|
|
||||||
a.py_sm,
|
|
||||||
a.align_center,
|
|
||||||
a.justify_center,
|
|
||||||
a.rounded_sm,
|
|
||||||
{
|
|
||||||
backgroundColor: isOwn
|
|
||||||
? t.palette.primary_500
|
|
||||||
: t.palette.contrast_25,
|
|
||||||
},
|
|
||||||
(state.hovered || state.pressed) && {
|
|
||||||
backgroundColor: isOwn
|
|
||||||
? t.palette.primary_600
|
|
||||||
: t.palette.contrast_50,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
{...props}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.font_bold,
|
|
||||||
{
|
|
||||||
color: isOwn
|
|
||||||
? 'white'
|
|
||||||
: t.atoms.text_contrast_medium.color,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Trans>Share</Trans>
|
|
||||||
</Text>
|
|
||||||
</Pressable>
|
|
||||||
) : (
|
|
||||||
<Pressable
|
|
||||||
{...props}
|
|
||||||
hitSlop={HITSLOP_20}
|
|
||||||
style={[
|
|
||||||
a.justify_center,
|
|
||||||
a.align_center,
|
|
||||||
a.rounded_full,
|
|
||||||
{height: 36, width: 36},
|
|
||||||
t.atoms.bg_contrast_50,
|
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_100],
|
|
||||||
]}
|
|
||||||
testID="headerDropdownBtn">
|
|
||||||
<Ellipsis size="lg" fill={t.atoms.text_contrast_medium.color} />
|
|
||||||
</Pressable>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Menu.Trigger>
|
|
||||||
<Menu.Outer style={{minWidth: 170}}>
|
|
||||||
<Menu.Group>
|
|
||||||
<Menu.Item
|
|
||||||
label={_(msg`Share link`)}
|
|
||||||
testID="shareStarterPackLinkBtn"
|
|
||||||
onPress={() => {
|
|
||||||
logEvent('starterPack:share', {
|
|
||||||
starterPack: starterPack.uri,
|
|
||||||
shareType: 'link',
|
|
||||||
})
|
|
||||||
shareUrl(makeStarterPackLink(name, rkey))
|
|
||||||
}}>
|
|
||||||
<Menu.ItemText>
|
|
||||||
<Trans>Share link</Trans>
|
|
||||||
</Menu.ItemText>
|
|
||||||
<Menu.ItemIcon icon={ArrowOutOfBox} position="right" />
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item
|
|
||||||
label={_(msg`Create QR code`)}
|
|
||||||
testID="createQRCodeBtn"
|
|
||||||
onPress={qrCodeDialogControl.open}>
|
|
||||||
<Menu.ItemText>
|
|
||||||
<Trans>Create QR code</Trans>
|
|
||||||
</Menu.ItemText>
|
|
||||||
<Menu.ItemIcon icon={QrCode} position="right" />
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu.Group>
|
|
||||||
{!isOwn && (
|
|
||||||
<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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import {AtUri} from '@atproto/api'
|
||||||
|
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAgent} from '#/state/session'
|
||||||
|
import {invalidateActorStarterPacksQuery} from 'state/queries/actor-starter-packs'
|
||||||
|
import {invalidateListMembersQuery} from 'state/queries/list-members'
|
||||||
|
import {invalidateStarterPack} from 'state/queries/useStarterPackQuery'
|
||||||
|
|
||||||
|
export function useDeleteStarterPackMutation() {
|
||||||
|
const agent = useAgent()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({listUri, rkey}: {listUri: string; rkey: string}) => {
|
||||||
|
if (!agent.session) {
|
||||||
|
throw new Error(`Requires logged in user`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO parallel?
|
||||||
|
await agent.app.bsky.graph.list.delete({
|
||||||
|
repo: agent.session.did,
|
||||||
|
rkey: new AtUri(listUri).rkey,
|
||||||
|
})
|
||||||
|
await agent.app.bsky.graph.starterpack.delete({
|
||||||
|
repo: agent.session.did,
|
||||||
|
rkey,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onSuccess: async (_, {listUri, rkey}) => {
|
||||||
|
await invalidateListMembersQuery({queryClient, uri: listUri})
|
||||||
|
await invalidateActorStarterPacksQuery({
|
||||||
|
queryClient,
|
||||||
|
did: agent.session!.did,
|
||||||
|
})
|
||||||
|
await invalidateStarterPack({
|
||||||
|
queryClient,
|
||||||
|
did: agent.session!.did,
|
||||||
|
rkey,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user