elk/components/status/StatusPoll.vue
2022-12-31 05:43:23 +08:00

70 lines
2.7 KiB
Vue

<script setup lang="ts">
import type { Status } from 'masto'
const { status } = defineProps<{
status: Status
}>()
const poll = reactive({ ...status.poll! })
function toPercentage(num: number) {
const percentage = 100 * num
return `${percentage.toFixed(1).replace(/\.?0+$/, '')}%`
}
const timeAgoOptions = useTimeAgoOptions()
const expiredTimeAgo = useTimeAgo(poll.expiresAt!, timeAgoOptions)
const expiredTimeFormatted = useFormattedDateTime(poll.expiresAt!)
const { formatHumanReadableNumber } = useHumanReadableNumber()
const masto = useMasto()
async function vote(e: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const choices = formData.getAll('choices') as string[]
// Update the poll optimistically
for (const [index, option] of poll.options.entries()) {
if (choices.includes(String(index)))
option.votesCount = (option.votesCount || 0) + 1
}
poll.voted = true
poll.votesCount++
poll.votersCount = (poll.votersCount || 0) + 1
cacheStatus({ ...status, poll }, undefined, true)
await masto.poll.vote(poll.id, { choices })
}
</script>
<template>
<div flex flex-col w-full items-stretch gap-3>
<form v-if="!poll.voted && !poll.expired" flex flex-col gap-4 accent-primary @click.stop="noop" @submit.prevent="vote">
<label v-for="(option, index) of poll.options" :key="index" flex items-center gap-2 px-2>
<input name="choices" :value="index" :type="poll.multiple ? 'checkbox' : 'radio'">
{{ option.title }}
</label>
<button btn-solid>
{{ $t('action.vote') }}
</button>
</form>
<template v-else>
<div v-for="(option, index) of poll.options" :key="index" py-1 relative :style="{ '--bar-width': toPercentage((option.votesCount || 0) / poll.votesCount) }">
<div flex justify-between pb-2 w-full>
<span inline-flex align-items>
{{ option.title }}
<span v-if="poll.voted && poll.ownVotes?.includes(index)" ml-2 mt-1 inline-block i-ri:checkbox-circle-line />
</span>
<span text-primary-active> {{ poll.votesCount ? toPercentage((option.votesCount || 0) / (poll.votesCount)) : '0%' }}</span>
</div>
<div class="bg-gray/40" rounded-l-sm rounded-r-lg h-5px w-full>
<div bg-primary-active h-full class="w-[var(--bar-width)]" />
</div>
</div>
</template>
<div text-sm>
{{ $t('status.poll.count', [formatHumanReadableNumber(poll.votersCount ?? 0)]) }}
&middot;
<CommonTooltip :content="expiredTimeFormatted" class="inline-block" placement="right">
<time :datetime="poll.expiresAt!">{{ $t(poll.expired ? 'status.poll.finished' : 'status.poll.ends', [expiredTimeAgo]) }}</time>
</CommonTooltip>
</div>
</div>
</template>