feat(notifications): add self-hosted offline reminder engine - #5
Open
mtherien wants to merge 5 commits into
Open
feat(notifications): add self-hosted offline reminder engine#5mtherien wants to merge 5 commits into
mtherien wants to merge 5 commits into
Conversation
Adds "download once, alert anytime" local notifications so self-hosted deployments (which have no push transport and may be intranet-only) still alert the device. Cloud APNs/FCM push is unchanged. Server (Web.Shared + Core + Infrastructure): - IUpcomingReminderService / UpcomingReminderService: projects domain state forward into future-dated reminders (calendar reminders at StartTime-ReminderMinutesBefore; per-item expiry; low-stock and task digests at a local morning hour; date-anchored chore/vehicle-maintenance due dates). Reuses the existing evaluators' query predicates and respects each user's per-type PushEnabled preference. - GET /api/v1/notifications/upcoming?days=N (per-authenticated-user). - Unit tests for FireAtUtc math, preference gating, and ordering. Mobile (MAUI): - INotificationScheduler with iOS (UNCalendarNotificationTrigger) and Android (AlarmManager.SetExactAndAllowWhileIdle + boot re-arm) impls. - NotificationSyncOrchestrator mirrors CalendarSyncOrchestrator: gated on ApiSettings.IsSelfHostedServer(), diffs the server feed against a SQLite mirror (schedule new/changed, cancel stale, prune past), caps at 60. - Background drivers (iOS BGTaskScheduler, Android WorkManager) plus a foreground/resume trigger; settings toggle shown only in self-hosted mode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Chb8Xj6dRF1fiPZc6nqqE
…e cache Make the on-device local scheduler the single path that DISPLAYS scheduled reminders (calendar/expiry/low-stock/task) in both cloud and self-hosted, and demote cloud push for those types to a silent data-sync channel. Removes double-delivery by construction and gives cloud users real-time freshness. - MessageType.IsLocallySchedulable() classifies the four scheduled types. - IReminderSyncPushService (+ NullReminderSyncPushService no-op default) mirrors the contact-sync silent-push pattern; CalendarController fires it on event create/update/delete (cloud sends a silent reminderSync push; self-hosted no-op). - Client handles action=reminderSync (iOS AppDelegate, Android FCM service) by re-running NotificationSyncOrchestrator.SyncAsync(); the orchestrator now runs in both modes (dropped the self-hosted gate) and defaults on in cloud; the Settings toggle shows in both modes. - Unit test for the classifier. No schema change. Cloud-side visible-push suppression + the cloud silent-push sender live in the private repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Chb8Xj6dRF1fiPZc6nqqE
…ent) Adds IAppBadgeService (iOS sets the springboard badge via SetBadgeCount / ApplicationIconBadgeNumber; Android no-op — launcher-managed) and an AppBadgeHelper that sets the badge = server unread-notification count. Wired to run on the existing AppShell unread poll, on app resume, and after the user reads/dismisses notifications (the reset). The server also stamps the same count onto outgoing pushes (private repo) so the badge updates while the app is closed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Chb8Xj6dRF1fiPZc6nqqE
…r-item
UpcomingReminderService now emits one aggregate Expiry reminder ("N item(s)
expiring soon", with expired/expiring-soon breakdown) fired at the next local
morning, instead of one local notification per stock entry. Mirrors the server
ExpiryEvaluator's digest and the existing LowStock/TaskSummary digests, so the
device shows a single expiry banner per day rather than one per item. Updated
unit tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Chb8Xj6dRF1fiPZc6nqqE
When the user dismisses a Famick OS notification, mark the corresponding in-app notification read so the unread count and app-icon badge stay in sync. Server/shared: CreateNotificationAsync returns the new notification id; InAppMessageTransport stamps it onto RenderedMessage.NotificationId (runs before the push transport) so the push can carry it. Client: - Android: visible pushes are now data-only, so FamickFirebaseMessagingService always posts the notification itself with a delete intent -> NotificationDismissReceiver marks it read (reliable in fore/background). - iOS: registers a FAMICK_DISMISSIBLE category with a custom dismiss action; ForegroundNotificationDelegate handles the dismiss action and marks read (best-effort — iOS only reports explicit dismissals). - Shared NotificationActionHelper marks read + refreshes the badge. Cloud-side push payload changes (aps.category + notificationId; FCM data-only) land in the private repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Chb8Xj6dRF1fiPZc6nqqE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.