1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Simplify update toot logic

This commit is contained in:
xmflsct
2023-01-01 18:37:05 +01:00
parent 56d1090ca9
commit 2705b4b804
12 changed files with 114 additions and 310 deletions

View File

@ -87,9 +87,9 @@ const TimelineDefault: React.FC<Props> = ({
const main = () => ( const main = () => (
<> <>
{item.reblog ? ( {item.reblog ? (
<TimelineActioned action='reblog' /> <TimelineActioned action='reblog' rootStatus={item} />
) : item._pinned ? ( ) : item._pinned ? (
<TimelineActioned action='pinned' /> <TimelineActioned action='pinned' rootStatus={item} />
) : null} ) : null}
<View <View
@ -167,7 +167,6 @@ const TimelineDefault: React.FC<Props> = ({
queryKey, queryKey,
rootQueryKey, rootQueryKey,
status, status,
reblogStatus: item.reblog ? item : undefined,
ownAccount, ownAccount,
spoilerHidden, spoilerHidden,
rawContent, rawContent,

View File

@ -61,6 +61,7 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
action={notification.type} action={notification.type}
isNotification isNotification
account={notification.account} account={notification.account}
rootStatus={notification.status}
/> />
) : null} ) : null}

View File

@ -14,11 +14,12 @@ export interface Props {
action: Mastodon.Notification['type'] | 'reblog' | 'pinned' action: Mastodon.Notification['type'] | 'reblog' | 'pinned'
isNotification?: boolean isNotification?: boolean
account?: Mastodon.Account // For notification account?: Mastodon.Account // For notification
rootStatus?: Mastodon.Status
} }
const TimelineActioned: React.FC<Props> = ({ action, isNotification, ...rest }) => { const TimelineActioned: React.FC<Props> = ({ action, isNotification, ...rest }) => {
const { status, reblogStatus } = useContext(StatusContext) const { status } = useContext(StatusContext)
const account = rest.account || (reblogStatus ? reblogStatus.account : status?.account) const account = rest.account || (rest.rootStatus || status)?.account
if (!account) return null if (!account) return null
const { t } = useTranslation('componentTimeline') const { t } = useTranslation('componentTimeline')

View File

@ -22,7 +22,7 @@ import { Pressable, StyleSheet, View } from 'react-native'
import StatusContext from './Context' import StatusContext from './Context'
const TimelineActions: React.FC = () => { const TimelineActions: React.FC = () => {
const { queryKey, rootQueryKey, status, reblogStatus, ownAccount, highlighted, disableDetails } = const { queryKey, rootQueryKey, status, ownAccount, highlighted, disableDetails } =
useContext(StatusContext) useContext(StatusContext)
if (!queryKey || !status || disableDetails) return null if (!queryKey || !status || disableDetails) return null
@ -38,16 +38,16 @@ const TimelineActions: React.FC = () => {
const theParams = params as MutationVarsTimelineUpdateStatusProperty const theParams = params as MutationVarsTimelineUpdateStatusProperty
if ( if (
// Un-bookmark from bookmarks page // Un-bookmark from bookmarks page
(queryKey[1].page === 'Bookmarks' && theParams.payload.property === 'bookmarked') || (queryKey[1].page === 'Bookmarks' && theParams.payload.type === 'bookmarked') ||
// Un-favourite from favourites page // Un-favourite from favourites page
(queryKey[1].page === 'Favourites' && theParams.payload.property === 'favourited') (queryKey[1].page === 'Favourites' && theParams.payload.type === 'favourited')
) { ) {
queryClient.invalidateQueries(queryKey) queryClient.invalidateQueries(queryKey)
} else if (theParams.payload.property === 'favourited') { } else if (theParams.payload.type === 'favourited') {
// When favourited, update favourited page // When favourited, update favourited page
const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }] const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }]
queryClient.invalidateQueries(tempQueryKey) queryClient.invalidateQueries(tempQueryKey)
} else if (theParams.payload.property === 'bookmarked') { } else if (theParams.payload.type === 'bookmarked') {
// When bookmarked, update bookmark page // When bookmarked, update bookmark page
const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }] const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }]
queryClient.invalidateQueries(tempQueryKey) queryClient.invalidateQueries(tempQueryKey)
@ -60,7 +60,7 @@ const TimelineActions: React.FC = () => {
type: 'error', type: 'error',
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t( function: t(
`componentTimeline:shared.actions.${correctParam.payload.property}.function` as any `componentTimeline:shared.actions.${correctParam.payload.type}.function` as any
) )
}), }),
...(err.status && ...(err.status &&
@ -111,14 +111,9 @@ const TimelineActions: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
fetchRemoteURI: status._remote ? status.uri : undefined,
payload: { payload: {
property: 'reblogged', type: 'reblogged',
currentValue: status.reblogged,
propertyCount: 'reblogs_count',
countValue: status.reblogs_count,
visibility: 'public' visibility: 'public'
} }
}) })
@ -128,14 +123,9 @@ const TimelineActions: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
fetchRemoteURI: status._remote ? status.uri : undefined,
payload: { payload: {
property: 'reblogged', type: 'reblogged',
currentValue: status.reblogged,
propertyCount: 'reblogs_count',
countValue: status.reblogs_count,
visibility: 'unlisted' visibility: 'unlisted'
} }
}) })
@ -148,14 +138,9 @@ const TimelineActions: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
fetchRemoteURI: status._remote ? status.uri : undefined,
payload: { payload: {
property: 'reblogged', type: 'reblogged',
currentValue: status.reblogged,
propertyCount: 'reblogs_count',
countValue: status.reblogs_count,
visibility: 'public' visibility: 'public'
} }
}) })
@ -166,14 +151,9 @@ const TimelineActions: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
fetchRemoteURI: status._remote ? status.uri : undefined,
payload: { payload: {
property: 'favourited', type: 'favourited'
currentValue: status.favourited,
propertyCount: 'favourites_count',
countValue: status.favourites_count
} }
}) })
} }
@ -182,14 +162,9 @@ const TimelineActions: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
fetchRemoteURI: status._remote ? status.uri : undefined,
payload: { payload: {
property: 'bookmarked', type: 'bookmarked'
currentValue: status.bookmarked,
propertyCount: undefined,
countValue: undefined
} }
}) })
} }

