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 client from 'src/api/client' import Button from 'src/components/Button' import { toast } from 'src/components/toast' import relativeTime from 'src/utils/relativeTime' import { StyleConstants } from 'src/utils/styles/constants' import { useTheme } from 'src/utils/styles/ThemeManager' import Emojis from './Emojis' const fireMutation = async ({ id, options }: { id: string options: { [key: number]: boolean } }) => { const formData = new FormData() Object.keys(options).forEach(option => { // @ts-ignore if (options[option]) { formData.append('choices[]', option) } }) const res = await client({ method: 'post', instance: 'local', endpoint: `polls/${id}/votes`, body: formData }) if (res.body.id === id) { toast({ type: 'success', content: '投票成功成功' }) return Promise.resolve() } else { toast({ type: 'error', content: '隐藏域名失败,请重试', autoHide: false }) return Promise.reject() } } export interface Props { queryKey: App.QueryKey status: Mastodon.Status } const TimelinePoll: React.FC = ({ queryKey, status: { poll } }) => { const { theme } = useTheme() const queryCache = useQueryCache() const [mutateAction] = useMutation(fireMutation, { onMutate: ({ id, options }) => { queryCache.cancelQueries(queryKey) const oldData = queryCache.getQueryData(queryKey) queryCache.setQueryData(queryKey, old => (old as {}[]).map((paging: any) => ({ toots: paging.toots.map((toot: any) => { if (toot.poll?.id === id) { const poll = toot.poll const myVotes = Object.keys(options).filter( // @ts-ignore option => options[option] ) const myVotesInt = myVotes.map(option => parseInt(option)) toot.poll = { ...toot.poll, votes_count: poll.votes_count ? poll.votes_count + myVotes.length : myVotes.length, voters_count: poll.voters_count ? poll.voters_count + 1 : 1, voted: true, own_votes: myVotesInt, // @ts-ignore options: poll.options.map((o, i) => { if (myVotesInt.includes(i)) { o.votes_count = o.votes_count + 1 } return o }) } } return toot }), pointer: paging.pointer })) ) return oldData }, onError: (err, _, oldData) => { toast({ type: 'error', content: '请重试' }) queryCache.setQueryData(queryKey, oldData) } }) const pollExpiration = useMemo(() => { // how many voted if (poll.expired) { return ( 投票已结束 ) } else { return ( 截止至{relativeTime(poll.expires_at)} ) } }, []) const [singleOptions, setSingleOptions] = useState({ ...[false, false, false, false].slice(0, poll.options.length) }) const [multipleOptions, setMultipleOptions] = useState({ ...[false, false, false, false].slice(0, poll.options.length) }) const isSelected = (index: number) => { if (poll.multiple) { return multipleOptions[index] ? 'check-square' : 'square' } else { return singleOptions[index] ? 'check-circle' : 'circle' } } return ( {poll.options.map((option, index) => poll.voted ? ( {poll.own_votes!.includes(index) && ( )} {Math.round((option.votes_count / poll.votes_count) * 100)}% ) : ( { if (poll.multiple) { setMultipleOptions({ ...multipleOptions, [index]: !multipleOptions[index] }) } else { setSingleOptions({ ...[ index === 0, index === 1, index === 2, index === 3 ].slice(0, poll.options.length) }) } }} > ) )} {!poll.expired && !poll.own_votes?.length && (