feat(messages): unread badges + OS notification for private messages
Private messages (NIP-17) arrived in the foreground with no signal: no unread count, no badge, no OS notification. Hook both into the single InboxService.ingest choke point. - UnreadService: per-peer "last read" in the keystore (timestamps+pubkey only, no message text), live count via a changes stream, active-peer suppression so the open chat never badges or notifies. - UnreadBadge: reusable, RTL-aware (Flutter Badge) — on the home hamburger, the drawer "Chat" item, and per-conversation in the messages list. - Mark-read on chat open (ChatScreen). - NotificationService over flutter_local_notifications: generic "New message from <name>" (no message text, for privacy), payload = pubkey, tap opens /chat/:pubkey. No-op on web/Windows; Android POST_NOTIFICATIONS. - i18n notifications.newMessageFrom (en/es/pt/ast). Background/push stays out of scope (foreground-only by design). Tests: UnreadService, NotificationService (mock plugin), InboxService hooks (new peer notifies/counts; duplicate, own, and open-chat do not), and a UnreadBadge widget test. dart analyze clean; commons_core green.
This commit is contained in:
parent
63f48db5c2
commit
5fe0f4540e
27 changed files with 1238 additions and 114 deletions
|
|
@ -13,6 +13,7 @@ import 'services/coarse_location.dart';
|
|||
import 'services/inbox_service.dart';
|
||||
import 'services/locale_store.dart';
|
||||
import 'services/message_store.dart';
|
||||
import 'services/notification_service.dart';
|
||||
import 'services/offer_outbox.dart';
|
||||
import 'services/onboarding_store.dart';
|
||||
import 'services/profile_cache.dart';
|
||||
|
|
@ -51,6 +52,12 @@ class _BootstrapState extends State<Bootstrap> {
|
|||
final onboarding = getIt<OnboardingStore>();
|
||||
final inbox =
|
||||
getIt.isRegistered<InboxService>() ? getIt<InboxService>() : null;
|
||||
final notifications = getIt.isRegistered<NotificationService>()
|
||||
? getIt<NotificationService>()
|
||||
: null;
|
||||
// Ask for notification permission and set up the OS channel (no-op on
|
||||
// unsupported platforms). Done before the inbox starts listening.
|
||||
if (notifications != null) await notifications.initialize();
|
||||
// Listen for incoming private messages app-wide (foreground) so they arrive
|
||||
// and fill the inbox even before their chat is opened.
|
||||
if (inbox != null) unawaited(inbox.start());
|
||||
|
|
@ -68,6 +75,7 @@ class _BootstrapState extends State<Bootstrap> {
|
|||
profileStore: getIt<ProfileStore>(),
|
||||
profileCache: getIt<ProfileCache>(),
|
||||
inbox: inbox,
|
||||
notifications: notifications,
|
||||
showIntro: !await onboarding.introSeen(),
|
||||
autoBackup: getIt.isRegistered<AutoBackupService>()
|
||||
? getIt<AutoBackupService>()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue