diff --git a/doc/new-activity-notifications-ios-android-parity.md b/doc/new-activity-notifications-ios-android-parity.md index 8c2cbc91..80882198 100644 --- a/doc/new-activity-notifications-ios-android-parity.md +++ b/doc/new-activity-notifications-ios-android-parity.md @@ -113,7 +113,48 @@ Plugin work item **§4A.3** should reflect this: document the chosen strategy (c --- -## 6. Acceptance checklist (iOS vs Android product intent) +## 6. Handoff to plugin repo (Cursor / isolated workspace) + +Use this section when **daily-notification-plugin** is open **without** the TimeSafari app tree, so implementers do not depend on paths that only exist in crowd-funder-for-time-pwa. + +### 6.1 Bring reference material into scope + +| Source (this app repo) | Why | +|------------------------|-----| +| `android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.java` | **Canonical Endorser behavior** for New Activity: POST body, pagination, aggregation copy, prefs keys for starred IDs and `last_acked_jwt_id`. Copy or open alongside the plugin when implementing iOS fetch or `setNativeFetcher`. | +| `src/services/notifications/dualScheduleConfig.ts` | Shape the app sends to `scheduleDualNotification` (`buildDualScheduleConfig`). | +| `doc/plugin-feedback-android-dual-schedule-native-fetch-and-timing.md` | Android plugin: dual path must call native fetcher at fetch cron. | +| `doc/plugin-feedback-ios-scheduleDualNotification.md` | iOS `UNIMPLEMENTED` / PluginHeaders troubleshooting. | + +In the plugin repo itself, align with **`src/definitions.ts`** (`DualScheduleConfiguration`, `configureNativeFetcher`, `updateStarredPlans`) and **INTEGRATION_GUIDE** if present. + +### 6.2 HTTP / storage contract (match `TimeSafariNativeFetcher`) + +Implementations on **iOS** (in-plugin Swift or host `NativeNotificationContentFetcher`) should match this **unless** product explicitly changes: + +- **Method & path:** `POST` `{apiBaseUrl}/api/v2/report/plansLastUpdatedBetween` (no trailing slash mismatch on `apiBaseUrl`). +- **Headers:** `Content-Type: application/json`, `Authorization: Bearer {token}` (token from `jwtToken` or **JWT pool** selection—see Java `selectBearerTokenForRequest`: UTC day mod pool size). +- **JSON body:** `planIds` (array of strings, possibly empty), `afterId` (string; use `"0"` if none stored). +- **Starred plans:** Persist `updateStarredPlans` the same way Android’s plugin + host expect: SharedPreferences name **`daily_notification_timesafari`**, key **`starredPlanIds`** (JSON array string). iOS should use **the same keys** if using `UserDefaults` / app group so behavior matches the Java fetcher and plugin docs. +- **Pagination:** After a successful response with non-empty `data`, update **`last_acked_jwt_id`** from the last row’s `jwtId` (item or nested `plan.jwtId`)—see Java `updateLastAckedJwtIdFromResponse`. +- **Empty `data`:** Return **no** notification items (empty list); do not synthesize a “no updates” push from an empty result—Java returns empty `contents` when `data` is absent or empty. +- **Non-empty `data`:** One aggregated `NotificationContent`: titles **Starred Project Update** / **Starred Project Updates**, bodies use typographic quotes around first project name and **has been updated.** / **+ N more have been updated.** (see Java `parseApiResponse`). + +### 6.3 Likely plugin touchpoints (names may drift—search the repo) + +- **iOS:** `ios/Plugin/DailyNotificationPlugin.swift`, `DailyNotificationScheduleHelper.swift`, background fetch / UN notification paths; add **`updateStarredPlans`** to `pluginMethods` and persistence. +- **Android:** `DailyNotificationPlugin.kt`, `FetchWorker` / `DailyNotificationFetchWorker`, `ScheduleHelper`—per dual-schedule feedback doc. + +### 6.4 Suggested order inside the plugin repo + +1. **Android dual path** + native fetcher at fetch cron (fixes real device behavior for existing host). +2. **iOS** `updateStarredPlans` + **`plansLastUpdatedBetween`** parity (or **iOS `setNativeFetcher`** + thin Swift adapter). +3. **iOS** prefetch/notify **ordering** (§3.3): document and implement chained arm vs best-effort. +4. Release / tag → consuming app bumps **`@timesafari/daily-notification-plugin`**, `npx cap sync`, `pod install`. + +--- + +## 7. Acceptance checklist (iOS vs Android product intent) - [ ] Prefetch uses **plansLastUpdatedBetween** (or host fetcher with identical behavior), not only `offers` GET. - [ ] **Starred plan IDs** from settings change what is queried (`updateStarredPlans` works on iOS).