Implement coordinator pause notification payload builder
epic-pause-status-notifications-foundation-task-011 — Implement the payload builder method that composes a localised coordinator notification for a peer mentor pause event. Interpolate mentor name and expected return date into the message template, enforce character constraints with truncation, and produce a FCM-ready payload map.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement PauseNotificationBuilder as a class with only static methods (no instance state) to keep it stateless and easily testable without construction. Localisation strings should be resolved via Flutter's AppLocalizations (generated from .arb files); the builder receives a BuildContext or an AppLocalizations instance as a parameter to stay testable without a widget tree. Truncation logic: compute the maximum allowed length for mentorName as maxBodyLength - templateLengthWithEmptyName - 1 (for ellipsis), then truncate mentorName before interpolation. This avoids truncating in the middle of multi-byte Unicode characters — use String.characters from the characters package for grapheme-cluster-safe truncation.
Do not truncate in the middle of words if possible: prefer truncating at the last word boundary before the limit. Date formatting: use DateFormat.yMMMd(locale) from the intl package. Locale string should be derived from the organisation's configured locale stored in Riverpod state, not from the device locale, to ensure notifications match the organisation's language even on devices with different system language.
Testing Requirements
Unit tests using flutter_test. All tests are synchronous. Required test cases: (1) standard input with mentorName and returnDate produces correct interpolated title and body, (2) returnDate null uses body_no_date template, (3) very long mentorName (>100 chars) triggers truncation and body remains ≤ 240 chars, (4) very long mentorName where even truncated version with ellipsis does not fit triggers further truncation correctly, (5) toFcmPayloadMap() on returned payload contains required FCM keys, (6) returned recipientType is PauseNotificationRecipientType.coordinator. Use test fixtures for localisation strings rather than loading real .arb files — inject strings via a TestLocalisationDelegate or simple map.
The org membership table structure used to resolve coordinator relationships may differ from what the repository assumes, causing incorrect coordinator lookup or missing rows for mentors in multi-chapter scenarios.
Mitigation & Contingency
Mitigation: Review the existing org membership table schema and RLS policies before writing repository queries. Align query logic with the patterns already used by peer-mentor-status-repository and multi-chapter-membership-service.
Contingency: If schema differs, add an adapter layer in the repository that normalises the membership resolution and document the discrepancy for the data team. Fall back to coordinator lookup via the feature's own stored coordinator_id field if org membership join fails.
Device tokens stored in the database may be stale or unregistered, causing FCM dispatch failures that silently drop coordinator notifications — the primary coordination safeguard of this feature.
Mitigation & Contingency
Mitigation: Implement token validation on every dispatch call and handle FCM's NOT_REGISTERED error by flagging the token as invalid in the database. Reuse the token refresh pattern already established by fcm-token-manager.
Contingency: If push delivery fails after retry, ensure the in-app notification record is always written regardless of push outcome so coordinators can still see the event in the notification centre.
The optional reason field may contain special characters, emoji, or non-Latin scripts that exceed the 200-character byte limit when FCM encodes the payload, causing delivery failures.
Mitigation & Contingency
Mitigation: Enforce the 200-character limit on Unicode code point count, not byte count, in the payload builder. Add a unit test with multi-byte input strings.
Contingency: If an oversized payload is detected at dispatch time, strip the reason field from the push notification body and note 'See in-app notification for full reason' to preserve delivery.