From d352aec5bea01fa383d67d6a367a94c8670d2275 Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Wed, 8 Apr 2026 15:40:37 +0200 Subject: [PATCH] feat(notifications): register FCM token with backend post-login Adds NotificationsRemoteDatasource in sf_shared that POSTs the current Firebase Messaging token to /notifications/push, wired into the legacy login flow right after getUserInfo so the device can receive targeted push notifications. Fire-and-forget with catchError so a failure never blocks login. --- .../presentation/state/login_view_model.dart | 8 +++++ .../activity/.flutter-plugins-dependencies | 2 +- packages/sf_shared/lib/sf_shared.dart | 3 ++ .../notifications_remote_datasource.dart | 4 +++ .../notifications_remote_datasource_impl.dart | 34 +++++++++++++++++++ ...ifications_remote_datasource_provider.dart | 11 ++++++ packages/sf_shared/pubspec.yaml | 1 + 7 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource.dart create mode 100644 packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource_impl.dart create mode 100644 packages/sf_shared/lib/src/providers/notifications_remote_datasource_provider.dart diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart index 84cb66b0..963de083 100644 --- a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart @@ -19,6 +19,7 @@ class LegacyLoginViewModel extends Notifier with LoginFormValidation { late final LegacyLoginRepository _repository; late final GetUserInfoUseCase _getUserInfoUseCase; + late final NotificationsRemoteDatasource _notifications; late final SfTrackingRepository _tracking; late final TextEditingController emailController; @@ -32,6 +33,7 @@ class LegacyLoginViewModel extends Notifier LegacyLoginViewState build() { _repository = ref.read(legacyLoginRepositoryProvider); _getUserInfoUseCase = ref.read(getUserInfoUseCaseProvider); + _notifications = ref.read(notificationsRemoteDatasourceProvider); _tracking = ref.read(sfTrackingProvider); emailController = TextEditingController(); @@ -224,6 +226,12 @@ class LegacyLoginViewModel extends Notifier if (!ref.mounted) return; + unawaited( + _notifications.registerCurrentPushToken().catchError((Object e) { + debugPrint('[FCM] failed to register push token post-login: $e'); + }), + ); + final hasDevices = await _repository.hasDevices(); if (!ref.mounted) return; diff --git a/modules/payment/modules/activity/.flutter-plugins-dependencies b/modules/payment/modules/activity/.flutter-plugins-dependencies index d79cef76..3b81d042 100644 --- a/modules/payment/modules/activity/.flutter-plugins-dependencies +++ b/modules/payment/modules/activity/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/save-family-app/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/save-family-app/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.23/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"shared_preferences_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_treezor_entrust_sdk_bridge","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2026-04-06 22:32:32.905321","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"firebase_core","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_core-4.6.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_messaging-16.1.3/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/save-family-app/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"firebase_core","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_core-4.6.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_messaging-16.1.3/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/save-family-app/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.23/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"firebase_core","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_core-4.6.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_messaging-16.1.3/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"name":"firebase_core","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_core-4.6.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"firebase_core_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_core_web-3.5.1/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.1.4/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_treezor_entrust_sdk_bridge","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2026-04-08 10:58:48.039048","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/packages/sf_shared/lib/sf_shared.dart b/packages/sf_shared/lib/sf_shared.dart index 2c70e3d8..2b48b72c 100644 --- a/packages/sf_shared/lib/sf_shared.dart +++ b/packages/sf_shared/lib/sf_shared.dart @@ -29,6 +29,9 @@ export 'src/providers/user_info_provider.dart'; export 'src/domain/repositories/user_repository.dart'; export 'src/data/repositories/user_repository_impl.dart'; export 'src/data/datasource/user_remote_datasource_impl.dart'; +export 'src/data/datasource/notifications_remote_datasource.dart'; +export 'src/data/datasource/notifications_remote_datasource_impl.dart'; +export 'src/providers/notifications_remote_datasource_provider.dart'; export 'src/domain/entities/beneficiary_validation_entity.dart'; export 'src/domain/entities/transaction_beneficiary_entity.dart'; export 'src/data/models/payout_beneficiary_model.dart'; diff --git a/packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource.dart b/packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource.dart new file mode 100644 index 00000000..f46294ab --- /dev/null +++ b/packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource.dart @@ -0,0 +1,4 @@ +abstract class NotificationsRemoteDatasource { + Future registerCurrentPushToken(); + Future registerPushToken(String token); +} diff --git a/packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource_impl.dart b/packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource_impl.dart new file mode 100644 index 00000000..2a52ab81 --- /dev/null +++ b/packages/sf_shared/lib/src/data/datasource/notifications_remote_datasource_impl.dart @@ -0,0 +1,34 @@ +import 'package:dio/dio.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:sf_infrastructure/sf_infrastructure.dart'; + +import 'notifications_remote_datasource.dart'; + +class NotificationsRemoteDatasourceImpl implements NotificationsRemoteDatasource { + NotificationsRemoteDatasourceImpl(this._repository); + + final SaveFamilyRepository _repository; + + @override + Future registerCurrentPushToken() async { + final token = await FirebaseMessaging.instance.getToken(); + if (token == null || token.isEmpty) return; + await registerPushToken(token); + } + + @override + Future registerPushToken(String token) async { + try { + await _repository.post>( + '/notifications/push', + body: {'token': token}, + ); + } on DioException catch (error) { + final apiMsg = error.response?.data; + final msg = apiMsg is String + ? apiMsg + : (error.message ?? 'Error registering push token'); + throw Exception(msg); + } + } +} diff --git a/packages/sf_shared/lib/src/providers/notifications_remote_datasource_provider.dart b/packages/sf_shared/lib/src/providers/notifications_remote_datasource_provider.dart new file mode 100644 index 00000000..117d1598 --- /dev/null +++ b/packages/sf_shared/lib/src/providers/notifications_remote_datasource_provider.dart @@ -0,0 +1,11 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:get_it/get_it.dart'; +import 'package:sf_infrastructure/sf_infrastructure.dart'; +import 'package:sf_shared/src/data/datasource/notifications_remote_datasource.dart'; +import 'package:sf_shared/src/data/datasource/notifications_remote_datasource_impl.dart'; + +final notificationsRemoteDatasourceProvider = + Provider((ref) { + final saveFamilyRepository = GetIt.I(); + return NotificationsRemoteDatasourceImpl(saveFamilyRepository); +}); diff --git a/packages/sf_shared/pubspec.yaml b/packages/sf_shared/pubspec.yaml index cf1ec93a..641988bf 100644 --- a/packages/sf_shared/pubspec.yaml +++ b/packages/sf_shared/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: flutter_svg: ^2.2.2 flutter_riverpod: ^3.0.3 dio: ^5.9.2 + firebase_messaging: ^16.1.3 freezed_annotation: ^3.1.0 freezed: ^3.2.3 json_annotation: ^4.9.0