import { displayMessage } from '@components/Message' import { useNavigation } from '@react-navigation/native' import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { useQueryClient } from '@tanstack/react-query' import apiInstance from '@utils/api/instance' import { featureCheck } from '@utils/helpers/featureCheck' import { checkIsMyAccount } from '@utils/helpers/isMyAccount' import { RootStackParamList, useNavState } from '@utils/navigation/navigators' import { MutationVarsTimelineUpdateStatusProperty, QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' import { useAccountStorage } from '@utils/storage/actions' import { useTheme } from '@utils/styles/ThemeManager' import { useTranslation } from 'react-i18next' import { Alert } from 'react-native' const menuStatus = ({ status, queryKey }: { status?: Mastodon.Status queryKey?: QueryKeyTimeline }): ContextMenu => { if (!status || !queryKey) return [] const navigation = useNavigation>() const { theme } = useTheme() const { t } = useTranslation(['common', 'componentContextMenu']) const navigationState = useNavState() const queryClient = useQueryClient() const mutation = useTimelineMutation({ onMutate: true, onError: (err: any, params, oldData) => { const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload ? (params as MutationVarsTimelineUpdateStatusProperty).payload.type : 'delete' displayMessage({ theme, type: 'error', message: t('common:message.error.message', { function: t(`componentContextMenu:status.${theFunction}.action` as any) }), ...(err?.status && typeof err.status === 'number' && err.data && err.data.error && typeof err.data.error === 'string' && { description: err.data.error }) }) queryClient.setQueryData(queryKey, oldData) } }) const menus: ContextMenu = [] const [accountAcct] = useAccountStorage.string('auth.account.acct') const isMyAccount = checkIsMyAccount(status.account.id) const canEditPost = featureCheck('edit_post') menus.push([ { type: 'item', key: 'status-edit', props: { onSelect: async () => { let replyToStatus: Mastodon.Status | undefined = undefined if (status.in_reply_to_id) { replyToStatus = await apiInstance({ method: 'get', url: `statuses/${status.in_reply_to_id}` }).then(res => res.body) } apiInstance<{ id: Mastodon.Status['id'] text: NonNullable spoiler_text: Mastodon.Status['spoiler_text'] }>({ method: 'get', url: `statuses/${status.id}/source` }).then(res => { navigation.navigate('Screen-Compose', { type: 'edit', incomingStatus: { ...status, text: res.body.text, spoiler_text: res.body.spoiler_text }, ...(replyToStatus && { replyToStatus }), navigationState }) }) }, disabled: false, destructive: false, hidden: !isMyAccount || !canEditPost }, title: t('componentContextMenu:status.edit.action'), icon: 'square.and.pencil' }, { type: 'item', key: 'status-delete-edit', props: { onSelect: () => Alert.alert( t('componentContextMenu:status.deleteEdit.alert.title'), t('componentContextMenu:status.deleteEdit.alert.message'), [ { text: t('common:buttons.confirm'), style: 'destructive', onPress: async () => { let replyToStatus: Mastodon.Status | undefined = undefined if (status.in_reply_to_id) { replyToStatus = await apiInstance({ method: 'get', url: `statuses/${status.in_reply_to_id}` }).then(res => res.body) } mutation .mutateAsync({ type: 'deleteItem', source: 'statuses', id: status.id }) .then(res => { navigation.navigate('Screen-Compose', { type: 'deleteEdit', incomingStatus: res.body as Mastodon.Status, ...(replyToStatus && { replyToStatus }), navigationState }) }) } }, { text: t('common:buttons.cancel') } ] ), disabled: false, destructive: true, hidden: !isMyAccount }, title: t('componentContextMenu:status.deleteEdit.action'), icon: 'pencil.and.outline' }, { type: 'item', key: 'status-delete', props: { onSelect: () => Alert.alert( t('componentContextMenu:status.delete.alert.title'), t('componentContextMenu:status.delete.alert.message'), [ { text: t('common:buttons.confirm'), style: 'destructive', onPress: async () => { mutation.mutate({ type: 'deleteItem', source: 'statuses', id: status.id }) } }, { text: t('common:buttons.cancel'), style: 'default' } ] ), disabled: false, destructive: true, hidden: !isMyAccount }, title: t('componentContextMenu:status.delete.action'), icon: 'trash' } ]) menus.push([ { type: 'item', key: 'status-mute', props: { onSelect: () => mutation.mutate({ type: 'updateStatusProperty', status, payload: { type: 'muted', to: !status.muted } }), disabled: false, destructive: false, hidden: !isMyAccount && queryKey[1].page !== 'Notifications' && !status.mentions?.find( mention => mention.acct === accountAcct && mention.username === accountAcct ) && !status.muted }, title: t('componentContextMenu:status.mute.action', { defaultValue: 'false', context: (status.muted || false).toString() }), icon: status.muted ? 'speaker' : 'speaker.slash' }, { type: 'item', key: 'status-pin', props: { onSelect: () => // Also note that reblogs cannot be pinned. mutation.mutate({ type: 'updateStatusProperty', status, payload: { type: 'pinned', to: !status.pinned } }), disabled: status.visibility !== 'public' && status.visibility !== 'unlisted', destructive: false, hidden: !isMyAccount }, title: t('componentContextMenu:status.pin.action', { defaultValue: 'false', context: (status.pinned || false).toString() }), icon: status.pinned ? 'pin.slash' : 'pin' }, { type: 'item', key: 'status-filter', props: { onSelect: () => // @ts-ignore navigation.navigate('Tab-Shared-Filter', { source: 'status', status, queryKey }), disabled: false, destructive: false, hidden: !('filtered' in status) }, title: t('componentContextMenu:status.filter.action', { defaultValue: 'false', context: (!!status.filtered?.length).toString() }), icon: status.pinned ? 'rectangle.badge.checkmark' : 'rectangle.badge.xmark' } ]) return menus } export default menuStatus