diff --git a/lib/core/services/onesignal_service.dart b/lib/core/services/onesignal_service.dart new file mode 100644 index 0000000..8016dfe --- /dev/null +++ b/lib/core/services/onesignal_service.dart @@ -0,0 +1,92 @@ +import 'package:flutter/foundation.dart'; +import 'package:onesignal_flutter/onesignal_flutter.dart'; + +/// OneSignal service for managing push notifications and external user ID. +/// +/// This service handles: +/// - Setting external user ID after login (using phone number) +/// - Restoring external user ID on app startup +/// - Clearing external user ID on logout +/// +/// Usage: +/// ```dart +/// // After successful login +/// await OneSignalService.login(phoneNumber); +/// +/// // On logout +/// await OneSignalService.logout(); +/// ``` +class OneSignalService { + OneSignalService._(); + + /// Login user to OneSignal by setting external user ID. + /// + /// This associates the device with the user's phone number, + /// allowing targeted push notifications to specific users. + /// + /// [phoneNumber] - The user's phone number (used as external ID) + static Future login(String phoneNumber) async { + try { + // Set external user ID for targeting + await OneSignal.login(phoneNumber); + debugPrint('🔔 OneSignal: login - external_id set to $phoneNumber'); + } catch (e) { + debugPrint('🔔 OneSignal error: Failed to set external user ID - $e'); + } + } + + /// Logout user from OneSignal by removing external user ID. + /// + /// This disassociates the device from the user, + /// so notifications won't be sent to this specific user anymore. + static Future logout() async { + try { + await OneSignal.logout(); + debugPrint('🔔 OneSignal: logout - external_id cleared'); + } catch (e) { + debugPrint('🔔 OneSignal error: Failed to clear external user ID - $e'); + } + } + + /// Add a tag to the user for segmentation. + /// + /// Tags can be used to segment users for targeted notifications. + /// Example: tier = "diamond", role = "contractor" + static Future setTag(String key, String value) async { + try { + await OneSignal.User.addTagWithKey(key, value); + debugPrint('🔔 OneSignal: tag set - $key: $value'); + } catch (e) { + debugPrint('🔔 OneSignal error: Failed to set tag - $e'); + } + } + + /// Add multiple tags at once. + static Future setTags(Map tags) async { + try { + await OneSignal.User.addTags(tags); + debugPrint('🔔 OneSignal: tags set - $tags'); + } catch (e) { + debugPrint('🔔 OneSignal error: Failed to set tags - $e'); + } + } + + /// Remove a tag from the user. + static Future removeTag(String key) async { + try { + await OneSignal.User.removeTag(key); + debugPrint('🔔 OneSignal: tag removed - $key'); + } catch (e) { + debugPrint('🔔 OneSignal error: Failed to remove tag - $e'); + } + } + + /// Get the OneSignal subscription ID (player ID). + /// + /// This is the device-specific ID used by OneSignal. + static String? get subscriptionId => OneSignal.User.pushSubscription.id; + + /// Check if push notifications are enabled. + static bool get isPushEnabled => + OneSignal.User.pushSubscription.optedIn ?? false; +} diff --git a/lib/features/auth/presentation/providers/auth_provider.dart b/lib/features/auth/presentation/providers/auth_provider.dart index 3358705..e517d05 100644 --- a/lib/features/auth/presentation/providers/auth_provider.dart +++ b/lib/features/auth/presentation/providers/auth_provider.dart @@ -11,6 +11,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:worker/core/constants/api_constants.dart'; import 'package:worker/core/network/dio_client.dart'; import 'package:worker/core/services/frappe_auth_service.dart'; +import 'package:worker/core/services/onesignal_service.dart'; import 'package:worker/features/auth/data/datasources/auth_local_datasource.dart'; import 'package:worker/features/auth/data/datasources/auth_remote_datasource.dart'; import 'package:worker/features/auth/domain/entities/user.dart'; @@ -100,7 +101,11 @@ class Auth extends _$Auth { final fullName = await secureStorage.read(key: 'frappe_full_name'); if (sid != null && userId != null && userId != ApiConstants.frappePublicUserId) { - // User is logged in and wants to be remembered, create User entity + // User is logged in and wants to be remembered + // Restore OneSignal external user ID for targeted notifications + await OneSignalService.login(userId); + + // Create User entity final now = DateTime.now(); return User( userId: userId, @@ -188,6 +193,9 @@ class Auth extends _$Auth { // Log login event await AnalyticsService.logLogin(method: 'phone'); + // Set OneSignal external user ID for targeted notifications + await OneSignalService.login(phoneNumber); + // Create and return User entity final now = DateTime.now(); return User( @@ -227,6 +235,9 @@ class Auth extends _$Auth { // Clear user ID from analytics await AnalyticsService.setUserId(null); + // Clear OneSignal external user ID + await OneSignalService.logout(); + // Clear saved session await _localDataSource.clearSession(); await frappeService.clearSession(); diff --git a/lib/features/auth/presentation/providers/auth_provider.g.dart b/lib/features/auth/presentation/providers/auth_provider.g.dart index 4acab06..7c1d4ca 100644 --- a/lib/features/auth/presentation/providers/auth_provider.g.dart +++ b/lib/features/auth/presentation/providers/auth_provider.g.dart @@ -272,7 +272,7 @@ final class AuthProvider extends $AsyncNotifierProvider { Auth create() => Auth(); } -String _$authHash() => r'f1d856a9a8fc461da111699e3c7ca2af1f2ce7ca'; +String _$authHash() => r'2aaad43ba390e824b5aa8d95bc14e514c421c8ef'; /// Authentication Provider /// diff --git a/pubspec.yaml b/pubspec.yaml index ad3c443..c10a1f4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1+26 +version: 1.0.1+29 environment: sdk: ^3.10.0