feat(polls): implement MSC3381 polls#589
Conversation
55449fb to
2ea98d6
Compare
|
is it using the ui you showed in matrix chat or the ui of cinnyapp/cinny#2763 ? |
The UI from Cinny |
There was a problem hiding this comment.
Pull request overview
Implements Matrix MSC3381 poll support in the room timeline, including poll creation UI, vote/end handling, and a feature-flag gate via client config.
Changes:
- Add poll event types/SDK exports and a
features.pollsclient-config flag (defaultfalse). - Add
PollCreatorDialog(poll creation) andPollEvent(timeline renderer + tallying/end action), plus supporting styles and tests. - Integrate poll rendering/filtering into timeline processing and add a
/pollcommand pathway inRoomInput.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/matrix/room.ts | Adds MSC3381 (unstable) poll event types to the room message type enum. |
| src/types/matrix-sdk.ts | Re-exports poll constants/types from matrix-js-sdk for app usage. |
| src/app/hooks/useCommands.ts | Adds /poll command entry for command discovery/autocomplete. |
| src/app/hooks/useClientConfig.ts | Extends client config typing with features.polls. |
| src/app/hooks/timeline/useTimelineEventRenderer.tsx | Adds timeline rendering for poll start events and suppresses rendering for response/end events. |
| src/app/hooks/timeline/useProcessedTimeline.ts | Filters poll response/end events so they don’t appear as timeline items. |
| src/app/features/room/RoomInput.tsx | Adds feature-gated poll creator dialog launch (via /poll) and sends poll start events. |
| src/app/features/room/poll/PollEvent.tsx | Implements poll content extraction, tally computation, voting, ending, and results UI. |
| src/app/features/room/poll/pollEvent.test.ts | Adds unit tests for poll parsing, tallying, and expiry formatting. |
| src/app/features/room/poll/PollEvent.css.ts | Adds styles for poll option controls and layout. |
| src/app/features/room/poll/PollCreatorDialog.tsx | Implements poll creation dialog (question/options/visibility/expiry/max selections). |
| src/app/features/room/poll/PollCreatorDialog.css.ts | Adds styles for the poll creation dialog. |
| src/app/features/room/poll/index.ts | Exports poll feature components/types. |
| config.json | Adds features.polls: false feature flag to config. |
| .changeset/feat-polls.md | Adds changeset entry for the new polls feature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add stable m.poll.* type aliases alongside unstable MSC3381 types - Register stable poll types in useTimelineEventRenderer - Fix datetime-local timezone bug in PollCreatorDialog (UTC→local) - Add FocusOutline from folds for keyboard a11y on poll options - Add MatrixEventEvent.Decrypted listener for encrypted poll responses - Support multi-select polls with Checkbox component
- Add stable m.poll.* type aliases alongside unstable MSC3381 types - Register stable poll types in useTimelineEventRenderer - Fix datetime-local timezone bug in PollCreatorDialog (UTC→local) - Add FocusOutline from folds for keyboard a11y on poll options - Add MatrixEventEvent.Decrypted listener for encrypted poll responses - Support multi-select polls with Checkbox component
|
the amount of commits and line changes this pr wants to merge seems a bit off for the problem stated 🤔 |
- Add stable m.poll.* type aliases alongside unstable MSC3381 types - Register stable poll types in useTimelineEventRenderer - Fix datetime-local timezone bug in PollCreatorDialog (UTC→local) - Add FocusOutline from folds for keyboard a11y on poll options - Add MatrixEventEvent.Decrypted listener for encrypted poll responses - Support multi-select polls with Checkbox component
- Add stable m.poll.* type aliases alongside unstable MSC3381 types - Register stable poll types in useTimelineEventRenderer - Fix datetime-local timezone bug in PollCreatorDialog (UTC→local) - Add FocusOutline from folds for keyboard a11y on poll options - Add MatrixEventEvent.Decrypted listener for encrypted poll responses - Support multi-select polls with Checkbox component
- Add stable m.poll.* type aliases alongside unstable MSC3381 types - Register stable poll types in useTimelineEventRenderer - Fix datetime-local timezone bug in PollCreatorDialog (UTC→local) - Add FocusOutline from folds for keyboard a11y on poll options - Add MatrixEventEvent.Decrypted listener for encrypted poll responses - Support multi-select polls with Checkbox component
- Attachment card wrapper with header row (poll type label, ended state, vote count) - RadioButton per answer, ProgressBar for results (disclosed/ended undisclosed) - Reactive ended state via Poll.isEnded + PollModelEvent.End listener - End poll confirmation modal for undisclosed poll creators - messageLayout prop wired through to Attachment outlined for bubble layout
The polls toolbar button is removed from RoomInput. The /poll slash command now opens PollCreator directly, keeping the feature accessible via keyboard-driven workflow without cluttering the toolbar.
The poll response and end-poll sendEvent calls were missing their event content bodies and .catch() handlers, so voting and ending polls were silently broken. Add the m.relates_to, response answers, and msc3381 compat fields.
- Replace PollContent with PollEvent (more complete implementation): closesAt/expiry, voter names, Checkbox for multi-select, MSC3381 end-time cutoff, permission check for unauthorized end events - Add missing PollEvent.css.ts and fix broken MessageEvent import - Update sendEvent calls in PollEvent to standard project pattern - Delete PollContent.tsx, usePollTally.ts, usePollTally.test.ts - Fix poll creator option/question inputs to fill dialog width
Adds a 'Poll ends after' row with preset buttons (No end / 1 day / 3 days / 7 days / 14 days). The selected duration is injected as closes_at (ms timestamp) into the m.poll.start subtype content, which PollEvent already reads to show the expiry countdown.
- PollCreator: add 2h/6h/12h hour presets; replace day-only buttons with unified preset list (No end/2h/6h/12h/1d/3d/7d/14d) + Custom button that reveals a datetime-local input - BookmarksList: add 1h/3h/6h/1d/3d preset chips above the reminder datetime input for quick selection
- Add 'export * from matrix-js-sdk/lib/@types/polls' to src/types/matrix-sdk.ts so M_POLL_END, M_POLL_KIND_DISCLOSED, M_POLL_RESPONSE, M_POLL_START are accessible via the app's import boundary - Replace non-existent PollContent import in useTimelineEventRenderer with PollEvent from its actual path (features/room/poll/PollEvent) - Use M_POLL_START from $types/matrix-sdk instead of direct matrix-js-sdk import - Remove unused injectedExperimentFlags variable from vite.config.ts
|
Thanks for the note! The branch was initially developed on top of my integration branch (which merges all feature branches together for local testing), which bloated the commit/diff count significantly beyond what the feature itself required. The branch has since been rebased onto |
Description
>⚠️ Merge together with SableClient/docs#12
Implements Matrix polls (MSC3381) with a creator dialog and timeline renderer.
PollCreatorDialog— question, 2–20 answers, disclosed/undisclosed type, voter visibility toggle (show/hide voter names), poll duration (presets: 1h / 12h / 24h / 48h / 1 week / custom date-time)PollEventtimeline renderer — vote buttons, live/final results bars, expandable voter lists per answer, expiry countdown in footer, auto-expire enforcement, end-poll actionPollEventfromorg.matrix.msc3381.poll.start,.poll.response, and.poll.endevents via paginated room timeline; respectsshow_voter_namesandcloses_atfields. Also handles stablem.poll.*types./pollslash command inRoomInput, gated byfeatures.pollsinconfig.json(defaultsfalse)m.poll.*) and unstable (org.matrix.msc3381.poll.*) event typesMatrixEventEvent.DecryptedlistenerFocusOutlinefocus-visible styles on poll optionsSpec: MSC3381
Related upstream issue: cinnyapp/cinny#563
Documentation: SableClient/docs#12
Fixes #
Type of change
Checklist:
AI disclosure:
Poll state is assembled inline in
PollEventby paginating the room timeline to collectpoll.responseevents keyed by sender, deduplicating so only the last vote per user counts, and tallying totals and percentages.PollEventrenders the resulting state as vote buttons with fill-bars; it re-fetches onRoomEvent.TimelineandMatrixEventEvent.Decryptedso it stays live even for encrypted responses.PollCreatorDialogbuilds them.poll.startcontent withcrypto.randomUUID()answer IDs and sends viamx.sendEvent. The/pollslash command is conditionally available based onclientConfig.features?.polls. Theshow_voter_namesflag is a client-side display hint stored in the poll start event; thecloses_atfield is a Unix ms timestamp enforced client-side (voting blocked after expiry, UI shows countdown).