diff --git a/LOTUS_FEATURES.md b/LOTUS_FEATURES.md index 3c059beb1..5efe081ae 100644 --- a/LOTUS_FEATURES.md +++ b/LOTUS_FEATURES.md @@ -866,6 +866,7 @@ player.kick). - `PollCreator.tsx` creates stable `m.poll.start` events (with a text fallback body for non-poll clients) - Supports 2 to 10 answer options; single-choice or multiple-choice +- **Max selections** — for a multiple-choice poll, a "Voters can pick up to N of M options" control sets `max_selections` (2 … option count), so you can run "pick your top 2" polls rather than only "select all". Defaults to the option count (unchanged "select all that apply" behavior) until you lower it; the display side already enforces the cap ("Select up to N") - **Results visibility toggle** — _Show live results_ (disclosed, default) vs _Hidden until ended_ (undisclosed) - Accessible via the `Icons.OrderList` button in the composer toolbar diff --git a/src/app/features/room/PollCreator.tsx b/src/app/features/room/PollCreator.tsx index ef2550c5c..21a3ae46c 100644 --- a/src/app/features/room/PollCreator.tsx +++ b/src/app/features/room/PollCreator.tsx @@ -34,6 +34,10 @@ export function PollCreator({ roomId, onClose }: PollCreatorProps) { const [question, setQuestion] = useState(''); const [options, setOptions] = useState(['', '']); const [isMultiple, setIsMultiple] = useState(false); + // For multiple-choice polls: the most options a voter may pick. Defaults high + // so an untouched multiple poll means "select all that apply" (the previous + // behavior); the effective value is clamped to the current option count. + const [maxSelections, setMaxSelections] = useState(10); // Results visibility: disclosed (live results, default) vs undisclosed (hidden // until the poll is ended). const [disclosed, setDisclosed] = useState(true); @@ -85,7 +89,9 @@ export function PollCreator({ roomId, onClose }: PollCreatorProps) { 'm.poll': { question: { 'm.text': trimmedQuestion }, answers: filledOptions.map((o, i) => ({ 'm.id': `${i}`, 'm.text': o })), - max_selections: isMultiple ? filledOptions.length : 1, + max_selections: isMultiple + ? Math.min(Math.max(2, maxSelections), filledOptions.length) + : 1, kind: disclosed ? 'm.poll.disclosed' : 'm.poll.undisclosed', }, body: fallbackBody, @@ -222,6 +228,32 @@ export function PollCreator({ roomId, onClose }: PollCreatorProps) { ); })} + {isMultiple && ( + + + Voters can pick up to + + ) => + setMaxSelections( + Math.min(options.length, Math.max(2, parseInt(e.target.value, 10) || 2)), + ) + } + style={{ width: '4rem' }} + aria-label="Maximum selections per voter" + /> + + of {options.length} options + + + )} {/* Results visibility */}