update login with device info

This commit is contained in:
Efril 2026-05-10 22:51:04 +07:00
parent 842b7a5041
commit a0610459bf
11 changed files with 181 additions and 9 deletions

View File

@ -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<LoginFormEvent, LoginFormState> {
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<LoginFormEvent>(_onLoginFormEvent);
}
@ -43,9 +52,14 @@ class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
);
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(

View File

@ -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<PackageInfo> get packageInfo => PackageInfo.fromPlatform();
}

View File

@ -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<Map<String, String>> 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<Map<String, String>> _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';
}
}

View File

@ -4,6 +4,8 @@ abstract class IAuthRepository {
Future<Either<AuthFailure, Login>> login({
required String email,
required String password,
required Map<String, String> devicePayload,
required String fcmToken,
});
Future<Either<AuthFailure, Unit>> logout();
Future<bool> hasToken();

View File

@ -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

View File

@ -21,11 +21,18 @@ class AuthRemoteDataProvider {
Future<DC<AuthFailure, LoginDto>> login({
required String email,
required String password,
required Map<String, String> 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) {

View File

@ -19,11 +19,15 @@ class AuthRepository implements IAuthRepository {
Future<Either<AuthFailure, Login>> login({
required String email,
required String password,
required Map<String, String> devicePayload,
required String fcmToken,
}) async {
try {
final result = await _dataProvider.login(
email: email,
password: password,
devicePayload: devicePayload,
fcmToken: fcmToken,
);
if (result.hasError) {

View File

@ -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 {}

View File

@ -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"))

View File

@ -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:

View File

@ -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