Files
crowd-funder-for-time-pwa/doc/notification-from-api-call.md
T
Jose Olarte III 7d87a746f9 feat(ios): register Swift TimeSafariNativeFetcher for New Activity notifications
Add TimeSafariNativeFetcher (plansLastUpdatedBetween parity with Android) and
call DailyNotificationPlugin.registerNativeFetcher from AppDelegate before JS
configureNativeFetcher; broaden DailyNotificationDelivered scheduled_time types
in willPresent. Wire the new file into the App target; normalize PBX object IDs
to 24-char hex.

Document plugin ≥3 handoff (consuming-app-handoff-ios-native-fetcher-chained-dual),
refresh iOS/Android parity and notification-from-api-call file tables.
2026-04-02 19:02:48 +08:00

8.5 KiB
Raw Blame History

New Activity Notification (API-Driven Daily Message)

Purpose: Integrate the daily-notification-plugin’s second feature—the daily, API-driven message—into the crowd-funder (TimeSafari) app. The first feature (daily static reminder) is already integrated; this document covers the plan, completed work, and remaining tasks for the API-driven flow.

References:

  • Plugin: daily-notification-plugin (INTEGRATION_GUIDE.md, definitions.ts)
  • Alignment outline: doc/daily-notification-alignment-outline.md
  • Help copy: HelpNotificationTypesView.vue (“New Activity Notifications”)

Plan Summary

The API-driven flow:

  1. Prefetch – Shortly before the user’s chosen time, the plugin runs a background job that calls the Endorser.ch API (e.g. plansLastUpdatedBetween, and optionally offers endpoints) using credentials supplied by the app.
  2. Cache – Fetched content is stored in the plugin’s cache.
  3. Notify – At the chosen time, the user sees a notification whose title/body come from that content (or a fallback).

The app must:

  • Configure the native fetcher with apiBaseUrl, activeDid, and a JWT so the plugin’s background workers can call the API.
  • Implement the native fetcher (or register an implementation) so the plugin can perform the actual HTTP requests and parse responses into notification content.
  • Sync starred plan IDs to the plugin via updateStarredPlans so the fetcher knows which plans to query.
  • Expose UI to enable/disable the “New Activity” notification and choose a time, and call scheduleDualNotification / cancelDualSchedule accordingly.

Tasks Finished

  • Configure native fetcher on startup and identity
    • Added configureNativeFetcherIfReady() in src/services/notifications/nativeFetcherConfig.ts (reads activeDid and apiServer from DB, gets JWT via getHeaders(did), calls DailyNotification.configureNativeFetcher()).
    • Called from main.capacitor.ts after the 2s delay (with deep link registration).
    • Called from AccountViewView.initializeState() when on native and activeDid is set; when New Activity is enabled, also calls updateStarredPlans(settings.starredPlanHandleIds).
  • Implement real API calls in Android native fetcher
    • android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.java implements NativeNotificationContentFetcher: POST to /api/v2/report/plansLastUpdatedBetween with planIds (from SharedPreferences daily_notification_timesafari / starredPlanIds) and afterId; when data is non-empty, builds one aggregated NotificationContent (title Starred Project Update or Starred Project Updates, body from plan.name with typographic quotes, then has been updated. or + N more have been updated.); when data is empty, returns an empty list (no “no updates” notification); updates last_acked_jwt_id for pagination when content is returned.
    • Registered in MainActivity.onCreate() via DailyNotificationPlugin.setNativeFetcher(new TimeSafariNativeFetcher(this)).
  • Sync starred plan IDs
    • Shared helper syncStarredPlansToNativePlugin(planIds) in src/services/notifications/syncStarredPlansToNativePlugin.ts (exported from src/services/notifications/index.ts) calls DailyNotification.updateStarredPlans on native only; ignores UNIMPLEMENTED.
    • When user enables New Activity, scheduleNewActivityDualNotification() uses the helper with settings.starredPlanHandleIds ?? [].
    • When Account view loads and New Activity is on, initializeState() uses the helper with the same list.
    • When the user stars or unstars on a project (ProjectViewView.toggleStar), after a successful settings save, the helper runs if notifyingNewActivityTime is set so prefetch sees the current list without reopening Account.
  • Dual schedule config and scheduling
    • Added src/services/notifications/dualScheduleConfig.ts: timeToCron(), timeToCronFiveMinutesBefore(), buildDualScheduleConfig({ notifyTime, title?, body? }) (contentFetch 5 min before, userNotification at chosen time).
    • When user enables New Activity and picks a time, app calls DailyNotification.scheduleDualNotification({ config }) with this config.
    • When user disables New Activity, app calls DailyNotification.cancelDualSchedule().
  • UI for New Activity notification
    • Unhid the “New Activity Notification” block in AccountViewView.vue (toggle + accessibility).
    • Enable flow: time dialog → save settings → on native, scheduleNewActivityDualNotification(timeText) (configure fetcher, updateStarredPlans, scheduleDualNotification).
    • Disable flow: on native, cancelDualSchedule() then save and clear settings.
    • Added starredPlanHandleIds to AccountSettings in interfaces/accountView.ts.
  • Exports
    • src/services/notifications/index.ts exports configureNativeFetcherIfReady, syncStarredPlansToNativePlugin, buildDualScheduleConfig, timeToCron, timeToCronFiveMinutesBefore, and DualScheduleConfigInput.