View File

@ -9,7 +9,6 @@ type StatusContextType = {
status?: Mastodon.Status status?: Mastodon.Status
reblogStatus?: Mastodon.Status // When it is a reblog, pass the root status
ownAccount?: boolean ownAccount?: boolean
spoilerHidden?: boolean spoilerHidden?: boolean
rawContent?: React.MutableRefObject<string[]> // When highlighted, for translate, edit history rawContent?: React.MutableRefObject<string[]> // When highlighted, for translate, edit history

View File

@ -20,15 +20,8 @@ import { Pressable, View } from 'react-native'
import StatusContext from './Context' import StatusContext from './Context'
const TimelinePoll: React.FC = () => { const TimelinePoll: React.FC = () => {
const { const { queryKey, rootQueryKey, status, ownAccount, spoilerHidden, disableDetails } =
queryKey, useContext(StatusContext)
rootQueryKey,
status,
reblogStatus,
ownAccount,
spoilerHidden,
disableDetails
} = useContext(StatusContext)
if (!queryKey || !status || !status.poll) return null if (!queryKey || !status || !status.poll) return null
const poll = status.poll const poll = status.poll
@ -45,10 +38,9 @@ const TimelinePoll: React.FC = () => {
rootQueryKey && queryClient.cancelQueries(rootQueryKey) rootQueryKey && queryClient.cancelQueries(rootQueryKey)
haptics('Success') haptics('Success')
switch (theParams.payload.property) { switch (theParams.payload.type) {
case 'poll': case 'poll':
theParams.payload.data = body as unknown as Mastodon.Poll updateStatusProperty({ ...theParams, poll: body as unknown as Mastodon.Poll })
updateStatusProperty(theParams)
break break
} }
}, },
@ -84,12 +76,10 @@ const TimelinePoll: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
payload: { payload: {
property: 'poll', type: 'poll',
id: poll.id, action: 'vote',
type: 'vote',
options: allOptions options: allOptions
} }
}) })
@ -110,12 +100,10 @@ const TimelinePoll: React.FC = () => {
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
isReblog: !!reblogStatus,
payload: { payload: {
property: 'poll', type: 'poll',
id: poll.id, action: 'refresh'
type: 'refresh'
} }
}) })
} }
@ -230,13 +218,9 @@ const TimelinePoll: React.FC = () => {
const pollVoteCounts = () => { const pollVoteCounts = () => {
if (poll.voters_count !== null) { if (poll.voters_count !== null) {
return ( return t('componentTimeline:shared.poll.meta.count.voters', { count: poll.voters_count })
t('componentTimeline:shared.poll.meta.count.voters', { count: poll.voters_count }) + ' • '
)
} else if (poll.votes_count !== null) { } else if (poll.votes_count !== null) {
return ( return t('componentTimeline:shared.poll.meta.count.votes', { count: poll.votes_count })
t('componentTimeline:shared.poll.meta.count.votes', { count: poll.votes_count }) + ' • '
)
} }
} }
@ -246,11 +230,14 @@ const TimelinePoll: React.FC = () => {
} else { } else {
if (poll.expires_at) { if (poll.expires_at) {
return ( return (
<Trans <>
ns='componentTimeline' {' • '}
i18nKey='shared.poll.meta.expiration.until' <Trans
components={[<RelativeTime time={poll.expires_at} />]} ns='componentTimeline'
/> i18nKey='shared.poll.meta.expiration.until'
components={[<RelativeTime time={poll.expires_at} />]}
/>
</>
) )
} }
} }

