fix: refine poll rendering and inline voting UX

This commit is contained in:
Bhavishy
2026-04-03 13:45:48 +05:30
parent 70ce54c5a5
commit 9b39858b49
3 changed files with 44 additions and 22 deletions
@@ -2,11 +2,11 @@
import type {ComponentProps} from "svelte"
import {derived} from "svelte/store"
import {PollResponse} from "nostr-tools/kinds"
import ContentMinimal from "@app/components/ContentMinimal.svelte"
import Content from "@app/components/Content.svelte"
import {deriveEvents} from "@app/core/state"
import {getPollResults} from "@app/util/polls"
const props: ComponentProps<typeof ContentMinimal> = $props()
const props: ComponentProps<typeof Content> = $props()
const responses = deriveEvents([{kinds: [PollResponse], "#e": [props.event.id]}])
@@ -14,6 +14,6 @@
</script>
<div class="flex flex-col gap-0">
<span class="text-sm">{props.event.content || "Poll"}</span>
<Content event={props.event} url={props.url} />
<span class="text-xs opacity-50">{$results.voters} voter{$results.voters === 1 ? "" : "s"}</span>
</div>
+1 -1
View File
@@ -21,7 +21,7 @@
</script>
<div class="flex flex-col gap-3">
<p class="text-xl">{props.event.content || "Poll"}</p>
<Content event={props.event} showEntire url={props.url} />
{#if props.url}
<PollVotes url={props.url} event={props.event} />
+40 -18
View File
@@ -1,12 +1,14 @@
<script lang="ts">
import {onDestroy} from "svelte"
import type {TrustedEvent} from "@welshman/util"
import {pubkey} from "@welshman/app"
import {pubkey, publishThunk, abortThunk} from "@welshman/app"
import {PollResponse} from "nostr-tools/kinds"
import {formatTimestampRelative} from "@welshman/lib"
import Button from "@lib/components/Button.svelte"
import {noop} from "@welshman/lib"
import {stopPropagation} from "@lib/html"
import {deriveEvents} from "@app/core/state"
import {pushToast} from "@app/util/toast"
import {publishPollResponse} from "@app/core/commands"
import {makePollResponse} from "@app/core/commands"
import {
getPollEndsAt,
getPollOptions,
@@ -29,6 +31,7 @@
const options = getPollOptions(event)
const closed = isPollClosed(event)
const endsAt = getPollEndsAt(event)
const publishDelay = pollType === "multiplechoice" ? 10_000 : undefined
const getOwnResponse = (responses: TrustedEvent[]) => {
let latest: TrustedEvent | undefined
@@ -46,43 +49,66 @@
return latest
}
const results = $derived(getPollResults(event, $responses))
const ownResponse = $derived(getOwnResponse($responses))
const submit = async () => {
if (closed) {
return pushToast({theme: "error", message: "This poll is closed."})
const publishSelection = (selection: string[]) => {
if (activeThunk) {
abortThunk(activeThunk)
}
const selection = pollType === "singlechoice" ? [selectedIds[0]].filter(Boolean) : selectedIds
if (selection.length === 0) {
activeThunk = undefined
return
}
activeThunk = publishThunk({
relays: [url],
event: makePollResponse({event, selectedIds: selection}),
delay: publishDelay,
})
}
const publishCurrentSelection = () => {
const selection = pollType === "singlechoice" ? selectedIds.slice(0, 1) : selectedIds
if (selection.length === 0) {
return pushToast({theme: "error", message: "Please select at least one option."})
}
publishPollResponse({relays: [url], event, selectedIds: selection})
publishSelection(selection)
}
const results = $derived(getPollResults(event, $responses))
const ownResponse = $derived(getOwnResponse($responses))
const setSingleChoice = (id: string) => {
selectedIds = [id]
publishCurrentSelection()
}
const toggleMultipleChoice = (id: string) => {
selectedIds = selectedIds.includes(id)
? selectedIds.filter(selectedId => selectedId !== id)
: [...selectedIds, id]
publishCurrentSelection()
}
let selectedIds = $state<string[]>([])
let activeThunk: ReturnType<typeof publishThunk> | undefined
$effect(() => {
if (ownResponse) {
selectedIds = getPollResponseSelections(ownResponse, pollType)
}
})
onDestroy(() => {
if (activeThunk) {
abortThunk(activeThunk)
}
})
</script>
<div class="flex flex-col gap-3 rounded-box bg-base-200 p-4">
<div class="card2 bg-alt p-4">
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="text-sm opacity-75">
{pollType === "multiplechoice" ? "Multiple choice" : "Single choice"}
@@ -111,12 +137,14 @@
type="radio"
class="radio radio-primary radio-sm"
checked={selectedIds[0] === option.id}
onclick={stopPropagation(noop)}
onchange={() => setSingleChoice(option.id)} />
{:else}
<input
type="checkbox"
class="checkbox checkbox-primary checkbox-sm"
checked={selectedIds.includes(option.id)}
onclick={stopPropagation(noop)}
onchange={() => toggleMultipleChoice(option.id)} />
{/if}
{/if}
@@ -130,10 +158,4 @@
</div>
{/each}
</div>
{#if !closed}
<div class="flex justify-end">
<Button class="btn btn-primary btn-sm" onclick={submit}>Cast vote</Button>
</div>
{/if}
</div>