diff --git a/lib/data/controllers/user_controller.dart b/lib/data/controllers/user_controller.dart index 5cda711..35d5d4b 100644 --- a/lib/data/controllers/user_controller.dart +++ b/lib/data/controllers/user_controller.dart @@ -1,5 +1,7 @@ -import 'package:get/get.dart'; +import 'package:get/get_rx/src/rx_types/rx_types.dart'; +import 'package:get/get_state_manager/src/simple/get_controllers.dart'; import 'package:quiz_app/core/utils/logger.dart'; +import 'package:quiz_app/data/entity/user/user_entity.dart'; import 'package:quiz_app/data/services/user_storage_service.dart'; class UserController extends GetxController { @@ -10,7 +12,8 @@ class UserController extends GetxController { Rx userName = "".obs; Rx userImage = Rx(null); Rx email = "".obs; - String userId = ""; + + UserEntity? userData; @override void onInit() { @@ -21,12 +24,19 @@ class UserController extends GetxController { Future loadUser() async { final data = await _userStorageService.loadUser(); if (data != null) { + userData = data; userName.value = data.name; userImage.value = data.picUrl; email.value = data.email; - userId = data.id ?? ""; - logC.i("user data $userId"); logC.i("Loaded user: ${data.toJson()}"); } } + + void setUserFromEntity(UserEntity data) { + final userEntity = data; + userData = userEntity; + userName.value = userEntity.name; + userImage.value = userEntity.picUrl; + email.value = userEntity.email; + } } diff --git a/lib/data/entity/user/user_entity.dart b/lib/data/entity/user/user_entity.dart new file mode 100644 index 0000000..7528448 --- /dev/null +++ b/lib/data/entity/user/user_entity.dart @@ -0,0 +1,35 @@ +class UserEntity { + final String id; + final String name; + final String email; + final String? picUrl; + final String? locale; + +UserEntity({ + required this.id, + required this.name, + required this.email, + this.picUrl, + this.locale, + }); + + factory UserEntity.fromJson(Map json) { + return UserEntity( + id: json['id'], + name: json['name'], + email: json['email'], + picUrl: json['pic_url'], + locale: json['locale'], + ); + } + + Map toJson() { + return { + 'id': id, + 'name': name, + 'email': email, + 'pic_url': picUrl, + 'locale': locale, + }; + } +} diff --git a/lib/data/models/login/login_response_model.dart b/lib/data/models/login/login_response_model.dart index 0e2a110..7d10c6a 100644 --- a/lib/data/models/login/login_response_model.dart +++ b/lib/data/models/login/login_response_model.dart @@ -25,7 +25,7 @@ class LoginResponseModel { factory LoginResponseModel.fromJson(Map json) { return LoginResponseModel( - id: json['_id'], + id: json['id'], googleId: json['google_id'], email: json['email'], name: json['name'], @@ -40,7 +40,7 @@ class LoginResponseModel { Map toJson() { return { - '_id': id, + 'id': id, 'google_id': googleId, 'email': email, 'name': name, diff --git a/lib/data/services/user_storage_service.dart b/lib/data/services/user_storage_service.dart index 71e599e..a806829 100644 --- a/lib/data/services/user_storage_service.dart +++ b/lib/data/services/user_storage_service.dart @@ -1,22 +1,22 @@ import 'dart:convert'; -import 'package:quiz_app/data/models/login/login_response_model.dart'; +import 'package:quiz_app/data/entity/user/user_entity.dart'; import 'package:shared_preferences/shared_preferences.dart'; class UserStorageService { static const _userKey = 'user_data'; bool isLogged = false; - Future saveUser(LoginResponseModel user) async { + Future saveUser(UserEntity user) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_userKey, jsonEncode(user.toJson())); } - Future loadUser() async { + Future loadUser() async { final prefs = await SharedPreferences.getInstance(); final jsonString = prefs.getString(_userKey); if (jsonString == null) return null; - return LoginResponseModel.fromJson(jsonDecode(jsonString)); + return UserEntity.fromJson(jsonDecode(jsonString)); } Future clearUser() async { diff --git a/lib/feature/login/bindings/login_binding.dart b/lib/feature/login/bindings/login_binding.dart index dce6efd..6c7e36f 100644 --- a/lib/feature/login/bindings/login_binding.dart +++ b/lib/feature/login/bindings/login_binding.dart @@ -1,5 +1,6 @@ import 'package:get/get_core/get_core.dart'; import 'package:get/get_instance/get_instance.dart'; +import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/services/auth_service.dart'; import 'package:quiz_app/data/services/user_storage_service.dart'; import 'package:quiz_app/feature/login/controllers/login_controller.dart'; @@ -8,6 +9,6 @@ class LoginBinding extends Bindings { @override void dependencies() { Get.lazyPut(() => AuthService()); - Get.lazyPut(() => LoginController(Get.find(), Get.find())); + Get.lazyPut(() => LoginController(Get.find(), Get.find(), Get.find())); } } diff --git a/lib/feature/login/controllers/login_controller.dart b/lib/feature/login/controllers/login_controller.dart index c052869..07f3e2d 100644 --- a/lib/feature/login/controllers/login_controller.dart +++ b/lib/feature/login/controllers/login_controller.dart @@ -1,9 +1,11 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_sign_in/google_sign_in.dart'; -import 'package:flutter/material.dart'; import 'package:quiz_app/app/routes/app_pages.dart'; import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/core/utils/logger.dart'; +import 'package:quiz_app/data/controllers/user_controller.dart'; +import 'package:quiz_app/data/entity/user/user_entity.dart'; import 'package:quiz_app/data/models/login/login_request_model.dart'; import 'package:quiz_app/data/models/login/login_response_model.dart'; import 'package:quiz_app/data/services/auth_service.dart'; @@ -12,16 +14,16 @@ import 'package:quiz_app/data/services/user_storage_service.dart'; class LoginController extends GetxController { final AuthService _authService; final UserStorageService _userStorageService; + final UserController _userController; - LoginController(this._authService, this._userStorageService); + LoginController(this._authService, this._userStorageService, this._userController); final TextEditingController emailController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); final Rx isButtonEnabled = ButtonType.disabled.obs; - - var isPasswordHidden = true.obs; - var isLoading = false.obs; + final RxBool isPasswordHidden = true.obs; + final RxBool isLoading = false.obs; final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email', 'profile', 'openid'], @@ -37,22 +39,17 @@ class LoginController extends GetxController { void _validateFields() { final isEmailNotEmpty = emailController.text.trim().isNotEmpty; final isPasswordNotEmpty = passwordController.text.trim().isNotEmpty; - print('its type'); - if (isEmailNotEmpty && isPasswordNotEmpty) { - isButtonEnabled.value = ButtonType.primary; - } else { - isButtonEnabled.value = ButtonType.disabled; - } + isButtonEnabled.value = (isEmailNotEmpty && isPasswordNotEmpty) ? ButtonType.primary : ButtonType.disabled; } void togglePasswordVisibility() { - isPasswordHidden.value = !isPasswordHidden.value; + isPasswordHidden.toggle(); } /// **🔹 Login via Email & Password** Future loginWithEmail() async { - String email = emailController.text.trim(); - String password = passwordController.text.trim(); + final email = emailController.text.trim(); + final password = passwordController.text.trim(); if (email.isEmpty || password.isEmpty) { Get.snackbar("Error", "Email and password are required"); @@ -62,18 +59,17 @@ class LoginController extends GetxController { try { isLoading.value = true; - LoginResponseModel response = await _authService.loginWithEmail( - LoginRequestModel( - email: email, - password: password, - ), + final LoginResponseModel response = await _authService.loginWithEmail( + LoginRequestModel(email: email, password: password), ); - await _userStorageService.saveUser(response); + final userEntity = _convertLoginResponseToUserEntity(response); + await _userStorageService.saveUser(userEntity); + _userController.setUserFromEntity(userEntity); _userStorageService.isLogged = true; - Get.toNamed(AppRoutes.mainPage); + Get.offAllNamed(AppRoutes.mainPage); } catch (e, stackTrace) { logC.e(e, stackTrace: stackTrace); Get.snackbar("Error", "Failed to connect to server"); @@ -82,6 +78,7 @@ class LoginController extends GetxController { } } + /// **🔹 Login via Google** Future loginWithGoogle() async { try { final GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); @@ -91,25 +88,39 @@ class LoginController extends GetxController { } final GoogleSignInAuthentication googleAuth = await googleUser.authentication; + final idToken = googleAuth.idToken; - if (googleAuth.idToken == null || googleAuth.idToken!.isEmpty) { + if (idToken == null || idToken.isEmpty) { Get.snackbar("Error", "Google sign-in failed. No ID Token received."); return; } - String idToken = googleAuth.idToken!; + final LoginResponseModel response = await _authService.loginWithGoogle(idToken); - final response = await _authService.loginWithGoogle(idToken); - await _userStorageService.saveUser(response); + final userEntity = _convertLoginResponseToUserEntity(response); + await _userStorageService.saveUser(userEntity); + _userController.setUserFromEntity(userEntity); _userStorageService.isLogged = true; - Get.toNamed(AppRoutes.mainPage); + Get.offAllNamed(AppRoutes.mainPage); } catch (e, stackTrace) { logC.e("Google Sign-In Error: $e", stackTrace: stackTrace); Get.snackbar("Error", "Google sign-in error"); } } - void goToRegsPage() => Get.toNamed(AppRoutes.mainPage); + void goToRegsPage() => Get.toNamed(AppRoutes.registerPage); + + /// Helper untuk convert LoginResponseModel ke UserEntity + UserEntity _convertLoginResponseToUserEntity(LoginResponseModel response) { + logC.i("user id : ${response.id}"); + return UserEntity( + id: response.id ?? '', + name: response.name, + email: response.email, + picUrl: response.picUrl, + locale: response.locale, + ); + } } diff --git a/lib/feature/profile/view/profile_view.dart b/lib/feature/profile/view/profile_view.dart index a911402..da67a4d 100644 --- a/lib/feature/profile/view/profile_view.dart +++ b/lib/feature/profile/view/profile_view.dart @@ -13,7 +13,7 @@ class ProfileView extends GetView { child: Padding( padding: const EdgeInsets.all(20), child: Obx(() { - return ListView( + return Column( children: [ const SizedBox(height: 20), _buildAvatar(), diff --git a/lib/feature/quiz_preview/controller/quiz_preview_controller.dart b/lib/feature/quiz_preview/controller/quiz_preview_controller.dart index e2d2f90..e37d6d5 100644 --- a/lib/feature/quiz_preview/controller/quiz_preview_controller.dart +++ b/lib/feature/quiz_preview/controller/quiz_preview_controller.dart @@ -57,7 +57,7 @@ class QuizPreviewController extends GetxController { date: formattedDate, totalQuiz: data.length, limitDuration: data.length * 30, - authorId: _userController.userId, + authorId: _userController.userData!.id, questionListings: _mapQuestionsToListings(data), ); final success = await _quizService.createQuiz(quizRequest);