2022-11-29 20:29:02 +00:00
|
|
|
<script setup lang="ts">
|
2023-01-08 06:21:09 +00:00
|
|
|
import type { mastodon } from 'masto'
|
2022-11-29 20:29:02 +00:00
|
|
|
|
2022-12-28 11:49:47 +00:00
|
|
|
const { status } = defineProps<{
|
2023-01-08 06:21:09 +00:00
|
|
|
status: mastodon.v1.Status
|
2022-11-29 20:29:02 +00:00
|
|
|
}>()
|
2022-12-28 11:49:47 +00:00
|
|
|
const poll = reactive({ ...status.poll! })
|
2022-11-29 20:29:02 +00:00
|
|
|
|
|
|
|
function toPercentage(num: number) {
|
|
|
|
const percentage = 100 * num
|
|
|
|
return `${percentage.toFixed(1).replace(/\.?0+$/, '')}%`
|
|
|
|
}
|
2022-12-02 02:18:36 +00:00
|
|
|
const timeAgoOptions = useTimeAgoOptions()
|
|
|
|
const expiredTimeAgo = useTimeAgo(poll.expiresAt!, timeAgoOptions)
|
2022-12-08 10:07:54 +00:00
|
|
|
const expiredTimeFormatted = useFormattedDateTime(poll.expiresAt!)
|
2023-01-09 11:24:26 +00:00
|
|
|
const { formatPercentage } = useHumanReadableNumber()
|
2022-11-29 20:29:02 +00:00
|
|
|
|
2024-02-21 15:20:08 +00:00
|
|
|
const { client } = useMasto()
|
2023-01-15 08:38:02 +00:00
|
|
|
|
2022-11-29 20:29:02 +00:00
|
|
|
async function vote(e: Event) {
|
|
|
|
const formData = new FormData(e.target as HTMLFormElement)
|
2023-04-12 12:35:35 +00:00
|
|
|
const choices = formData.getAll('choices').map(i => +i) as number[]
|
2022-11-29 20:29:02 +00:00
|
|
|
|
|
|
|
// Update the poll optimistically
|
|
|
|
for (const [index, option] of poll.options.entries()) {
|
2023-04-12 12:35:35 +00:00
|
|
|
if (choices.includes(index))
|
2022-11-29 20:29:02 +00:00
|
|
|
option.votesCount = (option.votesCount || 0) + 1
|
|
|
|
}
|
|
|
|
poll.voted = true
|
|
|
|
poll.votesCount++
|
2023-01-16 09:57:32 +00:00
|
|
|
|
|
|
|
if (!poll.votersCount && poll.votesCount)
|
|
|
|
poll.votesCount = poll.votesCount + 1
|
|
|
|
else
|
|
|
|
poll.votersCount = (poll.votersCount || 0) + 1
|
|
|
|
|
2022-12-28 11:49:47 +00:00
|
|
|
cacheStatus({ ...status, poll }, undefined, true)
|
|
|
|
|
2024-02-21 15:20:08 +00:00
|
|
|
await client.value.v1.polls.$select(poll.id).votes.create({ choices })
|
2022-11-29 20:29:02 +00:00
|
|
|
}
|
2023-01-01 14:29:11 +00:00
|
|
|
|
2024-02-21 15:20:08 +00:00
|
|
|
const votersCount = computed(() => poll.votersCount ?? poll.votesCount ?? 0)
|
2022-11-29 20:29:02 +00:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2023-01-17 12:55:36 +00:00
|
|
|
<div flex flex-col w-full items-stretch gap-2 py3 dir="auto" class="poll-wrapper">
|
2023-01-15 11:31:16 +00:00
|
|
|
<form v-if="!poll.voted && !poll.expired" flex="~ col gap3" accent-primary @click.stop="noop" @submit.prevent="vote">
|
|
|
|
<label v-for="(option, index) of poll.options" :key="index" flex="~ gap2" items-center>
|
2023-01-17 12:49:40 +00:00
|
|
|
<input name="choices" :value="index" :type="poll.multiple ? 'checkbox' : 'radio'" cursor-pointer>
|
2022-11-29 20:29:02 +00:00
|
|
|
{{ option.title }}
|
|
|
|
</label>
|
2023-01-15 11:31:16 +00:00
|
|
|
<button btn-solid mt-1>
|
2022-12-04 18:49:35 +00:00
|
|
|
{{ $t('action.vote') }}
|
2022-11-29 20:29:02 +00:00
|
|
|
</button>
|
|
|
|
</form>
|
|
|
|
<template v-else>
|
2023-01-15 11:31:16 +00:00
|
|
|
<div
|
|
|
|
v-for="(option, index) of poll.options"
|
|
|
|
:key="index" py-1 relative
|
2023-02-28 21:44:15 +00:00
|
|
|
:style="{ '--bar-width': toPercentage(votersCount === 0 ? 0 : (option.votesCount ?? 0) / votersCount) }"
|
2023-01-15 11:31:16 +00:00
|
|
|
>
|
2022-12-04 18:04:40 +00:00
|
|
|
<div flex justify-between pb-2 w-full>
|
|
|
|
<span inline-flex align-items>
|
|
|
|
{{ option.title }}
|
2023-01-01 14:29:11 +00:00
|
|
|
<span v-if="poll.voted && poll.ownVotes?.includes(index)" ms-2 mt-1 inline-block i-ri:checkbox-circle-line />
|
2022-12-04 18:04:40 +00:00
|
|
|
</span>
|
2023-01-01 14:29:11 +00:00
|
|
|
<span text-primary-active> {{ formatPercentage(votersCount > 0 ? (option.votesCount || 0) / votersCount : 0) }}</span>
|
2022-11-29 20:29:02 +00:00
|
|
|
</div>
|
2022-12-04 18:04:40 +00:00
|
|
|
<div class="bg-gray/40" rounded-l-sm rounded-r-lg h-5px w-full>
|
2023-02-28 21:44:15 +00:00
|
|
|
<div bg-primary-active h-full min-w="1%" class="w-[var(--bar-width)]" />
|
2022-11-29 20:29:02 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
2023-01-15 11:31:16 +00:00
|
|
|
<div text-sm flex="~ inline" gap-x-1 text-secondary>
|
2023-01-09 11:24:26 +00:00
|
|
|
<CommonLocalizedNumber
|
|
|
|
keypath="status.poll.count"
|
|
|
|
:count="poll.votesCount"
|
|
|
|
/>
|
2022-12-04 18:58:21 +00:00
|
|
|
·
|
2023-07-02 18:12:24 +00:00
|
|
|
<CommonTooltip v-if="poll.expiresAt" :content="expiredTimeFormatted" class="inline-block" placement="right">
|
2022-12-08 10:07:54 +00:00
|
|
|
<time :datetime="poll.expiresAt!">{{ $t(poll.expired ? 'status.poll.finished' : 'status.poll.ends', [expiredTimeAgo]) }}</time>
|
|
|
|
</CommonTooltip>
|
2022-11-29 20:29:02 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|