add device info at login
This commit is contained in:
parent
9b6e9c591d
commit
a4a12c6763
@ -4,6 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../../common/utils/device_info_service.dart';
|
||||||
|
import '../../../common/utils/fcm_service.dart';
|
||||||
import '../../../domain/auth/auth.dart';
|
import '../../../domain/auth/auth.dart';
|
||||||
|
|
||||||
part 'login_form_event.dart';
|
part 'login_form_event.dart';
|
||||||
@ -13,7 +15,11 @@ part 'login_form_bloc.freezed.dart';
|
|||||||
@injectable
|
@injectable
|
||||||
class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
|
class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
|
||||||
final IAuthRepository _repository;
|
final IAuthRepository _repository;
|
||||||
LoginFormBloc(this._repository) : super(LoginFormState.initial()) {
|
final DeviceInfoService _deviceInfoService;
|
||||||
|
final FcmService _fcmService;
|
||||||
|
|
||||||
|
LoginFormBloc(this._repository, this._deviceInfoService, this._fcmService)
|
||||||
|
: super(LoginFormState.initial()) {
|
||||||
on<LoginFormEvent>(_onLoginFormEvent);
|
on<LoginFormEvent>(_onLoginFormEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +42,25 @@ class LoginFormBloc extends Bloc<LoginFormEvent, LoginFormState> {
|
|||||||
final passwordValid = state.password.isNotEmpty;
|
final passwordValid = state.password.isNotEmpty;
|
||||||
|
|
||||||
if (emailValid && passwordValid) {
|
if (emailValid && passwordValid) {
|
||||||
|
// Ambil device info dan FCM token secara paralel
|
||||||
|
final results = await Future.wait([
|
||||||
|
_deviceInfoService.getDeviceInfo(),
|
||||||
|
_fcmService.getToken(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final deviceInfo = results[0] as DeviceInfo;
|
||||||
|
final fcmToken = results[1] as String?;
|
||||||
|
|
||||||
failureOrAuth = await _repository.login(
|
failureOrAuth = await _repository.login(
|
||||||
email: state.email,
|
email: state.email,
|
||||||
password: state.password,
|
password: state.password,
|
||||||
|
deviceId: deviceInfo.deviceId,
|
||||||
|
deviceName: deviceInfo.deviceName,
|
||||||
|
deviceType: deviceInfo.deviceType,
|
||||||
|
platform: deviceInfo.platform,
|
||||||
|
osVersion: deviceInfo.osVersion,
|
||||||
|
appVersion: deviceInfo.appVersion,
|
||||||
|
fcmToken: fcmToken,
|
||||||
);
|
);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
|
|||||||
82
lib/common/utils/device_info_service.dart
Normal file
82
lib/common/utils/device_info_service.dart
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
class DeviceInfo {
|
||||||
|
final String deviceId;
|
||||||
|
final String deviceName;
|
||||||
|
final String deviceType;
|
||||||
|
final String platform;
|
||||||
|
final String osVersion;
|
||||||
|
final String appVersion;
|
||||||
|
|
||||||
|
const DeviceInfo({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.deviceName,
|
||||||
|
required this.deviceType,
|
||||||
|
required this.platform,
|
||||||
|
required this.osVersion,
|
||||||
|
required this.appVersion,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'device_id': deviceId,
|
||||||
|
'device_name': deviceName,
|
||||||
|
'device_type': deviceType,
|
||||||
|
'platform': platform,
|
||||||
|
'os_version': osVersion,
|
||||||
|
'app_version': appVersion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
class DeviceInfoService {
|
||||||
|
final DeviceInfoPlugin _deviceInfo = DeviceInfoPlugin();
|
||||||
|
|
||||||
|
Future<DeviceInfo> getDeviceInfo() async {
|
||||||
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
final appVersion = packageInfo.version;
|
||||||
|
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
final info = await _deviceInfo.androidInfo;
|
||||||
|
return DeviceInfo(
|
||||||
|
deviceId: info.id,
|
||||||
|
deviceName: '${info.manufacturer} ${info.model}',
|
||||||
|
deviceType: _resolveDeviceType(info.model),
|
||||||
|
platform: 'android',
|
||||||
|
osVersion: 'Android ${info.version.release}',
|
||||||
|
appVersion: appVersion,
|
||||||
|
);
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
final info = await _deviceInfo.iosInfo;
|
||||||
|
return DeviceInfo(
|
||||||
|
deviceId: info.identifierForVendor ?? '',
|
||||||
|
deviceName: info.name,
|
||||||
|
deviceType: _resolveDeviceType(info.model),
|
||||||
|
platform: 'ios',
|
||||||
|
osVersion: '${info.systemName} ${info.systemVersion}',
|
||||||
|
appVersion: appVersion,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback (web/desktop — tidak dipakai tapi aman)
|
||||||
|
return DeviceInfo(
|
||||||
|
deviceId: 'unknown',
|
||||||
|
deviceName: 'unknown',
|
||||||
|
deviceType: 'desktop',
|
||||||
|
platform: 'web',
|
||||||
|
osVersion: 'unknown',
|
||||||
|
appVersion: appVersion,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tentukan device_type berdasarkan model name.
|
||||||
|
/// Nilai valid: 'mobile' | 'tablet' | 'desktop'
|
||||||
|
String _resolveDeviceType(String model) {
|
||||||
|
final lower = model.toLowerCase();
|
||||||
|
if (lower.contains('ipad') || lower.contains('tablet')) return 'tablet';
|
||||||
|
return 'mobile';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,13 @@ abstract class IAuthRepository {
|
|||||||
Future<Either<AuthFailure, Auth>> login({
|
Future<Either<AuthFailure, Auth>> login({
|
||||||
required String email,
|
required String email,
|
||||||
required String password,
|
required String password,
|
||||||
|
required String deviceId,
|
||||||
|
required String deviceName,
|
||||||
|
required String deviceType,
|
||||||
|
required String platform,
|
||||||
|
required String osVersion,
|
||||||
|
required String appVersion,
|
||||||
|
String? fcmToken,
|
||||||
});
|
});
|
||||||
Future<bool> hasToken();
|
Future<bool> hasToken();
|
||||||
Future<Either<AuthFailure, User>> currentUser();
|
Future<Either<AuthFailure, User>> currentUser();
|
||||||
|
|||||||
@ -21,11 +21,28 @@ class AuthRemoteDataProvider {
|
|||||||
Future<DC<AuthFailure, AuthDto>> login({
|
Future<DC<AuthFailure, AuthDto>> login({
|
||||||
required String email,
|
required String email,
|
||||||
required String password,
|
required String password,
|
||||||
|
required String deviceId,
|
||||||
|
required String deviceName,
|
||||||
|
required String deviceType,
|
||||||
|
required String platform,
|
||||||
|
required String osVersion,
|
||||||
|
required String appVersion,
|
||||||
|
String? fcmToken,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _apiClient.post(
|
final response = await _apiClient.post(
|
||||||
ApiPath.login,
|
ApiPath.login,
|
||||||
data: {'email': email, 'password': password},
|
data: {
|
||||||
|
'email': email,
|
||||||
|
'password': password,
|
||||||
|
'device_id': deviceId,
|
||||||
|
'device_name': deviceName,
|
||||||
|
'device_type': deviceType,
|
||||||
|
'platform': platform,
|
||||||
|
'os_version': osVersion,
|
||||||
|
'app_version': appVersion,
|
||||||
|
if (fcmToken != null) 'fcm_token': fcmToken,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.data['code'] == 401) {
|
if (response.data['code'] == 401) {
|
||||||
|
|||||||
@ -21,11 +21,25 @@ class AuthRepository implements IAuthRepository {
|
|||||||
Future<Either<AuthFailure, Auth>> login({
|
Future<Either<AuthFailure, Auth>> login({
|
||||||
required String email,
|
required String email,
|
||||||
required String password,
|
required String password,
|
||||||
|
required String deviceId,
|
||||||
|
required String deviceName,
|
||||||
|
required String deviceType,
|
||||||
|
required String platform,
|
||||||
|
required String osVersion,
|
||||||
|
required String appVersion,
|
||||||
|
String? fcmToken,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final result = await _remoteDataProvider.login(
|
final result = await _remoteDataProvider.login(
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
|
deviceId: deviceId,
|
||||||
|
deviceName: deviceName,
|
||||||
|
deviceType: deviceType,
|
||||||
|
platform: platform,
|
||||||
|
osVersion: osVersion,
|
||||||
|
appVersion: appVersion,
|
||||||
|
fcmToken: fcmToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.hasError) {
|
if (result.hasError) {
|
||||||
|
|||||||
@ -59,6 +59,8 @@ import 'package:apskel_owner_flutter/common/di/di_shared_preferences.dart'
|
|||||||
as _i402;
|
as _i402;
|
||||||
import 'package:apskel_owner_flutter/common/network/network_client.dart'
|
import 'package:apskel_owner_flutter/common/network/network_client.dart'
|
||||||
as _i543;
|
as _i543;
|
||||||
|
import 'package:apskel_owner_flutter/common/utils/device_info_service.dart'
|
||||||
|
as _i902;
|
||||||
import 'package:apskel_owner_flutter/common/utils/fcm_service.dart' as _i179;
|
import 'package:apskel_owner_flutter/common/utils/fcm_service.dart' as _i179;
|
||||||
import 'package:apskel_owner_flutter/domain/analytic/repositories/i_analytic_repository.dart'
|
import 'package:apskel_owner_flutter/domain/analytic/repositories/i_analytic_repository.dart'
|
||||||
as _i477;
|
as _i477;
|
||||||
@ -146,6 +148,7 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
preResolve: true,
|
preResolve: true,
|
||||||
);
|
);
|
||||||
gh.lazySingleton<_i179.FcmService>(() => _i179.FcmService());
|
gh.lazySingleton<_i179.FcmService>(() => _i179.FcmService());
|
||||||
|
gh.lazySingleton<_i902.DeviceInfoService>(() => _i902.DeviceInfoService());
|
||||||
gh.lazySingleton<_i543.NetworkClient>(
|
gh.lazySingleton<_i543.NetworkClient>(
|
||||||
() => _i543.NetworkClient(gh<_i895.Connectivity>()),
|
() => _i543.NetworkClient(gh<_i895.Connectivity>()),
|
||||||
);
|
);
|
||||||
@ -261,9 +264,6 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
gh.factory<_i945.AuthBloc>(
|
gh.factory<_i945.AuthBloc>(
|
||||||
() => _i945.AuthBloc(gh<_i49.IAuthRepository>()),
|
() => _i945.AuthBloc(gh<_i49.IAuthRepository>()),
|
||||||
);
|
);
|
||||||
gh.factory<_i775.LoginFormBloc>(
|
|
||||||
() => _i775.LoginFormBloc(gh<_i49.IAuthRepository>()),
|
|
||||||
);
|
|
||||||
gh.factory<_i574.LogoutFormBloc>(
|
gh.factory<_i574.LogoutFormBloc>(
|
||||||
() => _i574.LogoutFormBloc(gh<_i49.IAuthRepository>()),
|
() => _i574.LogoutFormBloc(gh<_i49.IAuthRepository>()),
|
||||||
);
|
);
|
||||||
@ -276,6 +276,13 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
gh.factory<_i147.UserEditFormBloc>(
|
gh.factory<_i147.UserEditFormBloc>(
|
||||||
() => _i147.UserEditFormBloc(gh<_i635.IUserRepository>()),
|
() => _i147.UserEditFormBloc(gh<_i635.IUserRepository>()),
|
||||||
);
|
);
|
||||||
|
gh.factory<_i775.LoginFormBloc>(
|
||||||
|
() => _i775.LoginFormBloc(
|
||||||
|
gh<_i49.IAuthRepository>(),
|
||||||
|
gh<_i902.DeviceInfoService>(),
|
||||||
|
gh<_i179.FcmService>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
gh.factory<_i346.InventoryReportBloc>(
|
gh.factory<_i346.InventoryReportBloc>(
|
||||||
() => _i346.InventoryReportBloc(
|
() => _i346.InventoryReportBloc(
|
||||||
gh<_i477.IAnalyticRepository>(),
|
gh<_i477.IAnalyticRepository>(),
|
||||||
|
|||||||
@ -14,11 +14,15 @@ import 'package:flutter/widgets.dart';
|
|||||||
class $AssetsImagesGen {
|
class $AssetsImagesGen {
|
||||||
const $AssetsImagesGen();
|
const $AssetsImagesGen();
|
||||||
|
|
||||||
|
/// File path: assets/images/ic_notification.png
|
||||||
|
AssetGenImage get icNotification =>
|
||||||
|
const AssetGenImage('assets/images/ic_notification.png');
|
||||||
|
|
||||||
/// File path: assets/images/logo.png
|
/// File path: assets/images/logo.png
|
||||||
AssetGenImage get logo => const AssetGenImage('assets/images/logo.png');
|
AssetGenImage get logo => const AssetGenImage('assets/images/logo.png');
|
||||||
|
|
||||||
/// List of all assets
|
/// List of all assets
|
||||||
List<AssetGenImage> get values => [logo];
|
List<AssetGenImage> get values => [icNotification, logo];
|
||||||
}
|
}
|
||||||
|
|
||||||
class Assets {
|
class Assets {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user