Files
flotilla/src/app/polls.ts
T
hodlbod 9df8cee501 Migrate to new @welshman/domain + instance-based @welshman/app API
Adopts the rewritten welshman API: the removed @welshman/util helpers
(Profile/List/Room/Handler/Encryptable) are now Reader/Builder classes in
@welshman/domain, and @welshman/app dropped its global singletons for an App
instance + app.use(Plugin) registry.

- src/app/welshman.ts is now the app bootstrap + session-state module (one shared
  App instance, multi-account sessions/login, app-wide reactive views) rather than
  a compat shim re-exporting the old globals.
- Rewrote ~100 callers to use app.use(Plugin) directly (thunks, profiles, relays,
  rooms, zaps, tags, wot, feeds, sync); thunk helpers are now thunk methods.
- Added @welshman/domain dependency.
- Resolved residual gaps (storage hydration via plugin.onItem/wrapManager/Plaintext,
  relay-list mutators, search-relay list, outbox #d filter).

Best-effort: no toolchain/linking available, so this is not build- or
type-checked. Remaining judgment calls are flagged with TODO(welshman-migration).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsMjvv7krpZeHK1Njeneru
2026-06-20 14:55:06 +00:00

93 lines
2.7 KiB
TypeScript

import {now, removeUndefined, uniq} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {POLL_RESPONSE, getTagValue, getTags, getTagValues, makeEvent} from "@welshman/util"
import {Thunks} from "@welshman/app"
import {app} from "@app/welshman"
export type PollType = "singlechoice" | "multiplechoice"
export type PollOption = {
id: string
label: string
votes: number
}
export const getPollType = (event: TrustedEvent): PollType =>
getTagValue("polltype", event.tags) === "multiplechoice" ? "multiplechoice" : "singlechoice"
export const getPollOptions = (event: TrustedEvent) =>
removeUndefined(
getTags("option", event.tags).map(tag => {
const [, id, label = id] = tag
if (!id) return undefined
return {id, label}
}),
)
export const getPollEndsAt = (event: TrustedEvent) => {
const endsAt = getTagValue("endsAt", event.tags)
if (!endsAt) return undefined
const timestamp = parseInt(endsAt)
return Number.isNaN(timestamp) ? undefined : timestamp
}
export const isPollClosed = (event: TrustedEvent) => {
const endsAt = getPollEndsAt(event)
return typeof endsAt === "number" ? endsAt <= now() : false
}
export const getPollResponseSelections = (event: TrustedEvent, pollType = getPollType(event)) => {
const selections = getTagValues("response", event.tags)
return pollType === "singlechoice" ? selections.slice(0, 1) : uniq(selections)
}
export const getPollResults = (event: TrustedEvent, responses: TrustedEvent[]) => {
const options = getPollOptions(event).map(option => ({...option, votes: 0}))
const counts = new Map(options.map(option => [option.id, option]))
const latestByPubkey = new Map<string, TrustedEvent>()
for (const response of responses) {
const current = latestByPubkey.get(response.pubkey)
if (!current || response.created_at > current.created_at) {
latestByPubkey.set(response.pubkey, response)
}
}
for (const response of latestByPubkey.values()) {
for (const optionId of getPollResponseSelections(response, getPollType(event))) {
const option = counts.get(optionId)
if (option) {
option.votes += 1
}
}
}
return {
options,
voters: latestByPubkey.size,
}
}
export type PollResponseParams = {
event: TrustedEvent
selectedIds: string[]
}
export const makePollResponse = ({event, selectedIds}: PollResponseParams) =>
makeEvent(POLL_RESPONSE, {
content: "",
tags: [["e", event.id], ...selectedIds.map(selectedId => ["response", selectedId])],
})
export const publishPollResponse = ({relays, ...params}: PollResponseParams & {relays: string[]}) =>
app.use(Thunks).publish({event: makePollResponse(params), relays})