forked from trent_larson/crowd-funder-for-time-pwa
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.
8.1 KiB
8.1 KiB
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:
- 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. - Cache – Fetched content is stored in the plugin’s cache.
- 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
updateStarredPlansso the fetcher knows which plans to query. - Expose UI to enable/disable the “New Activity” notification and choose a time, and call
scheduleDualNotification/cancelDualScheduleaccordingly.
Tasks Finished
- Configure native fetcher on startup and identity
- Added
configureNativeFetcherIfReady()insrc/services/notifications/nativeFetcherConfig.ts(readsactiveDidandapiServerfrom DB, gets JWT viagetHeaders(did), callsDailyNotification.configureNativeFetcher()). - Called from
main.capacitor.tsafter the 2s delay (with deep link registration). - Called from
AccountViewView.initializeState()when on native andactiveDidis set; when New Activity is enabled, also callsupdateStarredPlans(settings.starredPlanHandleIds).
- Added
- Implement real API calls in Android native fetcher
android/app/src/main/java/app/timesafari/TimeSafariNativeFetcher.javaimplementsNativeNotificationContentFetcher: POST to/api/v2/report/plansLastUpdatedBetweenwithplanIds(from SharedPreferencesdaily_notification_timesafari/starredPlanIds) andafterId; whendatais non-empty, builds one aggregatedNotificationContent(title Starred Project Update or Starred Project Updates, body fromplan.namewith typographic quotes, thenhas been updated.or+ N more have been updated.); whendatais empty, returns an empty list (no “no updates” notification); updateslast_acked_jwt_idfor pagination when content is returned.- Registered in
MainActivity.onCreate()viaDailyNotificationPlugin.setNativeFetcher(new TimeSafariNativeFetcher(this)).
- Sync starred plan IDs
- Shared helper
syncStarredPlansToNativePlugin(planIds)insrc/services/notifications/syncStarredPlansToNativePlugin.ts(exported fromsrc/services/notifications/index.ts) callsDailyNotification.updateStarredPlanson native only; ignoresUNIMPLEMENTED. - When user enables New Activity,
scheduleNewActivityDualNotification()uses the helper withsettings.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 ifnotifyingNewActivityTimeis set so prefetch sees the current list without reopening Account.
- Shared helper
- 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().
- Added
- 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
starredPlanHandleIdstoAccountSettingsininterfaces/accountView.ts.
- Unhid the “New Activity Notification” block in
- Exports
src/services/notifications/index.tsexportsconfigureNativeFetcherIfReady,syncStarredPlansToNativePlugin,buildDualScheduleConfig,timeToCron,timeToCronFiveMinutesBefore, andDualScheduleConfigInput.
Checklist of Remaining Tasks
iOS
- Confirm iOS native fetcher / dual schedule
Plugin exposesconfigureNativeFetcheron iOS. Confirm whether the plugin expects an iOS-specific native fetcher registration (similar to Android’ssetNativeFetcher) and, if so, register a TimeSafari fetcher implementation for iOS so API-driven notifications work on iPhone. - Verify dual schedule on iOS
TestscheduleDualNotificationandcancelDualScheduleon 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 emptystarredPlanHandleIds; confirm no crash; the native fetcher returns no Endorser-derived items when there is nothing to query or no new rows (seeTimeSafariNativeFetcher). - Test JWT expiry
Ensure behavior when the token passed toconfigureNativeFetcherhas expired (e.g. app in background for a long time); document or implement refresh (e.g. re-callconfigureNativeFetcherIfReadyon foreground or when opening Account).
Optional enhancements
- Offers endpoints
ExtendTimeSafariNativeFetcher(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 |