|
1 | 1 | # Events & Calendars |
2 | 2 |
|
3 | | -This## What's Implemented |
| 3 | +This document explains the Event model, how events are created and displayed, how visibility works, how calendars fit in, and the comprehensive notification system for event reminders and updates. |
| 4 | + |
| 5 | +## What's Implemented |
4 | 6 | - **RSVPs/Attendees**: `EventAttendance` model with `person_id`, `event_id`, `status` (interested/going/not_going), guarded by privacy/policy. |
5 | 7 | - **ICS Export**: Export endpoint at `/events/:id/ics` that renders VEVENT from name/description/time/location. |
| 8 | +- **Event Reminder System**: Comprehensive notification system for upcoming events with multiple delivery channels. |
| 9 | +- **Event Update Notifications**: Automatic notifications when event details change. |
| 10 | +- **Location Support**: Full location support with polymorphic `LocatableLocation` model. |
6 | 11 |
|
7 | 12 | ## What's Not Implemented Yet |
8 | 13 | - **Recurrence**: No repeat rules; all events are single instances. |
9 | 14 | - **Calendar Entries**: `CalendarEntry` exists but is not used to associate events to calendars. |
10 | | -- **Event Location/Address**: Address association is commented out; no geocoding integration active. |
11 | 15 | - **Advanced RSVP Features**: No waitlists, capacity limits, or guest allowances. |
12 | | -- **Event Reminders**: No notification system for upcoming events. |
13 | | -- **Event Updates/Changes**: No notification system when event details change. |
14 | | -- **Bulk Operations**: No bulk event creation, editing, or management tools.e explains the Event model, how events are created and displayed, how visibility works, and how calendars fit in. It also notes what is not yet implemented (RSVPs, attendees, recurrence) so maintainers know the current scope. |
| 16 | +- **Bulk Operations**: No bulk event creation, editing, or management tools. |
15 | 17 |
|
16 | 18 | ## Event Attendance & RSVPs |
| 19 | + |
| 20 | +## Event Reminder & Notification System |
| 21 | + |
| 22 | +### Components Overview |
| 23 | +The event notification system consists of several integrated components: |
| 24 | + |
| 25 | +- **EventReminderNotifier**: Noticed event class for sending event reminder notifications |
| 26 | +- **EventReminderJob**: Background job for processing reminder notifications for all attendees |
| 27 | +- **EventReminderSchedulerJob**: Schedules future reminder notifications at appropriate intervals |
| 28 | +- **EventMailer**: Handles email delivery for event reminders and updates |
| 29 | +- **EventUpdateNotifier**: Sends notifications when event details change |
| 30 | + |
| 31 | +### Event Reminder Workflow |
| 32 | + |
| 33 | +#### Scheduling Reminders |
| 34 | +1. When an event is created or updated, `EventReminderSchedulerJob` is triggered |
| 35 | +2. The scheduler calculates appropriate reminder times based on event start time: |
| 36 | + - **24 hours before**: For events more than 24 hours away |
| 37 | + - **1 hour before**: For events more than 1 hour away |
| 38 | + - **At start time**: For immediate notifications |
| 39 | +3. Background jobs are scheduled using `perform_at` for each reminder interval |
| 40 | +4. Only "going" attendees receive reminder notifications |
| 41 | + |
| 42 | +#### Notification Delivery |
| 43 | +1. `EventReminderJob` processes each scheduled reminder: |
| 44 | + - Finds all attendees with "going" status |
| 45 | + - Creates `EventReminderNotifier` instances for each attendee |
| 46 | + - Respects user notification preferences |
| 47 | +2. `EventReminderNotifier` handles multi-channel delivery: |
| 48 | + - **Action Cable**: Real-time in-app notifications via `NotificationsChannel` |
| 49 | + - **Email**: HTML emails with event details (15-minute delay to batch notifications) |
| 50 | +3. Email delivery is conditional based on: |
| 51 | + - User has email address configured |
| 52 | + - User has `notify_by_email` preference enabled |
| 53 | + - User has `event_reminders` preference enabled |
| 54 | + - Anti-spam: Only one email per unread event notifications |
| 55 | + |
| 56 | +#### Event Update Notifications |
| 57 | +1. When event details change, `EventUpdateNotifier` is triggered |
| 58 | +2. Sends notifications to all attendees about the changes |
| 59 | +3. Includes information about what specific attributes changed |
| 60 | +4. Uses the same delivery channels as reminder notifications |
| 61 | + |
| 62 | +### Notification Preferences |
| 63 | +Users can control event notifications through their preferences: |
| 64 | +- `event_reminders`: Enable/disable event reminder notifications |
| 65 | +- `notify_by_email`: Enable/disable email notifications globally |
| 66 | +- `show_conversation_details`: Control visibility of conversation details in emails |
| 67 | + |
| 68 | +### Anti-Spam & Batching |
| 69 | +- **Email Batching**: 15-minute delay on email delivery to group related notifications |
| 70 | +- **Duplicate Prevention**: Only one email per unread notification group per event |
| 71 | +- **Preference Respect**: All notifications respect user preferences and can be disabled |
| 72 | + |
| 73 | +### Technical Implementation Details |
| 74 | + |
| 75 | +#### Classes & Responsibilities |
| 76 | +- **`BetterTogether::EventReminderNotifier`**: Noticed event class extending `ApplicationNotifier` |
| 77 | + - Handles multi-channel delivery (Action Cable + Email) |
| 78 | + - Includes anti-spam logic and preference checking |
| 79 | + - Generates localized notification content |
| 80 | +- **`BetterTogether::EventReminderJob`**: Background job extending `ApplicationJob` |
| 81 | + - Processes events and finds "going" attendees |
| 82 | + - Creates notifier instances for each attendee |
| 83 | + - Handles error cases gracefully (missing events, connection issues) |
| 84 | + - Queue: `:notifications` with retry configuration |
| 85 | +- **`BetterTogether::EventReminderSchedulerJob`**: Scheduling job |
| 86 | + - Calculates appropriate reminder intervals based on event timing |
| 87 | + - Schedules future `EventReminderJob` instances |
| 88 | + - Prevents scheduling reminders for past events or drafts |
| 89 | +- **`BetterTogether::EventMailer`**: Mailer class extending `ApplicationMailer` |
| 90 | + - Renders HTML emails with event details |
| 91 | + - Uses Rails 7+ parameter pattern (`params[:key]`) |
| 92 | + - Includes event location, timing, and registration information |
| 93 | +- **`BetterTogether::EventUpdateNotifier`**: Handles event change notifications |
| 94 | + - Triggers when event attributes are modified |
| 95 | + - Notifies all attendees (not just "going" status) |
| 96 | + - Includes information about what changed |
| 97 | + |
| 98 | +#### Notification Timing Strategy |
| 99 | +- **24-hour reminders**: For events starting more than 24 hours in the future |
| 100 | +- **1-hour reminders**: For events starting more than 1 hour in the future |
| 101 | +- **Start-time notifications**: For events starting within the hour |
| 102 | +- **Update notifications**: Immediate when event details change |
| 103 | + |
| 104 | +#### Queue & Background Processing |
| 105 | +- Uses `:notifications` queue for all event-related jobs |
| 106 | +- Retry configuration: Up to 5 attempts with polynomial backoff |
| 107 | +- Discard policy: `ActiveRecord::RecordNotFound` errors are discarded |
| 108 | +- Error handling: Jobs complete gracefully for missing/invalid events |
| 109 | + |
| 110 | +### Models & Data Flow |
| 111 | +- **Event**: Has many `event_attendances` and `attendees` (people) |
| 112 | +- **EventAttendance**: Links person to event with status (interested/going/not_going) |
| 113 | +- **Noticed::Notification**: Stores notification records with read/unread status |
| 114 | +- **Noticed::Event**: Base class for all notifier events |
| 115 | + |
| 116 | +### Testing Coverage |
| 117 | +The event reminder system has comprehensive test coverage: |
| 118 | + |
| 119 | +#### EventReminderNotifier Specs |
| 120 | +- Tests notification content generation (title, body, identifiers) |
| 121 | +- Validates parameter handling and defaults |
| 122 | +- Verifies unread count inclusion in messages |
| 123 | +- Uses mock objects following established patterns |
| 124 | + |
| 125 | +#### EventReminderJob Specs |
| 126 | +- Tests attendee filtering and notification delivery |
| 127 | +- Validates error handling for missing/invalid events |
| 128 | +- Confirms queue configuration and retry policies |
| 129 | +- Verifies reminder type parameter handling |
| 130 | + |
| 131 | +#### EventMailer Specs |
| 132 | +- Tests email rendering with event details |
| 133 | +- Validates headers, subject lines, and recipient handling |
| 134 | +- Tests localization support |
| 135 | +- Confirms delivery methods work correctly |
| 136 | + |
| 137 | +#### Integration Testing |
| 138 | +- Tests complete notification workflow from event creation to delivery |
| 139 | +- Validates preference-based filtering |
| 140 | +- Tests anti-spam and batching behavior |
| 141 | +- Ensures proper authorization checks |
17 | 142 | - Model: `BetterTogether::EventAttendance` |
18 | 143 | - Associations: `belongs_to :event`, `belongs_to :person` |
19 | 144 | - Status enum: `interested`, `going`, `not_going` |
|
0 commit comments