diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index ae264a35..48725b8a 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -87,9 +87,9 @@ const TimelineDefault: React.FC = ({ const main = () => ( <> {item.reblog ? ( - + ) : item._pinned ? ( - + ) : null} = ({ queryKey, rootQueryKey, status, - reblogStatus: item.reblog ? item : undefined, ownAccount, spoilerHidden, rawContent, diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index f82332f4..75b7b030 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -61,6 +61,7 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { action={notification.type} isNotification account={notification.account} + rootStatus={notification.status} /> ) : null} diff --git a/src/components/Timeline/Shared/Actioned.tsx b/src/components/Timeline/Shared/Actioned.tsx index 8e73de69..62b9e29c 100644 --- a/src/components/Timeline/Shared/Actioned.tsx +++ b/src/components/Timeline/Shared/Actioned.tsx @@ -14,11 +14,12 @@ export interface Props { action: Mastodon.Notification['type'] | 'reblog' | 'pinned' isNotification?: boolean account?: Mastodon.Account // For notification + rootStatus?: Mastodon.Status } const TimelineActioned: React.FC = ({ action, isNotification, ...rest }) => { - const { status, reblogStatus } = useContext(StatusContext) - const account = rest.account || (reblogStatus ? reblogStatus.account : status?.account) + const { status } = useContext(StatusContext) + const account = rest.account || (rest.rootStatus || status)?.account if (!account) return null const { t } = useTranslation('componentTimeline') diff --git a/src/components/Timeline/Shared/Actions.tsx b/src/components/Timeline/Shared/Actions.tsx index 33cb0f4d..7df190f0 100644 --- a/src/components/Timeline/Shared/Actions.tsx +++ b/src/components/Timeline/Shared/Actions.tsx @@ -22,7 +22,7 @@ import { Pressable, StyleSheet, View } from 'react-native' import StatusContext from './Context' const TimelineActions: React.FC = () => { - const { queryKey, rootQueryKey, status, reblogStatus, ownAccount, highlighted, disableDetails } = + const { queryKey, rootQueryKey, status, ownAccount, highlighted, disableDetails } = useContext(StatusContext) if (!queryKey || !status || disableDetails) return null @@ -38,16 +38,16 @@ const TimelineActions: React.FC = () => { const theParams = params as MutationVarsTimelineUpdateStatusProperty if ( // 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 - (queryKey[1].page === 'Favourites' && theParams.payload.property === 'favourited') + (queryKey[1].page === 'Favourites' && theParams.payload.type === 'favourited') ) { queryClient.invalidateQueries(queryKey) - } else if (theParams.payload.property === 'favourited') { + } else if (theParams.payload.type === 'favourited') { // When favourited, update favourited page const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }] queryClient.invalidateQueries(tempQueryKey) - } else if (theParams.payload.property === 'bookmarked') { + } else if (theParams.payload.type === 'bookmarked') { // When bookmarked, update bookmark page const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }] queryClient.invalidateQueries(tempQueryKey) @@ -60,7 +60,7 @@ const TimelineActions: React.FC = () => { type: 'error', message: t('common:message.error.message', { function: t( - `componentTimeline:shared.actions.${correctParam.payload.property}.function` as any + `componentTimeline:shared.actions.${correctParam.payload.type}.function` as any ) }), ...(err.status && @@ -111,14 +111,9 @@ const TimelineActions: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, - fetchRemoteURI: status._remote ? status.uri : undefined, + status, payload: { - property: 'reblogged', - currentValue: status.reblogged, - propertyCount: 'reblogs_count', - countValue: status.reblogs_count, + type: 'reblogged', visibility: 'public' } }) @@ -128,14 +123,9 @@ const TimelineActions: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, - fetchRemoteURI: status._remote ? status.uri : undefined, + status, payload: { - property: 'reblogged', - currentValue: status.reblogged, - propertyCount: 'reblogs_count', - countValue: status.reblogs_count, + type: 'reblogged', visibility: 'unlisted' } }) @@ -148,14 +138,9 @@ const TimelineActions: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, - fetchRemoteURI: status._remote ? status.uri : undefined, + status, payload: { - property: 'reblogged', - currentValue: status.reblogged, - propertyCount: 'reblogs_count', - countValue: status.reblogs_count, + type: 'reblogged', visibility: 'public' } }) @@ -166,14 +151,9 @@ const TimelineActions: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, - fetchRemoteURI: status._remote ? status.uri : undefined, + status, payload: { - property: 'favourited', - currentValue: status.favourited, - propertyCount: 'favourites_count', - countValue: status.favourites_count + type: 'favourited' } }) } @@ -182,14 +162,9 @@ const TimelineActions: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, - fetchRemoteURI: status._remote ? status.uri : undefined, + status, payload: { - property: 'bookmarked', - currentValue: status.bookmarked, - propertyCount: undefined, - countValue: undefined + type: 'bookmarked' } }) } diff --git a/src/components/Timeline/Shared/Context.tsx b/src/components/Timeline/Shared/Context.tsx index ea689fc2..cc23fb8c 100644 --- a/src/components/Timeline/Shared/Context.tsx +++ b/src/components/Timeline/Shared/Context.tsx @@ -9,7 +9,6 @@ type StatusContextType = { status?: Mastodon.Status - reblogStatus?: Mastodon.Status // When it is a reblog, pass the root status ownAccount?: boolean spoilerHidden?: boolean rawContent?: React.MutableRefObject // When highlighted, for translate, edit history diff --git a/src/components/Timeline/Shared/Poll.tsx b/src/components/Timeline/Shared/Poll.tsx index 7e5f1f3a..d2de4b53 100644 --- a/src/components/Timeline/Shared/Poll.tsx +++ b/src/components/Timeline/Shared/Poll.tsx @@ -20,15 +20,8 @@ import { Pressable, View } from 'react-native' import StatusContext from './Context' const TimelinePoll: React.FC = () => { - const { - queryKey, - rootQueryKey, - status, - reblogStatus, - ownAccount, - spoilerHidden, - disableDetails - } = useContext(StatusContext) + const { queryKey, rootQueryKey, status, ownAccount, spoilerHidden, disableDetails } = + useContext(StatusContext) if (!queryKey || !status || !status.poll) return null const poll = status.poll @@ -45,10 +38,9 @@ const TimelinePoll: React.FC = () => { rootQueryKey && queryClient.cancelQueries(rootQueryKey) haptics('Success') - switch (theParams.payload.property) { + switch (theParams.payload.type) { case 'poll': - theParams.payload.data = body as unknown as Mastodon.Poll - updateStatusProperty(theParams) + updateStatusProperty({ ...theParams, poll: body as unknown as Mastodon.Poll }) break } }, @@ -84,12 +76,10 @@ const TimelinePoll: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, + status, payload: { - property: 'poll', - id: poll.id, - type: 'vote', + type: 'poll', + action: 'vote', options: allOptions } }) @@ -110,12 +100,10 @@ const TimelinePoll: React.FC = () => { type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, - isReblog: !!reblogStatus, + status, payload: { - property: 'poll', - id: poll.id, - type: 'refresh' + type: 'poll', + action: 'refresh' } }) } @@ -230,13 +218,9 @@ const TimelinePoll: React.FC = () => { const pollVoteCounts = () => { if (poll.voters_count !== null) { - return ( - t('componentTimeline:shared.poll.meta.count.voters', { count: poll.voters_count }) + ' • ' - ) + return t('componentTimeline:shared.poll.meta.count.voters', { count: poll.voters_count }) } else if (poll.votes_count !== null) { - return ( - t('componentTimeline:shared.poll.meta.count.votes', { count: poll.votes_count }) + ' • ' - ) + return t('componentTimeline:shared.poll.meta.count.votes', { count: poll.votes_count }) } } @@ -246,11 +230,14 @@ const TimelinePoll: React.FC = () => { } else { if (poll.expires_at) { return ( - ]} - /> + <> + {' • '} + ]} + /> + ) } } diff --git a/src/components/contextMenu/status.ts b/src/components/contextMenu/status.ts index 33b149e7..b36a7042 100644 --- a/src/components/contextMenu/status.ts +++ b/src/components/contextMenu/status.ts @@ -35,7 +35,7 @@ const menuStatus = ({ onMutate: true, onError: (err: any, params, oldData) => { const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload - ? (params as MutationVarsTimelineUpdateStatusProperty).payload.property + ? (params as MutationVarsTimelineUpdateStatusProperty).payload.type : 'delete' displayMessage({ theme, @@ -195,10 +195,9 @@ const menuStatus = ({ type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, + status, payload: { - property: 'muted', - currentValue: status.muted + type: 'muted' } }), disabled: false, @@ -220,12 +219,9 @@ const menuStatus = ({ type: 'updateStatusProperty', queryKey, rootQueryKey, - id: status.id, + status, payload: { - property: 'pinned', - currentValue: status.pinned, - propertyCount: undefined, - countValue: undefined + type: 'pinned' } }), disabled: false, diff --git a/src/utils/queryHooks/timeline.ts b/src/utils/queryHooks/timeline.ts index e848b8e9..be798429 100644 --- a/src/utils/queryHooks/timeline.ts +++ b/src/utils/queryHooks/timeline.ts @@ -248,35 +248,23 @@ export type MutationVarsTimelineUpdateStatusProperty = { type: 'updateStatusProperty' queryKey: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline - id: Mastodon.Status['id'] | Mastodon.Poll['id'] - isReblog?: boolean - fetchRemoteURI?: Mastodon.Status['uri'] + status: Mastodon.Status payload: | { - property: 'bookmarked' | 'muted' | 'pinned' - currentValue: boolean - propertyCount?: undefined - countValue?: undefined + type: 'bookmarked' | 'muted' | 'pinned' | 'favourited' } | { - property: 'favourited' - currentValue: boolean - propertyCount: 'favourites_count' | 'reblogs_count' - countValue: number - } - | { - property: 'reblogged' - currentValue: boolean - propertyCount: 'favourites_count' | 'reblogs_count' - countValue: number + type: 'reblogged' visibility: 'public' | 'unlisted' } | { - property: 'poll' - id: Mastodon.Poll['id'] - type: 'vote' | 'refresh' - options?: boolean[] - data?: Mastodon.Poll + type: 'poll' + action: 'vote' + options: boolean[] + } + | { + type: 'poll' + action: 'refresh' } } @@ -325,10 +313,10 @@ export type MutationVarsTimeline = const mutationFunction = async (params: MutationVarsTimeline) => { switch (params.type) { case 'updateStatusProperty': - switch (params.payload.property) { + switch (params.payload.type) { case 'poll': const formData = new FormData() - params.payload.type === 'vote' && + params.payload.action === 'vote' && params.payload.options?.forEach((option, index) => { if (option) { formData.append('choices[]', index.toString()) @@ -336,17 +324,17 @@ const mutationFunction = async (params: MutationVarsTimeline) => { }) return apiInstance({ - method: params.payload.type === 'vote' ? 'post' : 'get', + method: params.payload.action === 'vote' ? 'post' : 'get', url: - params.payload.type === 'vote' - ? `polls/${params.payload.id}/votes` - : `polls/${params.payload.id}`, - ...(params.payload.type === 'vote' && { body: formData }) + params.payload.action === 'vote' + ? `polls/${params.status.poll?.id}/votes` + : `polls/${params.status.poll?.id}`, + ...(params.payload.action === 'vote' && { body: formData }) }) default: - let tootId = params.id - if (params.fetchRemoteURI) { - const fetched = await searchFetchToot(params.fetchRemoteURI) + let tootId = params.status.id + if (params.status._remote) { + const fetched = await searchFetchToot(params.status.uri) if (fetched) { tootId = fetched.id } else { @@ -354,15 +342,15 @@ const mutationFunction = async (params: MutationVarsTimeline) => { } } const body = new FormData() - if (params.payload.property === 'reblogged') { + if (params.payload.type === 'reblogged') { body.append('visibility', params.payload.visibility) } return apiInstance({ method: 'post', - url: `statuses/${tootId}/${params.payload.currentValue ? 'un' : ''}${ - MapPropertyToUrl[params.payload.property] + url: `statuses/${tootId}/${params.status[params.payload.type] ? '' : 'un'}${ + MapPropertyToUrl[params.payload.type] }`, - ...(params.payload.property === 'reblogged' && { body }) + ...(params.payload.type === 'reblogged' && { body }) }) } case 'updateAccountProperty': diff --git a/src/utils/queryHooks/timeline/update/conversation.ts b/src/utils/queryHooks/timeline/update/conversation.ts deleted file mode 100644 index 9211bd4a..00000000 --- a/src/utils/queryHooks/timeline/update/conversation.ts +++ /dev/null @@ -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 diff --git a/src/utils/queryHooks/timeline/update/notification.ts b/src/utils/queryHooks/timeline/update/notification.ts deleted file mode 100644 index 1d652e77..00000000 --- a/src/utils/queryHooks/timeline/update/notification.ts +++ /dev/null @@ -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 diff --git a/src/utils/queryHooks/timeline/update/status.ts b/src/utils/queryHooks/timeline/update/status.ts deleted file mode 100644 index c6b0b5f4..00000000 --- a/src/utils/queryHooks/timeline/update/status.ts +++ /dev/null @@ -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 diff --git a/src/utils/queryHooks/timeline/updateStatusProperty.ts b/src/utils/queryHooks/timeline/updateStatusProperty.ts index fab61536..da0b9ee4 100644 --- a/src/utils/queryHooks/timeline/updateStatusProperty.ts +++ b/src/utils/queryHooks/timeline/updateStatusProperty.ts @@ -1,103 +1,72 @@ import { InfiniteData } from '@tanstack/react-query' import queryClient from '@utils/queryHooks' import { MutationVarsTimelineUpdateStatusProperty, TimelineData } from '../timeline' -import updateConversation from './update/conversation' -import updateNotification from './update/notification' -import updateStatus from './update/status' const updateStatusProperty = ({ queryKey, rootQueryKey, - id, - isReblog, - payload -}: MutationVarsTimelineUpdateStatusProperty) => { - queryClient.setQueryData | undefined>(queryKey, old => { - if (old) { - 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 }) - } - } + status, + payload, + poll +}: MutationVarsTimelineUpdateStatusProperty & { poll?: Mastodon.Poll }) => { + for (const key of [queryKey, rootQueryKey]) { + if (!key) continue - return page - } - }) - } - - return old - }) - - rootQueryKey && - queryClient.setQueryData | undefined>(rootQueryKey, old => { + queryClient.setQueryData | undefined>(key, old => { if (old) { - let foundToot = false + let foundToot: Mastodon.Status | undefined = undefined 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 }) - } + foundToot = (page.body as Mastodon.Conversation[]).find( + ({ last_status }) => last_status?.id === status.id + )?.last_status 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 }) - } + foundToot = (page.body as Mastodon.Notification[]).find( + no => no.status?.id === status.id + )?.status } 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 }) - } + foundToot = (page.body as Mastodon.Status[]).find(toot => toot.id === status.id) } 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 }) + } } export default updateStatusProperty