diff --git a/lib/application/auth/login_form/login_form_bloc.dart b/lib/application/auth/login_form/login_form_bloc.dart index e5bad21..98f19f4 100644 --- a/lib/application/auth/login_form/login_form_bloc.dart +++ b/lib/application/auth/login_form/login_form_bloc.dart @@ -5,6 +5,8 @@ import 'package:dartz/dartz.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:injectable/injectable.dart'; +import '../../../common/service/device_info_service.dart'; +import '../../../common/service/fcm_service.dart'; import '../../../domain/auth/auth.dart'; part 'login_form_event.dart'; @@ -14,7 +16,14 @@ part 'login_form_bloc.freezed.dart'; @injectable class LoginFormBloc extends Bloc { final IAuthRepository _authRepository; - LoginFormBloc(this._authRepository) : super(LoginFormState.initial()) { + final FcmService _fcmService; + final DeviceInfoService _deviceInfoService; + + LoginFormBloc( + this._authRepository, + this._fcmService, + this._deviceInfoService, + ) : super(LoginFormState.initial()) { on(_onLoginFormEvent); } @@ -43,9 +52,14 @@ class LoginFormBloc extends Bloc { ); if (emailValid && passwordValid) { + final fcmToken = await _fcmService.getToken() ?? ''; + final devicePayload = await _deviceInfoService.getDevicePayload(); + failureOrLogin = await _authRepository.login( email: state.email, password: state.password, + fcmToken: fcmToken, + devicePayload: devicePayload, ); emit( diff --git a/lib/common/di/di_firebase.dart b/lib/common/di/di_firebase.dart index fc9f3a3..103d1fd 100644 --- a/lib/common/di/di_firebase.dart +++ b/lib/common/di/di_firebase.dart @@ -1,6 +1,8 @@ +import 'package:device_info_plus/device_info_plus.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:injectable/injectable.dart'; +import 'package:package_info_plus/package_info_plus.dart'; @module abstract class FirebaseDi { @@ -10,4 +12,10 @@ abstract class FirebaseDi { @lazySingleton FlutterLocalNotificationsPlugin get localNotifications => FlutterLocalNotificationsPlugin(); + + @lazySingleton + DeviceInfoPlugin get deviceInfo => DeviceInfoPlugin(); + + @preResolve + Future get packageInfo => PackageInfo.fromPlatform(); } diff --git a/lib/common/service/device_info_service.dart b/lib/common/service/device_info_service.dart new file mode 100644 index 0000000..54493d6 --- /dev/null +++ b/lib/common/service/device_info_service.dart @@ -0,0 +1,64 @@ +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:injectable/injectable.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +@lazySingleton +class DeviceInfoService { + DeviceInfoService(this._deviceInfo, this._packageInfo); + + final DeviceInfoPlugin _deviceInfo; + final PackageInfo _packageInfo; + + String get appVersion => '${_packageInfo.version}+${_packageInfo.buildNumber}'; + + Future> getDevicePayload() async { + final info = await _getDeviceInfo(); + return { + 'device_id': info['device_id'] ?? '', + 'device_name': info['device_name'] ?? '', + 'device_type': info['device_type'] ?? 'mobile', + 'platform': _getPlatform(), + 'app_version': appVersion, + 'os_version': info['os_version'] ?? '', + }; + } + + String _getPlatform() { + if (Platform.isAndroid) return 'android'; + if (Platform.isIOS) return 'ios'; + return 'web'; + } + + Future> _getDeviceInfo() async { + if (Platform.isAndroid) { + final android = await _deviceInfo.androidInfo; + return { + 'device_id': android.id, + 'device_name': '${android.manufacturer} ${android.model}', + 'device_type': _resolveDeviceType(android.model), + 'os_version': 'Android ${android.version.release} (SDK ${android.version.sdkInt})', + }; + } else if (Platform.isIOS) { + final ios = await _deviceInfo.iosInfo; + return { + 'device_id': ios.identifierForVendor ?? '', + 'device_name': ios.name, + 'device_type': _resolveDeviceType(ios.model), + 'os_version': '${ios.systemName} ${ios.systemVersion}', + }; + } + return {'device_type': 'desktop'}; + } + + /// Deteksi device_type berdasarkan nama model + /// Nilai valid: mobile | tablet | desktop + String _resolveDeviceType(String model) { + final lower = model.toLowerCase(); + if (lower.contains('tablet') || lower.contains('tab') || lower.contains('ipad')) { + return 'tablet'; + } + return 'mobile'; + } +} diff --git a/lib/domain/auth/repositories/i_auth_repository.dart b/lib/domain/auth/repositories/i_auth_repository.dart index 569853f..cefc386 100644 --- a/lib/domain/auth/repositories/i_auth_repository.dart +++ b/lib/domain/auth/repositories/i_auth_repository.dart @@ -4,6 +4,8 @@ abstract class IAuthRepository { Future> login({ required String email, required String password, + required Map devicePayload, + required String fcmToken, }); Future> logout(); Future hasToken(); diff --git a/lib/env.dart b/lib/env.dart index 4e188b8..dc5b10b 100644 --- a/lib/env.dart +++ b/lib/env.dart @@ -10,7 +10,7 @@ abstract class Env { @dev class DevEnv implements Env { @override - String get baseUrl => 'https://api-pos.apskel.id'; + String get baseUrl => 'http://192.168.1.13:4000'; @override String get dbName => "apskel_pos_dev.db"; // example value diff --git a/lib/infrastructure/auth/datasources/remote_data_provider.dart b/lib/infrastructure/auth/datasources/remote_data_provider.dart index fe4f5a2..b5111b7 100644 --- a/lib/infrastructure/auth/datasources/remote_data_provider.dart +++ b/lib/infrastructure/auth/datasources/remote_data_provider.dart @@ -21,11 +21,18 @@ class AuthRemoteDataProvider { Future> login({ required String email, required String password, + required Map devicePayload, + required String fcmToken, }) async { try { final response = await _apiClient.post( ApiPath.login, - data: {'email': email, 'password': password}, + data: { + 'email': email, + 'password': password, + 'fcm_token': fcmToken, + ...devicePayload, + }, ); if (response.data['code'] == 401) { diff --git a/lib/infrastructure/auth/repositories/auth_repository.dart b/lib/infrastructure/auth/repositories/auth_repository.dart index 823145c..835a3f8 100644 --- a/lib/infrastructure/auth/repositories/auth_repository.dart +++ b/lib/infrastructure/auth/repositories/auth_repository.dart @@ -19,11 +19,15 @@ class AuthRepository implements IAuthRepository { Future> login({ required String email, required String password, + required Map devicePayload, + required String fcmToken, }) async { try { final result = await _dataProvider.login( email: email, password: password, + devicePayload: devicePayload, + fcmToken: fcmToken, ); if (result.hasError) { diff --git a/lib/injection.config.dart b/lib/injection.config.dart index ac50a33..44d3d43 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -85,6 +85,8 @@ import 'package:apskel_pos_flutter_v2/common/di/di_shared_preferences.dart' as _i135; import 'package:apskel_pos_flutter_v2/common/network/network_client.dart' as _i171; +import 'package:apskel_pos_flutter_v2/common/service/device_info_service.dart' + as _i288; import 'package:apskel_pos_flutter_v2/common/service/fcm_service.dart' as _i312; import 'package:apskel_pos_flutter_v2/domain/analytic/analytic.dart' as _i346; import 'package:apskel_pos_flutter_v2/domain/auth/auth.dart' as _i776; @@ -149,12 +151,14 @@ import 'package:apskel_pos_flutter_v2/infrastructure/table/repositories/table_re import 'package:apskel_pos_flutter_v2/presentation/router/app_router.dart' as _i800; import 'package:connectivity_plus/connectivity_plus.dart' as _i895; +import 'package:device_info_plus/device_info_plus.dart' as _i833; import 'package:dio/dio.dart' as _i361; import 'package:firebase_messaging/firebase_messaging.dart' as _i892; import 'package:flutter_local_notifications/flutter_local_notifications.dart' as _i163; import 'package:get_it/get_it.dart' as _i174; import 'package:injectable/injectable.dart' as _i526; +import 'package:package_info_plus/package_info_plus.dart' as _i655; import 'package:shared_preferences/shared_preferences.dart' as _i460; const String _dev = 'dev'; @@ -167,16 +171,20 @@ extension GetItInjectableX on _i174.GetIt { _i526.EnvironmentFilter? environmentFilter, }) async { final gh = _i526.GetItHelper(this, environment, environmentFilter); + final firebaseDi = _$FirebaseDi(); final sharedPreferencesDi = _$SharedPreferencesDi(); final databaseDi = _$DatabaseDi(); final autoRouteDi = _$AutoRouteDi(); final connectivityDi = _$ConnectivityDi(); final dioDi = _$DioDi(); - final firebaseDi = _$FirebaseDi(); gh.factory<_i13.CheckoutFormBloc>(() => _i13.CheckoutFormBloc()); gh.factory<_i96.PrinterBloc>(() => _i96.PrinterBloc()); gh.factory<_i257.ReportBloc>(() => _i257.ReportBloc()); gh.factory<_i334.SplitBillFormBloc>(() => _i334.SplitBillFormBloc()); + await gh.factoryAsync<_i655.PackageInfo>( + () => firebaseDi.packageInfo, + preResolve: true, + ); await gh.factoryAsync<_i460.SharedPreferences>( () => sharedPreferencesDi.prefs, preResolve: true, @@ -189,6 +197,13 @@ extension GetItInjectableX on _i174.GetIt { gh.lazySingleton<_i163.FlutterLocalNotificationsPlugin>( () => firebaseDi.localNotifications, ); + gh.lazySingleton<_i833.DeviceInfoPlugin>(() => firebaseDi.deviceInfo); + gh.lazySingleton<_i288.DeviceInfoService>( + () => _i288.DeviceInfoService( + gh<_i833.DeviceInfoPlugin>(), + gh<_i655.PackageInfo>(), + ), + ); gh.lazySingleton<_i171.NetworkClient>( () => _i171.NetworkClient(gh<_i895.Connectivity>()), ); @@ -276,6 +291,13 @@ extension GetItInjectableX on _i174.GetIt { gh<_i708.CategoryLocalDataProvider>(), ), ); + gh.factory<_i46.LoginFormBloc>( + () => _i46.LoginFormBloc( + gh<_i776.IAuthRepository>(), + gh<_i312.FcmService>(), + gh<_i288.DeviceInfoService>(), + ), + ); gh.factory<_i983.ITableRepository>( () => _i824.TableRepository( gh<_i95.TableRemoteDataProvider>(), @@ -308,9 +330,6 @@ extension GetItInjectableX on _i174.GetIt { gh<_i833.PaymentMethodRemoteDataProvider>(), ), ); - gh.factory<_i46.LoginFormBloc>( - () => _i46.LoginFormBloc(gh<_i776.IAuthRepository>()), - ); gh.factory<_i641.LogoutBloc>( () => _i641.LogoutBloc(gh<_i776.IAuthRepository>()), ); @@ -404,6 +423,8 @@ extension GetItInjectableX on _i174.GetIt { } } +class _$FirebaseDi extends _i857.FirebaseDi {} + class _$SharedPreferencesDi extends _i135.SharedPreferencesDi {} class _$DatabaseDi extends _i209.DatabaseDi {} @@ -413,5 +434,3 @@ class _$AutoRouteDi extends _i729.AutoRouteDi {} class _$ConnectivityDi extends _i807.ConnectivityDi {} class _$DioDi extends _i86.DioDi {} - -class _$FirebaseDi extends _i857.FirebaseDi {} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 0584489..1bbfd1a 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,20 +6,24 @@ import FlutterMacOS import Foundation import connectivity_plus +import device_info_plus import firebase_core import firebase_crashlytics import firebase_messaging import flutter_local_notifications +import package_info_plus import path_provider_foundation import shared_preferences_foundation import sqflite_darwin func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 1cc3eea..78f9cb7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -337,6 +337,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a" + url: "https://pub.dev" + source: hosted + version: "11.5.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" dio: dependency: "direct main" description: @@ -880,6 +896,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968" + url: "https://pub.dev" + source: hosted + version: "8.3.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" + url: "https://pub.dev" + source: hosted + version: "3.2.1" path: dependency: "direct main" description: @@ -1421,6 +1453,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" + url: "https://pub.dev" + source: hosted + version: "2.1.0" win_ble: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4dc4db1..58e97fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,8 @@ dependencies: firebase_crashlytics: ^5.0.3 firebase_messaging: ^16.0.3 flutter_local_notifications: ^18.0.1 + device_info_plus: ^11.4.0 + package_info_plus: ^8.3.0 another_flushbar: ^1.12.32 flutter_spinkit: ^5.2.2 bloc: ^9.1.0