Files
crowd-funder-for-time-pwa/doc/notification-from-api-call.md
Jose Olarte III 8ba84888ee feat(android): improve New Activity notification copy in TimeSafariNativeFetcher
Aggregate API rows into one notification with Starred Project Update(s) titles,
plan names in typographic quotes, and "+ N more have been updated." for multiples.
Stop emitting the empty-data "No Project Updates" fallback. Sync internal docs.
2026-03-31 19:50:14 +08:00

8.1 KiB
Raw Blame History

New Activity Notification (API-Driven Daily Message)

Purpose: Integrate the daily-notification-plugins 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 users 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 plugins 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 plugins 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

  • Confirm iOS native fetcher / dual schedule
    Plugin exposes configureNativeFetcher on iOS. Confirm whether the plugin expects an iOS-specific native fetcher registration (similar to Androids 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