Checklist of Remaining Tasks

iOS

Parity outline (API, starred plans, plugin vs app work): See doc/new-activity-notifications-ios-android-parity.md.

  • Confirm iOS native fetcher / dual schedule
    Plugin exposes configureNativeFetcher on iOS. Confirm whether the plugin expects an iOS-specific native fetcher registration (similar to Android’s setNativeFetcher) and, if so, register a TimeSafari fetcher implementation for iOS so API-driven notifications work on iPhone.
  • Verify dual schedule on iOS
    Test scheduleDualNotification and cancelDualSchedule on iOS; ensure content fetch and user notification fire at the expected times and that foreground/background behavior matches expectations.

Testing and hardening

  • Test full flow on Android
    Enable New Activity, set time, wait for prefetch and notification (or use a short rollover for testing). Confirm notification shows with API-derived or fallback content.
  • Test full flow on iOS
    Same as Android: enable, set time, verify prefetch and notification delivery and content.
  • Test with no starred plans
    Enable New Activity with empty starredPlanHandleIds; confirm no crash; the native fetcher returns no Endorser-derived items when there is nothing to query or no new rows (see TimeSafariNativeFetcher).
  • Test JWT expiry
    Ensure behavior when the token passed to configureNativeFetcher has expired (e.g. app in background for a long time); document or implement refresh (e.g. re-call configureNativeFetcherIfReady on foreground or when opening Account).

Optional enhancements

  • Offers endpoints
    Extend TimeSafariNativeFetcher (and any iOS fetcher) to call offers endpoints (e.g. offers, offersToPlansOwnedByMe) and merge with project-update content for richer notifications.
  • Documentation
    Add a short “New Activity notifications” section to BUILDING.md or a user-facing help page describing how the feature works and how to troubleshoot (e.g. no notification, wrong content, JWT/API errors).

File Reference

Area Files
Fetcher config src/services/notifications/nativeFetcherConfig.ts
Starred list → plugin src/services/notifications/syncStarredPlansToNativePlugin.ts
Dual schedule config src/services/notifications/dualScheduleConfig.ts
Notification exports src/services/notifications/index.ts
Startup src/main.capacitor.ts
Account UI and flow src/views/AccountViewView.vue
Project star / unstar src/views/ProjectViewView.vue (toggleStar)
Settings type src/interfaces/accountView.ts
Android native fetcher android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.java
Android registration android/app/src/main/java/app/timesafari/MainActivity.java
iOS native fetcher ios/App/App/TimeSafariNativeFetcher.swift
iOS registration ios/App/App/AppDelegate.swift (DailyNotificationPlugin.registerNativeFetcher)
Plugin 3.x handoff doc/consuming-app-handoff-ios-native-fetcher-chained-dual.md