mirror of https://github.com/tooot-app/app
Basic editing works
This commit is contained in:
parent
95ec76f411
commit
d4f91a5756
|
@ -302,7 +302,7 @@ const ParseHTML = React.memo(
|
|||
/>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
(prev, next) => prev.content === next.content
|
||||
)
|
||||
|
||||
export default ParseHTML
|
||||
|
|
|
@ -72,7 +72,7 @@ const TimelineContent = React.memo(
|
|||
</>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
(prev, next) => prev.status.content === next.status.content
|
||||
)
|
||||
|
||||
export default TimelineContent
|
||||
|
|
|
@ -167,11 +167,23 @@
|
|||
},
|
||||
"status": {
|
||||
"heading": "About toot",
|
||||
"edit": {
|
||||
"function": "Edit toot",
|
||||
"button": "Edit this toot"
|
||||
},
|
||||
"delete": {
|
||||
"function": "Delete toot",
|
||||
"button": "Delete this toot"
|
||||
"button": "Delete this toot",
|
||||
"alert": {
|
||||
"title": "Confirm deleting toot?",
|
||||
"message": "Are you sure to delete this toot? All boosts and favourites will be cleared, including all replies.",
|
||||
"buttons": {
|
||||
"confirm": "Confirm deleting",
|
||||
"cancel": "$t(common:buttons.cancel)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"deleteEdit": {
|
||||
"function": "Delete toot",
|
||||
"button": "Delete and re-draft",
|
||||
"alert": {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"default": "Toot",
|
||||
"conversation": "Toot DM",
|
||||
"reply": "Toot reply",
|
||||
"deleteEdit": "Toot",
|
||||
"edit": "Toot"
|
||||
},
|
||||
"alert": {
|
||||
|
|
|
@ -160,7 +160,7 @@
|
|||
"function": "툿 삭제",
|
||||
"button": "이 툿 삭제"
|
||||
},
|
||||
"edit": {
|
||||
"deleteEdit": {
|
||||
"function": "툿 삭제",
|
||||
"button": "삭제하고 다시 쓰기",
|
||||
"alert": {
|
||||
|
|
|
@ -162,7 +162,7 @@
|
|||
"function": "Xóa tút",
|
||||
"button": "Xóa tút này"
|
||||
},
|
||||
"edit": {
|
||||
"deleteEdit": {
|
||||
"function": "Xóa tút",
|
||||
"button": "Xóa và viết lại",
|
||||
"alert": {
|
||||
|
|
|
@ -162,7 +162,7 @@
|
|||
"function": "删除",
|
||||
"button": "删除此条嘟文"
|
||||
},
|
||||
"edit": {
|
||||
"deleteEdit": {
|
||||
"function": "删除",
|
||||
"button": "删除并重新编辑此条嘟文",
|
||||
"alert": {
|
||||
|
|
|
@ -14,6 +14,8 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||
import apiInstance from '@api/instance'
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||
|
||||
export interface Props {
|
||||
navigation: NativeStackNavigationProp<RootStackParamList, 'Screen-Actions'>
|
||||
|
@ -59,22 +61,89 @@ const ActionsStatus: React.FC<Props> = ({
|
|||
}
|
||||
})
|
||||
|
||||
const canEditPost = useSelector(checkInstanceFeature('edit_post'))
|
||||
|
||||
return (
|
||||
<MenuContainer>
|
||||
<MenuHeader heading={t('shared.header.actions.status.heading')} />
|
||||
{canEditPost ? (
|
||||
<MenuRow
|
||||
onPress={async () => {
|
||||
analytics('timeline_shared_headeractions_status_edit_press', {
|
||||
page: queryKey && queryKey[1].page
|
||||
})
|
||||
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||
if (status.in_reply_to_id) {
|
||||
replyToStatus = await apiInstance<Mastodon.Status>({
|
||||
method: 'get',
|
||||
url: `statuses/${status.in_reply_to_id}`
|
||||
}).then(res => res.body)
|
||||
}
|
||||
apiInstance<{
|
||||
id: Mastodon.Status['id']
|
||||
text: NonNullable<Mastodon.Status['text']>
|
||||
spoiler_text: Mastodon.Status['spoiler_text']
|
||||
}>({
|
||||
method: 'get',
|
||||
url: `statuses/${status.id}/source`
|
||||
}).then(res => {
|
||||
dismiss()
|
||||
navigation.navigate('Screen-Compose', {
|
||||
type: 'edit',
|
||||
incomingStatus: {
|
||||
...status,
|
||||
text: res.body.text,
|
||||
spoiler_text: res.body.spoiler_text
|
||||
},
|
||||
...(replyToStatus && { replyToStatus }),
|
||||
queryKey,
|
||||
rootQueryKey
|
||||
})
|
||||
})
|
||||
}}
|
||||
iconFront='Edit3'
|
||||
title={t('shared.header.actions.status.edit.button')}
|
||||
/>
|
||||
) : null}
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
analytics('timeline_shared_headeractions_status_delete_press', {
|
||||
page: queryKey && queryKey[1].page
|
||||
})
|
||||
dismiss()
|
||||
mutation.mutate({
|
||||
type: 'deleteItem',
|
||||
source: 'statuses',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id
|
||||
})
|
||||
Alert.alert(
|
||||
t('shared.header.actions.status.delete.alert.title'),
|
||||
t('shared.header.actions.status.delete.alert.message'),
|
||||
[
|
||||
{
|
||||
text: t(
|
||||
'shared.header.actions.status.delete.alert.buttons.cancel'
|
||||
),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: t(
|
||||
'shared.header.actions.status.delete.alert.buttons.confirm'
|
||||
),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
analytics(
|
||||
'timeline_shared_headeractions_status_delete_confirm',
|
||||
{
|
||||
page: queryKey && queryKey[1].page
|
||||
}
|
||||
)
|
||||
dismiss()
|
||||
mutation.mutate({
|
||||
type: 'deleteItem',
|
||||
source: 'statuses',
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
id: status.id
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
)
|
||||
}}
|
||||
iconFront='Trash'
|
||||
title={t('shared.header.actions.status.delete.button')}
|
||||
|
@ -85,18 +154,18 @@ const ActionsStatus: React.FC<Props> = ({
|
|||
page: queryKey && queryKey[1].page
|
||||
})
|
||||
Alert.alert(
|
||||
t('shared.header.actions.status.edit.alert.title'),
|
||||
t('shared.header.actions.status.edit.alert.message'),
|
||||
t('shared.header.actions.status.deleteEdit.alert.title'),
|
||||
t('shared.header.actions.status.deleteEdit.alert.message'),
|
||||
[
|
||||
{
|
||||
text: t(
|
||||
'shared.header.actions.status.edit.alert.buttons.cancel'
|
||||
'shared.header.actions.status.deleteEdit.alert.buttons.cancel'
|
||||
),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: t(
|
||||
'shared.header.actions.status.edit.alert.buttons.confirm'
|
||||
'shared.header.actions.status.deleteEdit.alert.buttons.confirm'
|
||||
),
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
|
@ -106,7 +175,7 @@ const ActionsStatus: React.FC<Props> = ({
|
|||
page: queryKey && queryKey[1].page
|
||||
}
|
||||
)
|
||||
let replyToStatus: Mastodon.Status
|
||||
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||
if (status.in_reply_to_id) {
|
||||
replyToStatus = await apiInstance<Mastodon.Status>({
|
||||
method: 'get',
|
||||
|
@ -122,10 +191,9 @@ const ActionsStatus: React.FC<Props> = ({
|
|||
})
|
||||
.then(res => {
|
||||
dismiss()
|
||||
// @ts-ignore
|
||||
navigation.navigate('Screen-Compose', {
|
||||
type: 'edit',
|
||||
incomingStatus: res.body,
|
||||
type: 'deleteEdit',
|
||||
incomingStatus: res.body as Mastodon.Status,
|
||||
...(replyToStatus && { replyToStatus }),
|
||||
queryKey
|
||||
})
|
||||
|
@ -136,7 +204,7 @@ const ActionsStatus: React.FC<Props> = ({
|
|||
)
|
||||
}}
|
||||
iconFront='Edit'
|
||||
title={t('shared.header.actions.status.edit.button')}
|
||||
title={t('shared.header.actions.status.deleteEdit.button')}
|
||||
/>
|
||||
<MenuRow
|
||||
onPress={() => {
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import analytics from '@components/analytics'
|
||||
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
|
||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||
import haptics from '@root/components/haptics'
|
||||
import formatText from '@screens/Compose/formatText'
|
||||
import ComposeRoot from '@screens/Compose/Root'
|
||||
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||
import {
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
} from '@utils/queryHooks/timeline'
|
||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||
import {
|
||||
getInstanceAccount,
|
||||
|
@ -133,6 +136,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||
useEffect(() => {
|
||||
switch (params?.type) {
|
||||
case 'edit':
|
||||
case 'deleteEdit':
|
||||
if (params.incomingStatus.spoiler_text) {
|
||||
formatText({
|
||||
textInput: 'spoiler',
|
||||
|
@ -268,6 +272,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||
}
|
||||
return false
|
||||
}, [totalTextCount, composeState.attachments.uploads, composeState.text.raw])
|
||||
const mutateTimeline = useTimelineMutation({ onMutate: true })
|
||||
const headerRight = useCallback(
|
||||
() => (
|
||||
<HeaderRight
|
||||
|
@ -282,7 +287,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||
composeDispatch({ type: 'posting', payload: true })
|
||||
|
||||
composePost(params, composeState)
|
||||
.then(() => {
|
||||
.then(res => {
|
||||
haptics('Success')
|
||||
if (
|
||||
Platform.OS === 'ios' &&
|
||||
|
@ -300,6 +305,15 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||
|
||||
switch (params?.type) {
|
||||
case 'edit':
|
||||
console.log('firing mutation')
|
||||
mutateTimeline.mutate({
|
||||
type: 'editItem',
|
||||
queryKey: params.queryKey,
|
||||
rootQueryKey: params.rootQueryKey,
|
||||
status: res.body
|
||||
})
|
||||
break
|
||||
case 'deleteEdit':
|
||||
case 'reply':
|
||||
if (params?.queryKey && params.queryKey[1].page === 'Toot') {
|
||||
queryClient.invalidateQueries(params.queryKey)
|
||||
|
|
|
@ -39,6 +39,7 @@ const composeParseState = (
|
|||
): ComposeState => {
|
||||
switch (params.type) {
|
||||
case 'edit':
|
||||
case 'deleteEdit':
|
||||
return {
|
||||
...composeInitialState,
|
||||
dirty: true,
|
||||
|
|
|
@ -51,8 +51,11 @@ const composePost = async (
|
|||
formData.append('visibility', composeState.visibility)
|
||||
|
||||
return apiInstance<Mastodon.Status>({
|
||||
method: 'post',
|
||||
url: 'statuses',
|
||||
method: params?.type === 'edit' ? 'put' : 'post',
|
||||
url:
|
||||
params?.type === 'edit'
|
||||
? `statuses/${params.incomingStatus.id}`
|
||||
: 'statuses',
|
||||
headers: {
|
||||
'Idempotency-Key': await Crypto.digestStringAsync(
|
||||
Crypto.CryptoDigestAlgorithm.SHA256,
|
||||
|
@ -67,7 +70,9 @@ const composePost = async (
|
|||
composeState.attachments.sensitive +
|
||||
composeState.attachments.uploads.map(upload => upload.remote?.id) +
|
||||
composeState.visibility +
|
||||
(params?.type === 'edit' ? Math.random() : '')
|
||||
(params?.type === 'edit' || params?.type === 'deleteEdit'
|
||||
? Math.random()
|
||||
: '')
|
||||
)
|
||||
},
|
||||
body: formData
|
||||
|
|
|
@ -26,31 +26,20 @@ export type RootStackParamList = {
|
|||
type: 'edit'
|
||||
incomingStatus: Mastodon.Status
|
||||
replyToStatus?: Mastodon.Status
|
||||
queryKey?: [
|
||||
'Timeline',
|
||||
{
|
||||
page: App.Pages
|
||||
hashtag?: Mastodon.Tag['name']
|
||||
list?: Mastodon.List['id']
|
||||
toot?: Mastodon.Status['id']
|
||||
account?: Mastodon.Account['id']
|
||||
}
|
||||
]
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
}
|
||||
| {
|
||||
type: 'deleteEdit'
|
||||
incomingStatus: Mastodon.Status
|
||||
replyToStatus?: Mastodon.Status
|
||||
queryKey?: QueryKeyTimeline
|
||||
}
|
||||
| {
|
||||
type: 'reply'
|
||||
incomingStatus: Mastodon.Status
|
||||
accts: Mastodon.Account['acct'][]
|
||||
queryKey?: [
|
||||
'Timeline',
|
||||
{
|
||||
page: App.Pages
|
||||
hashtag?: Mastodon.Tag['name']
|
||||
list?: Mastodon.List['id']
|
||||
toot?: Mastodon.Status['id']
|
||||
account?: Mastodon.Account['id']
|
||||
}
|
||||
]
|
||||
queryKey?: QueryKeyTimeline
|
||||
}
|
||||
| {
|
||||
type: 'conversation'
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
useMutation
|
||||
} from 'react-query'
|
||||
import deleteItem from './timeline/deleteItem'
|
||||
import editItem from './timeline/editItem'
|
||||
import updateStatusProperty from './timeline/updateStatusProperty'
|
||||
|
||||
export type QueryKeyTimeline = [
|
||||
|
@ -303,13 +304,21 @@ export type MutationVarsTimelineUpdateAccountProperty = {
|
|||
}
|
||||
}
|
||||
|
||||
export type MutationVarsTimelineEditItem = {
|
||||
// This is for editing status
|
||||
type: 'editItem'
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
status: Mastodon.Status
|
||||
}
|
||||
|
||||
export type MutationVarsTimelineDeleteItem = {
|
||||
// This is for deleting status and conversation
|
||||
type: 'deleteItem'
|
||||
source: 'statuses' | 'conversations'
|
||||
queryKey?: QueryKeyTimeline
|
||||
rootQueryKey?: QueryKeyTimeline
|
||||
id: Mastodon.Conversation['id']
|
||||
id: Mastodon.Status['id']
|
||||
}
|
||||
|
||||
export type MutationVarsTimelineDomainBlock = {
|
||||
|
@ -322,6 +331,7 @@ export type MutationVarsTimelineDomainBlock = {
|
|||
export type MutationVarsTimeline =
|
||||
| MutationVarsTimelineUpdateStatusProperty
|
||||
| MutationVarsTimelineUpdateAccountProperty
|
||||
| MutationVarsTimelineEditItem
|
||||
| MutationVarsTimelineDeleteItem
|
||||
| MutationVarsTimelineDomainBlock
|
||||
|
||||
|
@ -371,6 +381,8 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||
}
|
||||
})
|
||||
}
|
||||
case 'editItem':
|
||||
return { body: params.status }
|
||||
case 'deleteItem':
|
||||
return apiInstance<Mastodon.Conversation>({
|
||||
method: 'delete',
|
||||
|
@ -423,6 +435,9 @@ const useTimelineMutation = ({
|
|||
case 'updateStatusProperty':
|
||||
updateStatusProperty(params)
|
||||
break
|
||||
case 'editItem':
|
||||
editItem(params)
|
||||
break
|
||||
case 'deleteItem':
|
||||
deleteItem(params)
|
||||
break
|
||||
|
|
|
@ -6,11 +6,7 @@ const deleteItem = ({
|
|||
queryKey,
|
||||
rootQueryKey,
|
||||
id
|
||||
}: {
|
||||
queryKey?: MutationVarsTimelineDeleteItem['queryKey']
|
||||
rootQueryKey?: MutationVarsTimelineDeleteItem['rootQueryKey']
|
||||
id: MutationVarsTimelineDeleteItem['id']
|
||||
}) => {
|
||||
}: MutationVarsTimelineDeleteItem) => {
|
||||
queryKey &&
|
||||
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => {
|
||||
if (old) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import queryClient from '@helpers/queryClient'
|
||||
import { InfiniteData } from 'react-query'
|
||||
import { MutationVarsTimelineEditItem } from '../timeline'
|
||||
|
||||
const editItem = ({
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
status
|
||||
}: MutationVarsTimelineEditItem) => {
|
||||
console.log('START')
|
||||
queryKey &&
|
||||
queryClient.setQueryData<InfiniteData<any> | undefined>(queryKey, old => {
|
||||
if (old) {
|
||||
old.pages = old.pages.map(page => {
|
||||
page.body = page.body.map((item: Mastodon.Status) => {
|
||||
if (item.id === status.id) {
|
||||
console.log('found queryKey', queryKey)
|
||||
console.log('new content', status.content)
|
||||
item = status
|
||||
}
|
||||
return item
|
||||
})
|
||||
return page
|
||||
})
|
||||
return old
|
||||
}
|
||||
})
|
||||
|
||||
rootQueryKey &&
|
||||
queryClient.setQueryData<InfiniteData<any> | undefined>(
|
||||
rootQueryKey,
|
||||
old => {
|
||||
if (old) {
|
||||
old.pages = old.pages.map(page => {
|
||||
page.body = page.body.map((item: Mastodon.Status) => {
|
||||
if (item.id === status.id) {
|
||||
console.log('found rootQueryKey', queryKey)
|
||||
console.log('new content', status.content)
|
||||
item = status
|
||||
}
|
||||
return item
|
||||
})
|
||||
return page
|
||||
})
|
||||
return old
|
||||
}
|
||||
}
|
||||
)
|
||||
console.log('EDN')
|
||||
}
|
||||
|
||||
export default editItem
|
|
@ -14,13 +14,7 @@ const updateStatusProperty = ({
|
|||
id,
|
||||
reblog,
|
||||
payload
|
||||
}: {
|
||||
queryKey: MutationVarsTimelineUpdateStatusProperty['queryKey']
|
||||
rootQueryKey?: MutationVarsTimelineUpdateStatusProperty['rootQueryKey']
|
||||
id: MutationVarsTimelineUpdateStatusProperty['id']
|
||||
reblog?: MutationVarsTimelineUpdateStatusProperty['reblog']
|
||||
payload: MutationVarsTimelineUpdateStatusProperty['payload']
|
||||
}) => {
|
||||
}: MutationVarsTimelineUpdateStatusProperty) => {
|
||||
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(
|
||||
queryKey,
|
||||
old => {
|
||||
|
|
|
@ -344,14 +344,16 @@ export const getInstanceVersion = ({ instances: { instances } }: RootState) =>
|
|||
instances[findInstanceActive(instances)]?.version
|
||||
export const checkInstanceFeature =
|
||||
(feature: string) =>
|
||||
({ instances: { instances } }: RootState) => {
|
||||
return features
|
||||
.filter(f => f.feature === feature)
|
||||
.filter(
|
||||
f =>
|
||||
parseFloat(instances[findInstanceActive(instances)]?.version) >=
|
||||
f.version
|
||||
)
|
||||
({ instances: { instances } }: RootState): Boolean => {
|
||||
return (
|
||||
features
|
||||
.filter(f => f.feature === feature)
|
||||
.filter(
|
||||
f =>
|
||||
parseFloat(instances[findInstanceActive(instances)]?.version) >=
|
||||
f.version
|
||||
).length > 0
|
||||
)
|
||||
}
|
||||
|
||||
/* Get Instance Configuration */
|
||||
|
|
Loading…
Reference in New Issue