PollEditor.tsx raw
1 import { Button } from '@/components/ui/button'
2 import { Input } from '@/components/ui/input'
3 import { Label } from '@/components/ui/label'
4 import { Switch } from '@/components/ui/switch'
5 import { normalizeUrl } from '@/lib/url'
6 import { TPollCreateData } from '@/types'
7 import dayjs from 'dayjs'
8 import { Eraser, X } from 'lucide-react'
9 import { Dispatch, SetStateAction, useEffect, useState } from 'react'
10 import { useTranslation } from 'react-i18next'
11 import InfoCard from '../InfoCard'
12
13 export default function PollEditor({
14 pollCreateData,
15 setPollCreateData,
16 setIsPoll
17 }: {
18 pollCreateData: TPollCreateData
19 setPollCreateData: Dispatch<SetStateAction<TPollCreateData>>
20 setIsPoll: Dispatch<SetStateAction<boolean>>
21 }) {
22 const { t } = useTranslation()
23 const [isMultipleChoice, setIsMultipleChoice] = useState(pollCreateData.isMultipleChoice)
24 const [options, setOptions] = useState(pollCreateData.options)
25 const [endsAt, setEndsAt] = useState(
26 pollCreateData.endsAt ? dayjs(pollCreateData.endsAt * 1000).format('YYYY-MM-DDTHH:mm') : ''
27 )
28 const [relayUrls, setRelayUrls] = useState(pollCreateData.relays.join(', '))
29
30 useEffect(() => {
31 setPollCreateData({
32 isMultipleChoice,
33 options,
34 endsAt: endsAt ? dayjs(endsAt).startOf('minute').unix() : undefined,
35 relays: relayUrls
36 ? relayUrls
37 .split(',')
38 .map((url) => normalizeUrl(url.trim()))
39 .filter(Boolean)
40 : []
41 })
42 }, [isMultipleChoice, options, endsAt, relayUrls])
43
44 const handleAddOption = () => {
45 setOptions([...options, ''])
46 }
47
48 const handleRemoveOption = (index: number) => {
49 if (options.length > 2) {
50 setOptions(options.filter((_, i) => i !== index))
51 }
52 }
53
54 const handleOptionChange = (index: number, value: string) => {
55 const newOptions = [...options]
56 newOptions[index] = value
57 setOptions(newOptions)
58 }
59
60 return (
61 <div className="space-y-4 border rounded-lg p-3">
62 <div className="space-y-2">
63 {options.map((option, index) => (
64 <div key={index} className="flex gap-2">
65 <Input
66 value={option}
67 onChange={(e) => handleOptionChange(index, e.target.value)}
68 placeholder={t('Option {{number}}', { number: index + 1 })}
69 />
70 <Button
71 type="button"
72 variant="ghost-destructive"
73 size="icon"
74 onClick={() => handleRemoveOption(index)}
75 disabled={options.length <= 2}
76 >
77 <X />
78 </Button>
79 </div>
80 ))}
81 <Button type="button" variant="outline" onClick={handleAddOption}>
82 {t('Add Option')}
83 </Button>
84 </div>
85
86 <div className="flex items-center space-x-2">
87 <Label htmlFor="multiple-choice">{t('Allow multiple choices')}</Label>
88 <Switch
89 id="multiple-choice"
90 checked={isMultipleChoice}
91 onCheckedChange={setIsMultipleChoice}
92 />
93 </div>
94
95 <div className="grid gap-2">
96 <Label htmlFor="ends-at">{t('End Date (optional)')}</Label>
97 <div className="flex items-center gap-2">
98 <Input
99 id="ends-at"
100 type="datetime-local"
101 value={endsAt}
102 onChange={(e) => setEndsAt(e.target.value)}
103 />
104 <Button
105 type="button"
106 variant="ghost-destructive"
107 size="icon"
108 onClick={() => setEndsAt('')}
109 disabled={!endsAt}
110 title={t('Clear end date')}
111 >
112 <Eraser />
113 </Button>
114 </div>
115 </div>
116
117 <div className="grid gap-2">
118 <Label htmlFor="relay-urls">{t('Relay URLs (optional, comma-separated)')}</Label>
119 <Input
120 id="relay-urls"
121 value={relayUrls}
122 onChange={(e) => setRelayUrls(e.target.value)}
123 placeholder="wss://relay1.com, wss://relay2.com"
124 />
125 </div>
126
127 <div className="grid gap-2">
128 <InfoCard
129 variant="alert"
130 title={t('This is a poll note.')}
131 content={t(
132 'Unlike regular notes, polls are not widely supported and may not display on other clients.'
133 )}
134 />
135
136 <Button variant="ghost-destructive" className="w-full" onClick={() => setIsPoll(false)}>
137 {t('Remove poll')}
138 </Button>
139 </div>
140 </div>
141 )
142 }
143