Files
crowd-funder-for-time-pwa/docs/DAILY_NOTIFICATION_BUG_DIAGNOSIS.md
Jose Olarte III 29aff896be docs: add plugin feedback for Android rollover double-fire and user content
- New doc: rollover double-fire and missing user-set content diagnosis
- Link from DAILY_NOTIFICATION_BUG_DIAGNOSIS.md; include Cursor handoff section
2026-02-26 21:12:50 +08:00

123 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Daily Notification Bugs — Diagnosis (Plugin + App)
**Context:** Fixes were applied in both the plugin and the app, but "reset doesn't fire" and "notification text defaults to fallback" still occur. This doc summarizes what was checked and what to do next.
---
## What Was Verified
### App integration (correct)
- **NativeNotificationService.ts**
- Pre-cancel is gated: only iOS calls `cancelDailyReminder()` before scheduling (lines 289305). Android skips it.
- Schedules with `id: this.reminderId` (`"daily_timesafari_reminder"`), plus `time`, `title`, `body`.
- Calls `DailyNotification.scheduleDailyNotification(scheduleOptions)` (not `scheduleDailyReminder`).
- **AccountViewView.vue**
- `editReminderNotification()` only calls `cancelDailyNotification()` when **not** Android (lines 13031305). On Android it only calls `scheduleDailyNotification()`.
So the app is not double-cancelling on Android and is passing the expected options.
### Plugin in apps node_modules (fixed code present)
- **node_modules/@timesafari/daily-notification-plugin** is at **version 1.1.4** and contains:
- **NotifyReceiver.kt:** DB idempotence is skipped when `skipPendingIntentIdempotence=true` (wrapped in `if (!skipPendingIntentIdempotence)`).
- **DailyNotificationWorker.java:** `preserveStaticReminder` read from input, stable `scheduleId` for static reminders, and `scheduleExactNotification(..., preserveStaticReminder, ...)`.
- **DailyNotificationPlugin.kt:** `cancelDailyReminder(call)` implemented.
So the **source** the app uses (from its dependency) already has the fixes.
### Plugin schedule path (correct)
- App calls `scheduleDailyNotification` → plugins `scheduleDailyNotification(call)``ScheduleHelper.scheduleDailyNotification(...)`.
- That helper calls `NotifyReceiver.cancelNotification(context, scheduleId)` then `scheduleExactNotification(..., skipPendingIntentIdempotence = true)`.
- So the “re-set” path does set `skipPendingIntentIdempotence = true` and the DB idempotence skip should apply.
---
## Likely Causes Why Bugs Still Appear
### 1. Stale Android build / old APK
The Android app compiles the plugin from:
`android/capacitor.settings.gradle`
`project(':timesafari-daily-notification-plugin').projectDir = new File('../node_modules/@timesafari/daily-notification-plugin/android')`
If the app was not fully rebuilt after the plugin in node_modules was updated, the running APK may still contain old plugin code.
**Do this:**
- In the **app** repo (`crowd-funder-for-time-pwa`):
- `./gradlew clean` (or Android Studio → Build → Clean Project)
- Build and reinstall the app (e.g. Run on device/emulator).
- Confirm youre not installing an older APK from somewhere else.
### 2. Dependency not actually updated after plugin changes
The app depends on:
```json
"@timesafari/daily-notification-plugin": "git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#master"
```
If the fixes were only made in a **different** clone (e.g. `daily-notification-plugin_test`) and never pushed to that gitea `master`, then:
- `npm install` / `npm update` in the app would not pull the fixes.
- The apps `node_modules` would only have the fixes if they were copied/linked from the fixed repo.
**Do this:**
- If the fixes live in another clone: either **push** the fixed plugin to gitea `master` and run `npm update @timesafari/daily-notification-plugin` (then `npx cap sync android`, then clean build), **or** point the app at the fixed plugin locally, e.g. in **app** `package.json`:
- `"@timesafari/daily-notification-plugin": "file:../daily-notification-plugin"`
(adjust path to your fixed plugin repo), then `npm install`, `npx cap sync android`, clean build and reinstall.
### 3. Fallback text from native fetcher (Bug 2 only)
**TimeSafariNativeFetcher.java** in the app is still a placeholder: it always returns:
- Title: `"TimeSafari Update"`
- Body: `"Check your starred projects for updates!"`
That only affects flows that **fetch** content (e.g. prefetch or any path that uses the fetcher for display). The **static** daily reminder path does not use the fetcher for display: title/body come from the schedule Intent and WorkManager input. So if you only use the “daily reminder” (one time + custom title/body), the fetcher placeholder should not be the cause. If you have any flow that relies on **fetched** content for the text, youll see that placeholder until the fetcher is implemented and wired (and optionally token persistence).
---
## Verification Steps (after clean build + reinstall)
1. **Reset / “re-set” (Bug 1)**
- Set reminder for 23 minutes from now.
- Edit and save **without changing the time**.
- Wait for the time; the notification should fire.
- In logcat, filter by the plugins tags and look for:
- `Skipping DB idempotence (skipPendingIntentIdempotence=true) for scheduleId=...`
- `Scheduling next daily alarm: id=daily_timesafari_reminder ...`
If you see these, the fixed path is running.
2. **Static text on rollover (Bug 2)**
- Set a custom title/body, let the notification fire once.
- In logcat look for:
- `DN|ROLLOVER next=... scheduleId=daily_timesafari_reminder static=true`
If you see `static=true` and the same `scheduleId`, the next occurrence should keep your custom text.
3. **Plugin version at build time**
- In the apps `node_modules/@timesafari/daily-notification-plugin/package.json`, confirm `"version": "1.1.4"` (or the version that includes the fixes).
- After that, a clean build ensures that version is whats in the APK.
---
## Summary
| Check | Status |
|-------|--------|
| App gates cancel on Android | OK |
| App calls scheduleDailyNotification with id/title/body | OK |
| Plugin in app node_modules has DB idempotence skip | OK (1.1.4) |
| Plugin in app node_modules has static rollover fix | OK |
| Plugin in app node_modules has cancelDailyReminder | OK |
| Schedule path passes skipPendingIntentIdempotence = true | OK |
**See also:** `docs/plugin-feedback-android-rollover-double-fire-and-user-content.md` — when two notifications fire (e.g. one ~3 min early, one on the dot) and neither shows user-set content.
Most likely the app is still running an **old Android build**. Do a **clean build and reinstall**, and ensure the plugin dependency in the app really points at the fixed code (gitea master or local path). Then re-test and check logcat for the lines above. If the bugs persist after that, the next step is to capture a full logcat from “edit reminder (same time)” through the next fire and from “first fire” through “next day” to see which path runs.