diff --git a/src/@types/react-navigation.d.ts b/src/@types/react-navigation.d.ts index 1b65c3eb..6d02bc1a 100644 --- a/src/@types/react-navigation.d.ts +++ b/src/@types/react-navigation.d.ts @@ -17,6 +17,7 @@ declare namespace Nav { | { type: 'edit' incomingStatus: Mastodon.Status + replyToStatus?: Mastodon.Status queryKey?: [ 'Timeline', { diff --git a/src/i18n/en/screens/sharedCompose.ts b/src/i18n/en/screens/sharedCompose.ts index ee98fd47..12bc2f78 100644 --- a/src/i18n/en/screens/sharedCompose.ts +++ b/src/i18n/en/screens/sharedCompose.ts @@ -19,8 +19,17 @@ export default { edit: 'Toot' }, alert: { - title: 'Tooting failed', - button: 'Try again' + default: { + title: 'Tooting failed', + button: 'Try again' + }, + removeReply: { + title: 'Replied toot could not be found', + description: + 'Replied toot could have been deleted. Do you want to remove it from your reference?', + cancel: '$t(common:buttons.cancel)', + confirm: 'Remove reference' + } } } }, diff --git a/src/i18n/zh-Hans/screens/sharedCompose.ts b/src/i18n/zh-Hans/screens/sharedCompose.ts index 890f5ff9..ee6f4c65 100644 --- a/src/i18n/zh-Hans/screens/sharedCompose.ts +++ b/src/i18n/zh-Hans/screens/sharedCompose.ts @@ -19,8 +19,16 @@ export default { edit: '发嘟嘟' }, alert: { - title: '发布失败', - button: '返回重试' + default: { + title: '发布失败', + button: '返回重试' + }, + removeReply: { + title: '回复的嘟文不存在', + description: '回复的嘟文可能已被删除。确认移除回复嘟文的关联?', + cancel: '$t(common:buttons.cancel)', + confirm: '移除关联' + } } } }, diff --git a/src/screens/Actions/Status.tsx b/src/screens/Actions/Status.tsx index 3f9f304f..4e42e565 100644 --- a/src/screens/Actions/Status.tsx +++ b/src/screens/Actions/Status.tsx @@ -10,9 +10,9 @@ import { } from '@utils/queryHooks/timeline' import analytics from '@components/analytics' import { StackNavigationProp } from '@react-navigation/stack' -import deleteItem from '@utils/queryHooks/timeline/deleteItem' import { displayMessage } from '@components/Message' import { useTheme } from '@utils/styles/ThemeManager' +import apiInstance from '@api/instance' export interface Props { navigation: StackNavigationProp @@ -106,26 +106,30 @@ const ActionsStatus: React.FC = ({ page: queryKey && queryKey[1].page } ) - dismiss() - const res = await mutation.mutateAsync({ - type: 'deleteItem', - source: 'statuses', - queryKey, - id: status.id - }) - deleteItem({ - queryClient, - rootQueryKey, - id: status.id - }) - if (res.body.id) { - // @ts-ignore - navigation.navigate('Screen-Compose', { - type: 'edit', - incomingStatus: res.body, - queryKey - }) + let replyToStatus: Mastodon.Status + 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', + queryKey, + id: status.id + }) + .then(res => { + dismiss() + // @ts-ignore + navigation.navigate('Screen-Compose', { + type: 'edit', + incomingStatus: res.body, + ...(replyToStatus && { replyToStatus }), + queryKey + }) + }) } } ] diff --git a/src/screens/Compose.tsx b/src/screens/Compose.tsx index 8fc5228a..cb90bc25 100644 --- a/src/screens/Compose.tsx +++ b/src/screens/Compose.tsx @@ -298,14 +298,38 @@ const ScreenCompose: React.FC = ({ navigation.goBack() }) .catch(error => { - Sentry.Native.captureException(error) - haptics('Error') - composeDispatch({ type: 'posting', payload: false }) - Alert.alert(t('heading.right.alert.title'), undefined, [ - { - text: t('heading.right.alert.button') - } - ]) + if (error.removeReply) { + Alert.alert( + t('heading.right.alert.removeReply.title'), + t('heading.right.alert.removeReply.description'), + [ + { + text: t('heading.right.alert.removeReply.cancel'), + onPress: () => { + composeDispatch({ type: 'posting', payload: false }) + }, + style: 'destructive' + }, + { + text: t('heading.right.alert.removeReply.confirm'), + onPress: () => { + composeDispatch({ type: 'removeReply' }) + composeDispatch({ type: 'posting', payload: false }) + }, + style: 'default' + } + ] + ) + } else { + Sentry.Native.captureException(error) + haptics('Error') + composeDispatch({ type: 'posting', payload: false }) + Alert.alert(t('heading.right.alert.default.title'), undefined, [ + { + text: t('heading.right.alert.default.button') + } + ]) + } }) }} loading={composeState.posting} diff --git a/src/screens/Compose/utils/parseState.ts b/src/screens/Compose/utils/parseState.ts index 4377185f..4f1ee7a3 100644 --- a/src/screens/Compose/utils/parseState.ts +++ b/src/screens/Compose/utils/parseState.ts @@ -45,7 +45,8 @@ const composeParseState = ( 'public', ...(params.incomingStatus.visibility === 'direct' && { visibilityLock: true - }) + }), + ...(params.replyToStatus && { replyToStatus: params.replyToStatus }) } case 'reply': const actualStatus = params.incomingStatus.reblog || params.incomingStatus diff --git a/src/screens/Compose/utils/post.ts b/src/screens/Compose/utils/post.ts index dedce6f6..2ba436cc 100644 --- a/src/screens/Compose/utils/post.ts +++ b/src/screens/Compose/utils/post.ts @@ -9,7 +9,17 @@ const composePost = async ( const formData = new FormData() if (composeState.replyToStatus) { - formData.append('in_reply_to_id', composeState.replyToStatus!.id) + try { + await apiInstance({ + method: 'get', + url: `statuses/${composeState.replyToStatus.id}` + }) + } catch (err) { + if (err.status == 404) { + return Promise.reject({ removeReply: true }) + } + } + formData.append('in_reply_to_id', composeState.replyToStatus.id) } if (composeState.spoiler.active) { diff --git a/src/screens/Compose/utils/reducer.ts b/src/screens/Compose/utils/reducer.ts index e7fecc30..0480393b 100644 --- a/src/screens/Compose/utils/reducer.ts +++ b/src/screens/Compose/utils/reducer.ts @@ -103,6 +103,8 @@ const composeReducer = ( ...state, textInputFocus: { ...state.textInputFocus, ...action.payload } } + case 'removeReply': + return { ...state, replyToStatus: undefined } default: throw new Error('Unexpected action') } diff --git a/src/screens/Compose/utils/types.d.ts b/src/screens/Compose/utils/types.d.ts index 883ff0ae..40e03c57 100644 --- a/src/screens/Compose/utils/types.d.ts +++ b/src/screens/Compose/utils/types.d.ts @@ -132,3 +132,6 @@ export type ComposeAction = type: 'textInputFocus' payload: Partial } + | { + type: 'removeReply' + }