feat(events): event / RSVP cards (LC-491) #467

Merged
longjacksonle merged 4 commits from feat/LC-491-events-rsvp into main 2026-06-28 01:17:24 +02:00

What

Event / RSVP cards (LC-491): post an event with a date, optional location, and Going / Maybe / Can't-go RSVP, exported to the room's iCal feed. Inspired by Discord events.

How (reuses the poll engine)

An event is a poll with a start time: migration 0072 adds polls.event_at + event_location. A poll with event_at set renders as an event card and exports to iCal; everything else is an ordinary poll, unchanged.

  • Create: a composer "Event" button opens a new modal (title + datetime-local + optional location), POSTing /room/{id}/event. The handler validates and calls db::polls::create_event, which inserts the message + poll (single-choice, non-anonymous, never-closes) + three RSVP options (in the creator's locale).
  • RSVP: the options are poll options, so voting reuses POST /poll/{id}/vote, PollUpdated WS re-render, and build_poll_view unchanged. poll_block.html gains an event-card header (calendar + when + location) when event_at is set.
  • iCal: the existing GET /feed/ical/{token} now emits a VEVENT per event (DTSTART = event time, SUMMARY = title, LOCATION), alongside the existing poll-closing events.

Tests

routes_polls.rs: create-event stores event_at/location + 3 options, RSVP reuses the vote path, the event surfaces in events_for_room, and an invalid time is rejected. just check, just test, just test-saas all green.

Notes

  • Reminders (the third reuse the issue mentions) were left out to keep scope bounded - the iCal feed already gives a subscribable calendar. Easy follow-up via the scheduled worker if wanted.

Verify

just verify builds the release binary clean; boot fails only on the missing SSO env (environmental, as in prior PRs).

## What Event / RSVP cards (LC-491): post an event with a date, optional location, and Going / Maybe / Can't-go RSVP, exported to the room's iCal feed. Inspired by Discord events. ## How (reuses the poll engine) An event is a **poll with a start time**: migration 0072 adds `polls.event_at` + `event_location`. A poll with `event_at` set renders as an event card and exports to iCal; everything else is an ordinary poll, unchanged. - **Create**: a composer "Event" button opens a new modal (title + datetime-local + optional location), POSTing `/room/{id}/event`. The handler validates and calls `db::polls::create_event`, which inserts the message + poll (single-choice, non-anonymous, never-closes) + three RSVP options (in the creator's locale). - **RSVP**: the options are poll options, so voting reuses `POST /poll/{id}/vote`, `PollUpdated` WS re-render, and `build_poll_view` **unchanged**. `poll_block.html` gains an event-card header (calendar + when + location) when `event_at` is set. - **iCal**: the existing `GET /feed/ical/{token}` now emits a `VEVENT` per event (`DTSTART` = event time, `SUMMARY` = title, `LOCATION`), alongside the existing poll-closing events. ## Tests `routes_polls.rs`: create-event stores `event_at`/location + 3 options, RSVP reuses the vote path, the event surfaces in `events_for_room`, and an invalid time is rejected. `just check`, `just test`, `just test-saas` all green. ## Notes - Reminders (the third reuse the issue mentions) were left out to keep scope bounded - the iCal feed already gives a subscribable calendar. Easy follow-up via the scheduled worker if wanted. ## Verify `just verify` builds the release binary clean; boot fails only on the missing SSO env (environmental, as in prior PRs).
Migration 0072 adds polls.event_at + event_location; a poll with event_at set is an event. db::polls::create_event inserts an event (single-choice, non-anonymous, never-closes, RSVP options) atomically; events_for_room feeds the iCal export. Poll struct + get carry the new fields.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PollView carries event_at/event_location; poll_block renders an event-card header (calendar + when + location) above the options when set, and labels the footer "responded" for events. RSVP buttons are the poll options, reusing the vote path unchanged. Token-driven CSS for the header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
POST /room/{id}/event creates an event (validates title/time/location, RSVP options in the creator's locale) via create_event. A composer "Event" button opens a new modal (date + optional location) mirroring the poll modal. The room iCal feed emits a VEVENT per event (DTSTART = event time, SUMMARY = title, LOCATION). i18n en + es.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(events): cover event create + RSVP + iCal source (LC-491)
All checks were successful
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 5s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / TruffleHog (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 5m38s
Create release / Create release from merged PR (pull_request) Has been skipped
ebae358bea
create_event stores event_at/location + 3 RSVP options; RSVP reuses the poll vote path; the event surfaces in events_for_room; an invalid time is rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-06-28 01:12:25 +02:00
longjacksonle deleted branch feat/LC-491-events-rsvp 2026-06-28 01:17:24 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!467
No description provided.