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

Using react-query 3

This commit is contained in:
Zhiyuan Zheng
2020-12-18 23:58:53 +01:00
parent b420c7b02b
commit 7491478176
32 changed files with 288 additions and 265 deletions

View File

@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
import { AppState, StyleSheet } from 'react-native'
import { setFocusHandler, useInfiniteQuery } from 'react-query'
import { StyleSheet } from 'react-native'
import { useInfiniteQuery } from 'react-query'
import TimelineNotifications from '@components/Timelines/Timeline/Notifications'
import TimelineDefault from '@components/Timelines/Timeline/Default'
@ -29,17 +29,7 @@ const Timeline: React.FC<Props> = ({
account,
disableRefresh = false
}) => {
setFocusHandler(handleFocus => {
const handleAppStateChange = (appState: string) => {
if (appState === 'active') {
handleFocus()
}
}
AppState.addEventListener('change', handleAppStateChange)
return () => AppState.removeEventListener('change', handleAppStateChange)
})
const queryKey: App.QueryKey = [
const queryKey: QueryKey.Timeline = [
page,
{
page,
@ -51,24 +41,38 @@ const Timeline: React.FC<Props> = ({
]
const {
status,
isSuccess,
isLoading,
isError,
isFetchingMore,
canFetchMore,
data,
fetchMore,
refetch
refetch,
hasPreviousPage,
fetchPreviousPage,
isFetchingPreviousPage,
hasNextPage,
fetchNextPage,
isFetchingNextPage
} = useInfiniteQuery(queryKey, timelineFetch, {
getFetchMore: last => last?.toots.length > 0
getPreviousPageParam: firstPage => ({
direction: 'prev',
id: firstPage.toots[0].id
}),
getNextPageParam: lastPage =>
lastPage.toots.length
? {
direction: 'next',
id: lastPage.toots[lastPage.toots.length - 1].id
}
: undefined
})
const flattenData = data ? data.flatMap(d => [...d?.toots]) : []
const flattenPointer = data ? data.flatMap(d => [d?.pointer]) : []
const flattenPinnedLength = data ? data.flatMap(d => [d?.pinnedLength]) : []
const flattenData = data?.pages ? data.pages.flatMap(d => [...d?.toots]) : []
const flattenPointer = data?.pages
? data.pages.flatMap(d => [d?.pointer])
: []
const flattenPinnedLength = data?.pages
? data.pages.flatMap(d => [d?.pinnedLength])
: []
const flRef = useRef<FlatList<any>>(null)
useEffect(() => {
if (toot && isSuccess) {
if (toot && status === 'success') {
setTimeout(() => {
flRef.current?.scrollToIndex({
index: flattenPointer[0]!,
@ -76,7 +80,7 @@ const Timeline: React.FC<Props> = ({
})
}, 500)
}
}, [isSuccess])
}, [status])
const flKeyExtrator = useCallback(({ id }) => id, [])
const flRenderItem = useCallback(({ item, index }) => {
@ -110,33 +114,16 @@ const Timeline: React.FC<Props> = ({
)
const flItemEmptyComponent = useMemo(
() => <TimelineEmpty status={status} refetch={refetch} />,
[isLoading, isError, isSuccess]
[status]
)
const flOnRefresh = useCallback(
() =>
!disableRefresh &&
fetchMore(
{
direction: 'prev',
id: flattenData.length ? flattenData[0].id : null
},
{ previous: true }
),
[flattenData]
)
const flOnEndReach = useCallback(
() =>
!disableRefresh &&
canFetchMore &&
fetchMore({
direction: 'next',
id: flattenData[flattenData.length - 1].id
}),
[flattenData]
() => !disableRefresh && fetchPreviousPage(),
[]
)
const flOnEndReach = useCallback(() => fetchNextPage(), [])
const flFooter = useCallback(() => {
return <TimelineEnd isFetchingMore={isFetchingMore} />
}, [isFetchingMore])
return <TimelineEnd hasNextPage={hasNextPage} />
}, [hasNextPage])
const onScrollToIndexFailed = useCallback(error => {
const offset = error.averageItemLength * error.index
flRef.current?.scrollToOffset({ offset })
@ -159,11 +146,11 @@ const Timeline: React.FC<Props> = ({
onEndReached={flOnEndReach}
keyExtractor={flKeyExtrator}
ListFooterComponent={flFooter}
refreshing={isFetchingPreviousPage}
ListEmptyComponent={flItemEmptyComponent}
ItemSeparatorComponent={flItemSeparatorComponent}
onEndReachedThreshold={!disableRefresh ? 0.75 : null}
refreshing={!disableRefresh && isLoading && flattenData.length > 0}
{...(toot && isSuccess && { onScrollToIndexFailed })}
{...(toot && status === 'success' && { onScrollToIndexFailed })}
/>
)
}

View File

@ -10,7 +10,7 @@ import TimelineActions from '@components/Timelines/Timeline/Shared/Actions'
export interface Props {
item: Mastodon.Conversation
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
highlighted?: boolean
}
// Unread and mark as unread

View File

@ -15,7 +15,7 @@ import { StyleConstants } from '@utils/styles/constants'
export interface Props {
item: Mastodon.Status
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
index: number
pinnedLength?: number
highlighted?: boolean

View File

@ -15,7 +15,7 @@ import { StyleConstants } from '@utils/styles/constants'
export interface Props {
notification: Mastodon.Notification
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
highlighted?: boolean
}

View File

@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from 'react'
import { ActionSheetIOS, Pressable, StyleSheet, Text, View } from 'react-native'
import { useMutation, useQueryCache } from 'react-query'
import { useMutation, useQueryClient } from 'react-query'
import { Feather } from '@expo/vector-icons'
import client from '@api/client'
@ -44,7 +44,7 @@ const fireMutation = async ({
}
export interface Props {
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
status: Mastodon.Status
}
@ -55,20 +55,21 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => {
const iconColorAction = (state: boolean) =>
state ? theme.primary : theme.secondary
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
const queryClient = useQueryClient()
const { mutate } = useMutation(fireMutation, {
onMutate: ({ id, type, stateKey, prevState }) => {
queryCache.cancelQueries(queryKey)
const oldData = queryCache.getQueryData(queryKey)
queryClient.cancelQueries(queryKey)
const oldData = queryClient.getQueryData(queryKey)
switch (type) {
case 'favourite':
case 'reblog':
case 'bookmark':
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
queryClient.setQueryData(queryKey, (old: any) => {
old.pages.map((paging: any) => ({
toots: paging.toots.map((toot: any) => {
if (toot.id === id) {
console.log(toot[stateKey])
toot[stateKey] =
typeof prevState === 'boolean' ? !prevState : true
}
@ -76,7 +77,8 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => {
}),
pointer: paging.pointer
}))
)
return old
})
break
}
@ -84,7 +86,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => {
},
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试' })
queryCache.setQueryData(queryKey, oldData)
queryClient.setQueryData(queryKey, oldData)
}
})
@ -99,7 +101,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => {
}, [])
const onPressReblog = useCallback(
() =>
mutateAction({
mutate({
id: status.id,
type: 'reblog',
stateKey: 'reblogged',
@ -109,7 +111,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => {
)
const onPressFavourite = useCallback(
() =>
mutateAction({
mutate({
id: status.id,
type: 'favourite',
stateKey: 'favourited',
@ -119,7 +121,7 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status }) => {
)
const onPressBookmark = useCallback(
() =>
mutateAction({
mutate({
id: status.id,
type: 'bookmark',
stateKey: 'bookmarked',

View File

@ -4,7 +4,7 @@ import { Video } from 'expo-av'
import { ButtonRound } from '@components/Button'
export interface Props {
media_attachments: Mastodon.AttachmentVideo[]
media_attachments: Mastodon.Attachment[]
width: number
}

View File

@ -4,7 +4,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useNavigation } from '@react-navigation/native'
export interface Props {
queryKey?: App.QueryKey
queryKey?: QueryKey.Timeline
account: Mastodon.Account
}

View File

@ -5,15 +5,15 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
export interface Props {
isFetchingMore: false | 'previous' | 'next' | undefined
hasNextPage?: boolean
}
const TimelineEnd: React.FC<Props> = ({ isFetchingMore }) => {
const TimelineEnd: React.FC<Props> = ({ hasNextPage }) => {
const { theme } = useTheme()
return (
<View style={styles.base}>
{isFetchingMore ? (
{hasNextPage ? (
<ActivityIndicator />
) : (
<Text style={[styles.text, { color: theme.secondary }]}>

View File

@ -1,7 +1,7 @@
import { Feather } from '@expo/vector-icons'
import React, { useCallback, useMemo } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { useMutation, useQueryCache } from 'react-query'
import { useMutation, useQueryClient } from 'react-query'
import client from '@api/client'
import { toast } from '@components/toast'
@ -11,7 +11,7 @@ import { useTheme } from '@utils/styles/ThemeManager'
import Emojis from '@components/Timelines/Timeline/Shared/Emojis'
export interface Props {
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
id: string
account: Mastodon.Account
created_at?: Mastodon.Status['created_at']
@ -43,14 +43,14 @@ const HeaderConversation: React.FC<Props> = ({
account,
created_at
}) => {
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
const queryClient = useQueryClient()
const { mutate } = useMutation(fireMutation, {
onMutate: () => {
queryCache.cancelQueries(queryKey)
const oldData = queryCache.getQueryData(queryKey)
queryClient.cancelQueries(queryKey)
const oldData = queryClient.getQueryData(queryKey)
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
queryClient.setQueryData(queryKey, (old: any) =>
old.pages.map((paging: any) => ({
toots: paging.toots.filter((toot: any) => toot.id !== id),
pointer: paging.pointer
}))
@ -60,16 +60,13 @@ const HeaderConversation: React.FC<Props> = ({
},
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试', autoHide: false })
queryCache.setQueryData(queryKey, oldData)
},
onSettled: () => {
queryCache.invalidateQueries(queryKey)
queryClient.setQueryData(queryKey, oldData)
}
})
const { theme } = useTheme()
const actionOnPress = useCallback(() => mutateAction({ id }), [])
const actionOnPress = useCallback(() => mutate({ id }), [])
const actionChildren = useMemo(
() => (

View File

@ -15,7 +15,7 @@ import HeaderDefaultActionsStatus from '@components/Timelines/Timeline/Shared/He
import HeaderDefaultActionsDomain from '@components/Timelines/Timeline/Shared/HeaderDefault/ActionsDomain'
export interface Props {
queryKey?: App.QueryKey
queryKey?: QueryKey.Timeline
status: Mastodon.Status
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useMutation, useQueryCache } from 'react-query'
import { useMutation, useQueryClient } from 'react-query'
import client from '@api/client'
import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu'
import { toast } from '@components/toast'
@ -56,7 +56,7 @@ const fireMutation = async ({
}
export interface Props {
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
accountId: string
account: string
setBottomSheetVisible: React.Dispatch<React.SetStateAction<boolean>>
@ -68,19 +68,16 @@ const HeaderDefaultActionsAccount: React.FC<Props> = ({
account,
setBottomSheetVisible
}) => {
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
const queryClient = useQueryClient()
const { mutate } = useMutation(fireMutation, {
onMutate: () => {
queryCache.cancelQueries(queryKey)
const oldData = queryCache.getQueryData(queryKey)
queryClient.cancelQueries(queryKey)
const oldData = queryClient.getQueryData(queryKey)
return oldData
},
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试', autoHide: false })
queryCache.setQueryData(queryKey, oldData)
},
onSettled: () => {
queryCache.invalidateQueries(queryKey)
queryClient.setQueryData(queryKey, oldData)
}
})
@ -90,7 +87,7 @@ const HeaderDefaultActionsAccount: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({
mutate({
type: 'mute',
id: accountId,
stateKey: 'muting'
@ -102,7 +99,7 @@ const HeaderDefaultActionsAccount: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({
mutate({
type: 'block',
id: accountId,
stateKey: 'blocking'
@ -114,7 +111,7 @@ const HeaderDefaultActionsAccount: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({
mutate({
type: 'reports',
id: accountId
})

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useMutation, useQueryCache } from 'react-query'
import { useMutation, useQueryClient } from 'react-query'
import client from '@api/client'
import MenuContainer from '@components/Menu/Container'
import MenuHeader from '@components/Menu/Header'
@ -30,7 +30,7 @@ const fireMutation = async ({ domain }: { domain: string }) => {
}
export interface Props {
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
domain: string
setBottomSheetVisible: React.Dispatch<React.SetStateAction<boolean>>
}
@ -40,19 +40,19 @@ const HeaderDefaultActionsDomain: React.FC<Props> = ({
domain,
setBottomSheetVisible
}) => {
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
const queryClient = useQueryClient()
const { mutate } = useMutation(fireMutation, {
onMutate: () => {
queryCache.cancelQueries(queryKey)
const oldData = queryCache.getQueryData(queryKey)
queryClient.cancelQueries(queryKey)
const oldData = queryClient.getQueryData(queryKey)
return oldData
},
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试', autoHide: false })
queryCache.setQueryData(queryKey, oldData)
queryClient.setQueryData(queryKey, oldData)
},
onSettled: () => {
queryCache.invalidateQueries(queryKey)
queryClient.invalidateQueries(queryKey)
}
})
@ -62,7 +62,7 @@ const HeaderDefaultActionsDomain: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({ domain })
mutate({ domain })
}}
iconFront='cloud-off'
title={`屏蔽域名 ${domain}`}

View File

@ -1,7 +1,7 @@
import { useNavigation } from '@react-navigation/native'
import React from 'react'
import { Alert } from 'react-native'
import { useMutation, useQueryCache } from 'react-query'
import { useMutation, useQueryClient } from 'react-query'
import client from '@api/client'
import { MenuContainer, MenuHeader, MenuRow } from '@components/Menu'
import { toast } from '@components/toast'
@ -55,7 +55,7 @@ const fireMutation = async ({
}
export interface Props {
queryKey: App.QueryKey
queryKey: QueryKey.Timeline
status: Mastodon.Status
setBottomSheetVisible: React.Dispatch<React.SetStateAction<boolean>>
}
@ -66,17 +66,17 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
setBottomSheetVisible
}) => {
const navigation = useNavigation()
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
const queryClient = useQueryClient()
const { mutate } = useMutation(fireMutation, {
onMutate: ({ id, type, stateKey, prevState }) => {
queryCache.cancelQueries(queryKey)
const oldData = queryCache.getQueryData(queryKey)
queryClient.cancelQueries(queryKey)
const oldData = queryClient.getQueryData(queryKey)
switch (type) {
case 'mute':
case 'pin':
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
queryClient.setQueryData(queryKey, (old: any) =>
old.pages.map((paging: any) => ({
toots: paging.toots.map((toot: any) => {
if (toot.id === id) {
toot[stateKey] =
@ -89,8 +89,8 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
)
break
case 'delete':
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
queryClient.setQueryData(queryKey, (old: any) =>
old.pages.map((paging: any) => ({
toots: paging.toots.filter((toot: any) => toot.id !== id),
pointer: paging.pointer
}))
@ -102,7 +102,7 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
},
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试' })
queryCache.setQueryData(queryKey, oldData)
queryClient.setQueryData(queryKey, oldData)
}
})
@ -112,7 +112,7 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({
mutate({
type: 'delete',
id: status.id,
stateKey: 'id'
@ -138,7 +138,7 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
url: `statuses/${status.id}`
})
.then(res => {
queryCache.invalidateQueries(queryKey)
queryClient.invalidateQueries(queryKey)
setBottomSheetVisible(false)
navigation.navigate(getCurrentTab(navigation), {
screen: 'Screen-Shared-Compose',
@ -159,7 +159,7 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({
mutate({
type: 'mute',
id: status.id,
stateKey: 'muted',
@ -174,7 +174,7 @@ const HeaderDefaultActionsStatus: React.FC<Props> = ({
<MenuRow
onPress={() => {
setBottomSheetVisible(false)
mutateAction({
mutate({
type: 'pin',
id: status.id,
stateKey: 'pinned',

View File

@ -1,7 +1,7 @@
import { Feather } from '@expo/vector-icons'
import React, { useMemo, useState } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'
import { useMutation, useQueryCache } from 'react-query'
import { useMutation, useQueryClient } from 'react-query'
import client from '@api/client'
import { ButtonRow } from '@components/Button'
import { toast } from '@components/toast'
@ -47,21 +47,21 @@ const fireMutation = async ({
}
export interface Props {
queryKey: App.QueryKey
status: Mastodon.Status
queryKey: QueryKey.Timeline
status: Required<Mastodon.Status, 'poll'>
}
const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
const { theme } = useTheme()
const queryCache = useQueryCache()
const [mutateAction] = useMutation(fireMutation, {
const queryClient = useQueryClient()
const { mutate } = useMutation(fireMutation, {
onMutate: ({ id, options }) => {
queryCache.cancelQueries(queryKey)
const oldData = queryCache.getQueryData(queryKey)
queryClient.cancelQueries(queryKey)
const oldData = queryClient.getQueryData(queryKey)
queryCache.setQueryData(queryKey, old =>
(old as {}[]).map((paging: any) => ({
queryClient.setQueryData(queryKey, (old: any) =>
old.pages.map((paging: any) => ({
toots: paging.toots.map((toot: any) => {
if (toot.poll?.id === id) {
const poll = toot.poll
@ -98,13 +98,13 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
},
onError: (err, _, oldData) => {
toast({ type: 'error', content: '请重试' })
queryCache.setQueryData(queryKey, oldData)
queryClient.setQueryData(queryKey, oldData)
}
})
const pollExpiration = useMemo(() => {
// how many voted
if (poll!.expired) {
if (poll.expired) {
return (
<Text style={[styles.expiration, { color: theme.secondary }]}>
@ -113,20 +113,20 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
} else {
return (
<Text style={[styles.expiration, { color: theme.secondary }]}>
{relativeTime(poll!.expires_at)}
{relativeTime(poll.expires_at)}
</Text>
)
}
}, [])
const [singleOptions, setSingleOptions] = useState({
...[false, false, false, false].slice(0, poll!.options.length)
...[false, false, false, false].slice(0, poll.options.length)
})
const [multipleOptions, setMultipleOptions] = useState({
...[false, false, false, false].slice(0, poll!.options.length)
...[false, false, false, false].slice(0, poll.options.length)
})
const isSelected = (index: number) => {
if (poll!.multiple) {
if (poll.multiple) {
return multipleOptions[index] ? 'check-square' : 'square'
} else {
return singleOptions[index] ? 'check-circle' : 'circle'
@ -135,28 +135,28 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
return (
<View style={styles.base}>
{poll!.options.map((option, index) =>
poll!.voted ? (
{poll.options.map((option, index) =>
poll.voted ? (
<View key={index} style={styles.poll}>
<View style={styles.optionSelected}>
<View style={styles.contentSelected}>
<Emojis
content={option.title}
emojis={poll!.emojis}
emojis={poll.emojis}
size={StyleConstants.Font.Size.M}
numberOfLines={1}
/>
{poll!.own_votes!.includes(index) && (
{poll.own_votes!.includes(index) && (
<Feather
style={styles.voted}
name={poll!.multiple ? 'check-square' : 'check-circle'}
name={poll.multiple ? 'check-square' : 'check-circle'}
size={StyleConstants.Font.Size.M}
color={theme.primary}
/>
)}
</View>
<Text style={[styles.percentage, { color: theme.primary }]}>
{Math.round((option.votes_count / poll!.votes_count) * 100)}%
{Math.round((option.votes_count / poll.votes_count) * 100)}%
</Text>
</View>
@ -165,7 +165,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
styles.background,
{
width: `${Math.round(
(option.votes_count / poll!.votes_count) * 100
(option.votes_count / poll.votes_count) * 100
)}%`,
backgroundColor: theme.border
}
@ -177,7 +177,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
<Pressable
style={[styles.optionUnselected]}
onPress={() => {
if (poll!.multiple) {
if (poll.multiple) {
setMultipleOptions({
...multipleOptions,
[index]: !multipleOptions[index]
@ -189,7 +189,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
index === 1,
index === 2,
index === 3
].slice(0, poll!.options.length)
].slice(0, poll.options.length)
})
}
}}
@ -203,7 +203,7 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
<View style={styles.contentUnselected}>
<Emojis
content={option.title}
emojis={poll!.emojis}
emojis={poll.emojis}
size={StyleConstants.Font.Size.M}
/>
</View>
@ -217,9 +217,9 @@ const TimelinePoll: React.FC<Props> = ({ queryKey, status: { poll } }) => {
<ButtonRow
onPress={() => {
if (poll.multiple) {
mutateAction({ id: poll.id, options: multipleOptions })
mutate({ id: poll.id, options: multipleOptions })
} else {
mutateAction({ id: poll.id, options: singleOptions })
mutate({ id: poll.id, options: singleOptions })
}
}}
text='投票'