View File

@ -35,7 +35,7 @@ const menuStatus = ({
onMutate: true, onMutate: true,
onError: (err: any, params, oldData) => { onError: (err: any, params, oldData) => {
const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload
? (params as MutationVarsTimelineUpdateStatusProperty).payload.property ? (params as MutationVarsTimelineUpdateStatusProperty).payload.type
: 'delete' : 'delete'
displayMessage({ displayMessage({
theme, theme,
@ -195,10 +195,9 @@ const menuStatus = ({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
payload: { payload: {
property: 'muted', type: 'muted'
currentValue: status.muted
} }
}), }),
disabled: false, disabled: false,
@ -220,12 +219,9 @@ const menuStatus = ({
type: 'updateStatusProperty', type: 'updateStatusProperty',
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id: status.id, status,
payload: { payload: {
property: 'pinned', type: 'pinned'
currentValue: status.pinned,
propertyCount: undefined,
countValue: undefined
} }
}), }),
disabled: false, disabled: false,

View File

@ -248,35 +248,23 @@ export type MutationVarsTimelineUpdateStatusProperty = {
type: 'updateStatusProperty' type: 'updateStatusProperty'
queryKey: QueryKeyTimeline queryKey: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline
id: Mastodon.Status['id'] | Mastodon.Poll['id'] status: Mastodon.Status
isReblog?: boolean
fetchRemoteURI?: Mastodon.Status['uri']
payload: payload:
| { | {
property: 'bookmarked' | 'muted' | 'pinned' type: 'bookmarked' | 'muted' | 'pinned' | 'favourited'
currentValue: boolean
propertyCount?: undefined
countValue?: undefined
} }
| { | {
property: 'favourited' type: 'reblogged'
currentValue: boolean
propertyCount: 'favourites_count' | 'reblogs_count'
countValue: number
}
| {
property: 'reblogged'
currentValue: boolean
propertyCount: 'favourites_count' | 'reblogs_count'
countValue: number
visibility: 'public' | 'unlisted' visibility: 'public' | 'unlisted'
} }
| { | {
property: 'poll' type: 'poll'
id: Mastodon.Poll['id'] action: 'vote'
type: 'vote' | 'refresh' options: boolean[]
options?: boolean[] }
data?: Mastodon.Poll | {
type: 'poll'
action: 'refresh'
} }
} }
@ -325,10 +313,10 @@ export type MutationVarsTimeline =
const mutationFunction = async (params: MutationVarsTimeline) => { const mutationFunction = async (params: MutationVarsTimeline) => {
switch (params.type) { switch (params.type) {
case 'updateStatusProperty': case 'updateStatusProperty':
switch (params.payload.property) { switch (params.payload.type) {
case 'poll': case 'poll':
const formData = new FormData() const formData = new FormData()
params.payload.type === 'vote' && params.payload.action === 'vote' &&
params.payload.options?.forEach((option, index) => { params.payload.options?.forEach((option, index) => {
if (option) { if (option) {
formData.append('choices[]', index.toString()) formData.append('choices[]', index.toString())
@ -336,17 +324,17 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
}) })
return apiInstance<Mastodon.Poll>({ return apiInstance<Mastodon.Poll>({
method: params.payload.type === 'vote' ? 'post' : 'get', method: params.payload.action === 'vote' ? 'post' : 'get',
url: url:
params.payload.type === 'vote' params.payload.action === 'vote'
? `polls/${params.payload.id}/votes` ? `polls/${params.status.poll?.id}/votes`
: `polls/${params.payload.id}`, : `polls/${params.status.poll?.id}`,
...(params.payload.type === 'vote' && { body: formData }) ...(params.payload.action === 'vote' && { body: formData })
}) })
default: default:
let tootId = params.id let tootId = params.status.id
if (params.fetchRemoteURI) { if (params.status._remote) {
const fetched = await searchFetchToot(params.fetchRemoteURI) const fetched = await searchFetchToot(params.status.uri)
if (fetched) { if (fetched) {
tootId = fetched.id tootId = fetched.id
} else { } else {
@ -354,15 +342,15 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
} }
} }
const body = new FormData() const body = new FormData()
if (params.payload.property === 'reblogged') { if (params.payload.type === 'reblogged') {
body.append('visibility', params.payload.visibility) body.append('visibility', params.payload.visibility)
} }
return apiInstance<Mastodon.Status>({ return apiInstance<Mastodon.Status>({
method: 'post', method: 'post',
url: `statuses/${tootId}/${params.payload.currentValue ? 'un' : ''}${ url: `statuses/${tootId}/${params.status[params.payload.type] ? '' : 'un'}${
MapPropertyToUrl[params.payload.property] MapPropertyToUrl[params.payload.type]
}`, }`,
...(params.payload.property === 'reblogged' && { body }) ...(params.payload.type === 'reblogged' && { body })
}) })
} }
case 'updateAccountProperty': case 'updateAccountProperty':

View File

@ -1,34 +0,0 @@
import { MutationVarsTimelineUpdateStatusProperty } from '@utils/queryHooks/timeline'
const updateConversation = ({
item,
payload
}: {
item: Mastodon.Conversation
payload: MutationVarsTimelineUpdateStatusProperty['payload']
}) => {
switch (payload.property) {
case 'poll':
if (item.last_status) {
item.last_status[payload.property] = payload.data
}
return item
default:
if (item.last_status) {
item.last_status[payload.property] =
typeof payload.currentValue === 'boolean'
? !payload.currentValue
: true
if (payload.propertyCount) {
if (typeof payload.currentValue === 'boolean' && payload.currentValue) {
item.last_status[payload.propertyCount] = payload.countValue - 1
} else {
item.last_status[payload.propertyCount] = payload.countValue + 1
}
}
}
return item
}
}
export default updateConversation

View File

@ -1,31 +0,0 @@
import { MutationVarsTimelineUpdateStatusProperty } from '@utils/queryHooks/timeline'
const updateNotification = ({
item,
payload
}: {
item: Mastodon.Notification
payload: MutationVarsTimelineUpdateStatusProperty['payload']
}) => {
switch (payload.property) {
case 'poll':
return item
default:
if (item.status) {
item.status[payload.property] =
typeof payload.currentValue === 'boolean'
? !payload.currentValue
: true
if (payload.propertyCount) {
if (typeof payload.currentValue === 'boolean' && payload.currentValue) {
item.status[payload.propertyCount] = payload.countValue - 1
} else {
item.status[payload.propertyCount] = payload.countValue + 1
}
}
}
return item
}
}
export default updateNotification

View File

@ -1,46 +0,0 @@
import { MutationVarsTimelineUpdateStatusProperty } from '@utils/queryHooks/timeline'
const updateStatus = ({
item,
isReblog,
payload
}: {
item: Mastodon.Status
isReblog?: boolean
payload: MutationVarsTimelineUpdateStatusProperty['payload']
}) => {
switch (payload.property) {
case 'poll':
if (isReblog) {
item.reblog!.poll = payload.data
} else {
item.poll = payload.data
}
break
default:
if (isReblog) {
item.reblog![payload.property] =
typeof payload.currentValue === 'boolean' ? !payload.currentValue : true
if (payload.propertyCount) {
if (typeof payload.currentValue === 'boolean' && payload.currentValue) {
item.reblog![payload.propertyCount] = payload.countValue - 1
} else {
item.reblog![payload.propertyCount] = payload.countValue + 1
}
}
} else {
item[payload.property] =
typeof payload.currentValue === 'boolean' ? !payload.currentValue : true
if (payload.propertyCount) {
if (typeof payload.currentValue === 'boolean' && payload.currentValue) {
item[payload.propertyCount] = payload.countValue - 1
} else {
item[payload.propertyCount] = payload.countValue + 1
}
}
}
return item
}
}
export default updateStatus

View File

@ -1,103 +1,72 @@
import { InfiniteData } from '@tanstack/react-query' import { InfiniteData } from '@tanstack/react-query'
import queryClient from '@utils/queryHooks' import queryClient from '@utils/queryHooks'
import { MutationVarsTimelineUpdateStatusProperty, TimelineData } from '../timeline' import { MutationVarsTimelineUpdateStatusProperty, TimelineData } from '../timeline'
import updateConversation from './update/conversation'
import updateNotification from './update/notification'
import updateStatus from './update/status'
const updateStatusProperty = ({ const updateStatusProperty = ({
queryKey, queryKey,
rootQueryKey, rootQueryKey,
id, status,
isReblog, payload,
payload poll
}: MutationVarsTimelineUpdateStatusProperty) => { }: MutationVarsTimelineUpdateStatusProperty & { poll?: Mastodon.Poll }) => {
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(queryKey, old => { for (const key of [queryKey, rootQueryKey]) {
if (old) { if (!key) continue
let foundToot = false
old.pages = old.pages.map(page => {
// Skip rest of the pages if any toot is found
if (foundToot) {
return page
} else {
if (typeof (page.body as Mastodon.Conversation[])[0].unread === 'boolean') {
const items = page.body as Mastodon.Conversation[]
const tootIndex = items.findIndex(({ last_status }) => last_status?.id === id)
if (tootIndex >= 0) {
foundToot = true
updateConversation({ item: items[tootIndex], payload })
}
return page
} else if (typeof (page.body as Mastodon.Notification[])[0].type === 'string') {
const items = page.body as Mastodon.Notification[]
const tootIndex = items.findIndex(({ status }) => status?.id === id)
if (tootIndex >= 0) {
foundToot = true
updateNotification({ item: items[tootIndex], payload })
}
} else {
const items = page.body as Mastodon.Status[]
const tootIndex = isReblog
? items.findIndex(({ reblog }) => reblog?.id === id)
: items.findIndex(toot => toot.id === id)
// if favourites page and notifications page, remove the item instead
if (tootIndex >= 0) {
foundToot = true
updateStatus({ item: items[tootIndex], isReblog, payload })
}
}
return page queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(key, old => {
}
})
}
return old
})
rootQueryKey &&
queryClient.setQueryData<InfiniteData<TimelineData> | undefined>(rootQueryKey, old => {
if (old) { if (old) {
let foundToot = false let foundToot: Mastodon.Status | undefined = undefined
old.pages = old.pages.map(page => { old.pages = old.pages.map(page => {
// Skip rest of the pages if any toot is found
if (foundToot) { if (foundToot) {
return page return page
} else { } else {
if (typeof (page.body as Mastodon.Conversation[])[0].unread === 'boolean') { if (typeof (page.body as Mastodon.Conversation[])[0].unread === 'boolean') {
const items = page.body as Mastodon.Conversation[] foundToot = (page.body as Mastodon.Conversation[]).find(
const tootIndex = items.findIndex(({ last_status }) => last_status?.id === id) ({ last_status }) => last_status?.id === status.id
if (tootIndex >= 0) { )?.last_status
foundToot = true
updateConversation({ item: items[tootIndex], payload })
}
return page return page
} else if (typeof (page.body as Mastodon.Notification[])[0].type === 'string') { } else if (typeof (page.body as Mastodon.Notification[])[0].type === 'string') {
const items = page.body as Mastodon.Notification[] foundToot = (page.body as Mastodon.Notification[]).find(
const tootIndex = items.findIndex(({ status }) => status?.id === id) no => no.status?.id === status.id
if (tootIndex >= 0) { )?.status
foundToot = true
updateNotification({ item: items[tootIndex], payload })
}
} else { } else {
const items = page.body as Mastodon.Status[] foundToot = (page.body as Mastodon.Status[]).find(toot => toot.id === status.id)
const tootIndex = isReblog
? items.findIndex(({ reblog }) => reblog?.id === id)
: items.findIndex(toot => toot.id === id)
// if favourites page and notifications page, remove the item instead
if (tootIndex >= 0) {
foundToot = true
updateStatus({ item: items[tootIndex], isReblog, payload })
}
} }
return page return page
} }
}) })
if (foundToot) {
enum MapPropertyToCount {
favourited = 'favourites_count',
reblogged = 'reblogs_count'
}
switch (payload.type) {
case 'poll':
status.poll = poll
break
default:
status[payload.type] =
typeof status[payload.type] === 'boolean' ? !status[payload.type] : true
switch (payload.type) {
case 'favourited':
case 'reblogged':
if (typeof status[payload.type] === 'boolean' && status[payload.type]) {
status[MapPropertyToCount[payload.type]]--
} else {
status[MapPropertyToCount[payload.type]]++
}
break
}
break
}
}
} }
return old return old
}) })
}
} }
export default updateStatusProperty export default updateStatusProperty