fix: refine poll UX and review and fix requested changes
This commit is contained in:
@@ -67,7 +67,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<Button onclick={createPoll}>
|
<Button onclick={createPoll}>
|
||||||
<Icon size={4} icon={Revote} />
|
<Icon size={4} icon={Revote} />
|
||||||
Poll
|
Ask a Question
|
||||||
</Button>
|
</Button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -2,21 +2,18 @@
|
|||||||
import type {ComponentProps} from "svelte"
|
import type {ComponentProps} from "svelte"
|
||||||
import {derived} from "svelte/store"
|
import {derived} from "svelte/store"
|
||||||
import {PollResponse} from "nostr-tools/kinds"
|
import {PollResponse} from "nostr-tools/kinds"
|
||||||
import {repository} from "@welshman/app"
|
|
||||||
import {deriveArray, deriveEventsById} from "@welshman/store"
|
|
||||||
import ContentMinimal from "@app/components/ContentMinimal.svelte"
|
import ContentMinimal from "@app/components/ContentMinimal.svelte"
|
||||||
|
import {deriveEvents} from "@app/core/state"
|
||||||
import {getPollResults} from "@app/util/polls"
|
import {getPollResults} from "@app/util/polls"
|
||||||
|
|
||||||
const props: ComponentProps<typeof ContentMinimal> = $props()
|
const props: ComponentProps<typeof ContentMinimal> = $props()
|
||||||
|
|
||||||
const responses = deriveArray(
|
const responses = deriveEvents([{kinds: [PollResponse], "#e": [props.event.id]}])
|
||||||
deriveEventsById({repository, filters: [{kinds: [PollResponse], "#e": [props.event.id]}]}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const results = derived(responses, $responses => getPollResults(props.event, $responses))
|
const results = derived(responses, $responses => getPollResults(props.event, $responses))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-0">
|
||||||
<span class="text-sm">{props.event.content || "Poll"}</span>
|
<span class="text-sm">{props.event.content || "Poll"}</span>
|
||||||
<span class="text-xs opacity-50">{$results.voters} voter{$results.voters === 1 ? "" : "s"}</span>
|
<span class="text-xs opacity-50">{$results.voters} voter{$results.voters === 1 ? "" : "s"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,55 +1,29 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {ComponentProps} from "svelte"
|
import type {ComponentProps} from "svelte"
|
||||||
import {derived} from "svelte/store"
|
import {onMount} from "svelte"
|
||||||
import {formatTimestampRelative} from "@welshman/lib"
|
import {request} from "@welshman/net"
|
||||||
import {PollResponse} from "nostr-tools/kinds"
|
import {PollResponse} from "nostr-tools/kinds"
|
||||||
import {repository} from "@welshman/app"
|
import PollVotes from "@app/components/PollVotes.svelte"
|
||||||
import {deriveArray, deriveEventsById} from "@welshman/store"
|
|
||||||
import Content from "@app/components/Content.svelte"
|
import Content from "@app/components/Content.svelte"
|
||||||
import {getPollEndsAt, getPollResults, getPollType, isPollClosed} from "@app/util/polls"
|
|
||||||
|
|
||||||
const props: ComponentProps<typeof Content> = $props()
|
const props: ComponentProps<typeof Content> = $props()
|
||||||
|
|
||||||
const responses = deriveArray(
|
onMount(() => {
|
||||||
deriveEventsById({repository, filters: [{kinds: [PollResponse], "#e": [props.event.id]}]}),
|
if (!props.url) {
|
||||||
)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const results = derived(responses, $responses => getPollResults(props.event, $responses))
|
request({
|
||||||
|
relays: [props.url],
|
||||||
const endsAt = getPollEndsAt(props.event)
|
filters: [{kinds: [PollResponse], "#e": [props.event.id]}],
|
||||||
const pollType = getPollType(props.event)
|
})
|
||||||
const closed = isPollClosed(props.event)
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-2">
|
<p class="text-xl">{props.event.content || "Poll"}</p>
|
||||||
<div class="min-w-0 flex-grow">
|
|
||||||
<p class="text-xl">{props.event.content || "Poll"}</p>
|
|
||||||
<p class="text-xs opacity-50">
|
|
||||||
{pollType === "multiplechoice" ? "Multiple choice" : "Single choice"}
|
|
||||||
{#if endsAt}
|
|
||||||
· Ends {formatTimestampRelative(endsAt)}
|
|
||||||
{/if}
|
|
||||||
{#if closed}
|
|
||||||
· Closed
|
|
||||||
{/if}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p class="whitespace-nowrap text-xs opacity-50">
|
|
||||||
{$results.voters} voter{$results.voters === 1 ? "" : "s"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
{#if props.url}
|
||||||
{#each $results.options as option (option.id)}
|
<PollVotes url={props.url} event={props.event} />
|
||||||
{@const maxVotes = Math.max(...$results.options.map(item => item.votes), 1)}
|
{/if}
|
||||||
<div class="flex flex-col gap-1">
|
|
||||||
<div class="flex items-center justify-between gap-2 text-sm">
|
|
||||||
<span class="truncate">{option.label}</span>
|
|
||||||
<span class="whitespace-nowrap opacity-75">{option.votes}</span>
|
|
||||||
</div>
|
|
||||||
<progress class="progress progress-primary" value={option.votes} max={maxVotes}></progress>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {randomId, removeUndefined} from "@welshman/lib"
|
import {insertAt, now, randomId, removeAt, removeUndefined} from "@welshman/lib"
|
||||||
import {makeEvent} from "@welshman/util"
|
import {makeEvent} from "@welshman/util"
|
||||||
import {publishThunk} from "@welshman/app"
|
import {publishThunk} from "@welshman/app"
|
||||||
import {Poll} from "nostr-tools/kinds"
|
import {Poll} from "nostr-tools/kinds"
|
||||||
import {isMobile, preventDefault} from "@lib/html"
|
import {isMobile, preventDefault} from "@lib/html"
|
||||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||||
|
import HamburgerMenu from "@assets/icons/hamburger-menu.svg?dataurl"
|
||||||
import PlusCircle from "@assets/icons/add-circle.svg?dataurl"
|
import PlusCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||||
import MinusCircle from "@assets/icons/minus-circle.svg?dataurl"
|
import MinusCircle from "@assets/icons/minus-circle.svg?dataurl"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Field from "@lib/components/Field.svelte"
|
import Field from "@lib/components/Field.svelte"
|
||||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||||
|
import DateTimeInput from "@lib/components/DateTimeInput.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {PROTECTED} from "@app/core/state"
|
import {PROTECTED} from "@app/core/state"
|
||||||
import {canEnforceNip70} from "@app/core/commands"
|
import {canEnforceNip70} from "@app/core/commands"
|
||||||
|
import type {PollType} from "@app/util/polls"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
url: string
|
url: string
|
||||||
@@ -30,14 +33,62 @@
|
|||||||
|
|
||||||
const shouldProtect = canEnforceNip70(url)
|
const shouldProtect = canEnforceNip70(url)
|
||||||
|
|
||||||
|
type DraftOption = {
|
||||||
|
id: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
const addOption = () => {
|
const addOption = () => {
|
||||||
options = [...options, ""]
|
options = [...options, {id: randomId(), value: ""}]
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeOption = (index: number) => {
|
const removeOption = (id: string) => {
|
||||||
options = options.filter((_, optionIndex) => optionIndex !== index)
|
options = options.filter(option => option.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateOption = (id: string, value: string) => {
|
||||||
|
options = options.map(option => (option.id === id ? {...option, value} : option))
|
||||||
|
}
|
||||||
|
|
||||||
|
const reorderOptions = (targetId: string) => {
|
||||||
|
if (!draggedOptionId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceIndex = options.findIndex(option => option.id === draggedOptionId)
|
||||||
|
const targetIndex = options.findIndex(option => option.id === targetId)
|
||||||
|
|
||||||
|
if (sourceIndex === -1 || targetIndex === -1 || sourceIndex === targetIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
options = insertAt(targetIndex, options[sourceIndex], removeAt(sourceIndex, options))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragStart = (e: DragEvent, id: string) => {
|
||||||
|
draggedOptionId = id
|
||||||
|
|
||||||
|
if (e.dataTransfer) {
|
||||||
|
e.dataTransfer.effectAllowed = "move"
|
||||||
|
e.dataTransfer.setData("text/plain", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragOver = (e: DragEvent, targetId: string) => {
|
||||||
|
e.preventDefault()
|
||||||
|
reorderOptions(targetId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDrop = (e: DragEvent, targetId: string) => {
|
||||||
|
e.preventDefault()
|
||||||
|
reorderOptions(targetId)
|
||||||
|
draggedOptionId = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragEnd = () => {
|
||||||
|
draggedOptionId = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
@@ -45,13 +96,15 @@
|
|||||||
return pushToast({theme: "error", message: "Please provide a title for your poll."})
|
return pushToast({theme: "error", message: "Please provide a title for your poll."})
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonEmptyOptions = removeUndefined(options.map(option => option.trim() || undefined))
|
const nonEmptyOptions = removeUndefined(options.map(option => option.value.trim() || undefined))
|
||||||
|
|
||||||
if (nonEmptyOptions.length < 2) {
|
if (nonEmptyOptions.length < 2) {
|
||||||
return pushToast({theme: "error", message: "Please provide at least two options."})
|
return pushToast({theme: "error", message: "Please provide at least two options."})
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedEndsAt = endsAt ? Math.floor(new Date(endsAt).getTime() / 1000) : undefined
|
if (endsAt && endsAt <= now()) {
|
||||||
|
return pushToast({theme: "error", message: "End time must be in the future."})
|
||||||
|
}
|
||||||
|
|
||||||
const tags: string[][] = [
|
const tags: string[][] = [
|
||||||
...nonEmptyOptions.map(option => ["option", randomId(), option]),
|
...nonEmptyOptions.map(option => ["option", randomId(), option]),
|
||||||
@@ -59,8 +112,8 @@
|
|||||||
["relay", url],
|
["relay", url],
|
||||||
]
|
]
|
||||||
|
|
||||||
if (parsedEndsAt) {
|
if (endsAt) {
|
||||||
tags.push(["endsAt", String(parsedEndsAt)])
|
tags.push(["endsAt", String(endsAt)])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h) {
|
if (h) {
|
||||||
@@ -80,16 +133,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let title = $state("")
|
let title = $state("")
|
||||||
let pollType = $state<"singlechoice" | "multiplechoice">("singlechoice")
|
let pollType = $state<PollType>("singlechoice")
|
||||||
let endsAt = $state("")
|
let endsAt = $state<number | undefined>()
|
||||||
let options = $state(["Yes", "No"])
|
let options = $state<DraftOption[]>([
|
||||||
|
{id: randomId(), value: "Yes"},
|
||||||
|
{id: randomId(), value: "No"},
|
||||||
|
])
|
||||||
|
let draggedOptionId = $state<string | undefined>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal tag="form" onsubmit={preventDefault(submit)}>
|
<Modal tag="form" onsubmit={preventDefault(submit)}>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
<ModalTitle>Create a Poll</ModalTitle>
|
<ModalTitle>Create a Poll</ModalTitle>
|
||||||
<ModalSubtitle>Ask the room a question with one or more answers.</ModalSubtitle>
|
<ModalSubtitle>Ask a question and collect votes right in the feed.</ModalSubtitle>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<div class="col-8 relative">
|
<div class="col-8 relative">
|
||||||
<Field>
|
<Field>
|
||||||
@@ -114,22 +171,33 @@
|
|||||||
<p>Options*</p>
|
<p>Options*</p>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2" role="list">
|
||||||
{#each options as option, index (index)}
|
{#each options as option, index (option.id)}
|
||||||
<div class="flex items-center gap-2">
|
<div
|
||||||
|
class="flex items-center gap-2"
|
||||||
|
draggable="true"
|
||||||
|
role="listitem"
|
||||||
|
ondragstart={e => onDragStart(e, option.id)}
|
||||||
|
ondragover={e => onDragOver(e, option.id)}
|
||||||
|
ondrop={e => onDrop(e, option.id)}
|
||||||
|
ondragend={onDragEnd}>
|
||||||
|
<div class="cursor-move opacity-70" aria-label="Drag handle">
|
||||||
|
<Icon icon={HamburgerMenu} size={4} />
|
||||||
|
</div>
|
||||||
<label class="input input-bordered flex w-full items-center gap-2">
|
<label class="input input-bordered flex w-full items-center gap-2">
|
||||||
<input
|
<input
|
||||||
bind:value={options[index]}
|
value={option.value}
|
||||||
class="grow"
|
class="grow"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={`Option ${index + 1}`} />
|
placeholder={`Option ${index + 1}`}
|
||||||
|
oninput={e => updateOption(option.id, e.currentTarget.value)} />
|
||||||
</label>
|
</label>
|
||||||
<Button class="btn btn-ghost btn-sm" onclick={() => removeOption(index)}>
|
<Button class="btn btn-ghost btn-sm" onclick={() => removeOption(option.id)}>
|
||||||
<Icon icon={MinusCircle} size={4} />
|
<Icon icon={MinusCircle} size={4} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<Button class="btn btn-ghost btn-sm self-start" onclick={addOption}>
|
<Button class="btn btn-outline btn-sm self-end" onclick={addOption}>
|
||||||
<Icon icon={PlusCircle} size={4} />
|
<Icon icon={PlusCircle} size={4} />
|
||||||
Add option
|
Add option
|
||||||
</Button>
|
</Button>
|
||||||
@@ -154,10 +222,7 @@
|
|||||||
Ends at
|
Ends at
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<input
|
<DateTimeInput bind:value={endsAt} />
|
||||||
bind:value={endsAt}
|
|
||||||
class="input input-bordered w-full max-w-xs"
|
|
||||||
type="datetime-local" />
|
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</FieldInline>
|
</FieldInline>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import {pubkey, repository} from "@welshman/app"
|
import {pubkey} from "@welshman/app"
|
||||||
import {deriveArray, deriveEventsById} from "@welshman/store"
|
|
||||||
import {PollResponse} from "nostr-tools/kinds"
|
import {PollResponse} from "nostr-tools/kinds"
|
||||||
import {formatTimestampRelative} from "@welshman/lib"
|
import {formatTimestampRelative} from "@welshman/lib"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import {deriveEvents} from "@app/core/state"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {publishPollResponse} from "@app/core/commands"
|
import {publishPollResponse} from "@app/core/commands"
|
||||||
import {
|
import {
|
||||||
@@ -23,22 +23,31 @@
|
|||||||
|
|
||||||
const {url, event}: Props = $props()
|
const {url, event}: Props = $props()
|
||||||
|
|
||||||
const responses = deriveArray(
|
const responses = deriveEvents([{kinds: [PollResponse], "#e": [event.id]}])
|
||||||
deriveEventsById({repository, filters: [{kinds: [PollResponse], "#e": [event.id]}]}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const pollType = getPollType(event)
|
const pollType = getPollType(event)
|
||||||
const options = getPollOptions(event)
|
const options = getPollOptions(event)
|
||||||
const closed = isPollClosed(event)
|
const closed = isPollClosed(event)
|
||||||
const endsAt = getPollEndsAt(event)
|
const endsAt = getPollEndsAt(event)
|
||||||
|
|
||||||
const results = $derived.by(() => getPollResults(event, $responses))
|
const getOwnResponse = (responses: TrustedEvent[]) => {
|
||||||
const ownResponse = $derived.by(
|
let latest: TrustedEvent | undefined
|
||||||
() =>
|
|
||||||
$responses
|
for (const response of responses) {
|
||||||
.filter(response => response.pubkey === $pubkey)
|
if (response.pubkey !== $pubkey) {
|
||||||
.sort((left, right) => right.created_at - left.created_at)[0],
|
continue
|
||||||
)
|
}
|
||||||
|
|
||||||
|
if (!latest || response.created_at > latest.created_at) {
|
||||||
|
latest = response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return latest
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = $derived(getPollResults(event, $responses))
|
||||||
|
const ownResponse = $derived(getOwnResponse($responses))
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
@@ -78,10 +87,11 @@
|
|||||||
<div class="text-sm opacity-75">
|
<div class="text-sm opacity-75">
|
||||||
{pollType === "multiplechoice" ? "Multiple choice" : "Single choice"}
|
{pollType === "multiplechoice" ? "Multiple choice" : "Single choice"}
|
||||||
{#if endsAt}
|
{#if endsAt}
|
||||||
· Ends {formatTimestampRelative(endsAt)}
|
{#if closed}
|
||||||
{/if}
|
• Ended {formatTimestampRelative(endsAt)}
|
||||||
{#if closed}
|
{:else}
|
||||||
· Closed
|
• Ends {formatTimestampRelative(endsAt)}
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm opacity-75">{results.voters} vote{results.voters === 1 ? "" : "s"}</div>
|
<div class="text-sm opacity-75">{results.voters} vote{results.voters === 1 ? "" : "s"}</div>
|
||||||
|
|||||||
+8
-12
@@ -1,6 +1,6 @@
|
|||||||
import {now, uniq} from "@welshman/lib"
|
import {now, removeUndefined, uniq} from "@welshman/lib"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import {getTagValue, getTags} from "@welshman/util"
|
import {getTagValue, getTags, getTagValues} from "@welshman/util"
|
||||||
|
|
||||||
export type PollType = "singlechoice" | "multiplechoice"
|
export type PollType = "singlechoice" | "multiplechoice"
|
||||||
|
|
||||||
@@ -10,24 +10,22 @@ export type PollOption = {
|
|||||||
votes: number
|
votes: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDefined = <T>(value: T | undefined): value is T => value !== undefined
|
|
||||||
|
|
||||||
export const getPollType = (event: TrustedEvent): PollType =>
|
export const getPollType = (event: TrustedEvent): PollType =>
|
||||||
getTagValue("polltype", event.tags) === "multiplechoice" ? "multiplechoice" : "singlechoice"
|
getTagValue("polltype", event.tags) === "multiplechoice" ? "multiplechoice" : "singlechoice"
|
||||||
|
|
||||||
export const getPollOptions = (event: TrustedEvent) =>
|
export const getPollOptions = (event: TrustedEvent) =>
|
||||||
getTags("option", event.tags)
|
removeUndefined(
|
||||||
.map(tag => {
|
getTags("option", event.tags).map(tag => {
|
||||||
const [, id, label = id] = tag
|
const [, id, label = id] = tag
|
||||||
|
|
||||||
if (!id) return undefined
|
if (!id) return undefined
|
||||||
|
|
||||||
return {id, label}
|
return {id, label}
|
||||||
})
|
}),
|
||||||
.filter(isDefined)
|
)
|
||||||
|
|
||||||
export const getPollEndsAt = (event: TrustedEvent) => {
|
export const getPollEndsAt = (event: TrustedEvent) => {
|
||||||
const endsAt = getTagValue("endsAt", event.tags) || getTagValue("endsat", event.tags)
|
const endsAt = getTagValue("endsAt", event.tags)
|
||||||
|
|
||||||
if (!endsAt) return undefined
|
if (!endsAt) return undefined
|
||||||
|
|
||||||
@@ -43,9 +41,7 @@ export const isPollClosed = (event: TrustedEvent) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getPollResponseSelections = (event: TrustedEvent, pollType = getPollType(event)) => {
|
export const getPollResponseSelections = (event: TrustedEvent, pollType = getPollType(event)) => {
|
||||||
const selections = getTags("response", event.tags)
|
const selections = getTagValues("response", event.tags)
|
||||||
.map(tag => tag[1])
|
|
||||||
.filter(isDefined)
|
|
||||||
|
|
||||||
return pollType === "singlechoice" ? selections.slice(0, 1) : uniq(selections)
|
return pollType === "singlechoice" ? selections.slice(0, 1) : uniq(selections)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
import SpaceBar from "@app/components/SpaceBar.svelte"
|
import SpaceBar from "@app/components/SpaceBar.svelte"
|
||||||
import NoteCard from "@app/components/NoteCard.svelte"
|
import NoteCard from "@app/components/NoteCard.svelte"
|
||||||
import NoteContent from "@app/components/NoteContent.svelte"
|
import NoteContent from "@app/components/NoteContent.svelte"
|
||||||
import PollVotes from "@app/components/PollVotes.svelte"
|
|
||||||
import CommentActions from "@app/components/CommentActions.svelte"
|
import CommentActions from "@app/components/CommentActions.svelte"
|
||||||
import EventReply from "@app/components/EventReply.svelte"
|
import EventReply from "@app/components/EventReply.svelte"
|
||||||
import {deriveEvent, decodeRelay} from "@app/core/state"
|
import {deriveEvent, decodeRelay} from "@app/core/state"
|
||||||
@@ -71,7 +70,6 @@
|
|||||||
<NoteCard event={$event} {url} class="card2 bg-alt z-feature w-full">
|
<NoteCard event={$event} {url} class="card2 bg-alt z-feature w-full">
|
||||||
<div class="col-3 ml-12 flex flex-col gap-3">
|
<div class="col-3 ml-12 flex flex-col gap-3">
|
||||||
<NoteContent showEntire event={$event} {url} />
|
<NoteContent showEntire event={$event} {url} />
|
||||||
<PollVotes {url} event={$event} />
|
|
||||||
<CommentActions segment="polls" showActivity {url} event={$event} />
|
<CommentActions segment="polls" showActivity {url} event={$event} />
|
||||||
</div>
|
</div>
|
||||||
</NoteCard>
|
</NoteCard>
|
||||||
|
|||||||
Reference in New Issue
Block a user