fix: login id not registered

This commit is contained in:
akhdanre 2025-04-28 13:04:29 +07:00
parent 6adcb2e471
commit e4ac170a21
8 changed files with 97 additions and 40 deletions

View File

@ -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/core/utils/logger.dart';
import 'package:quiz_app/data/entity/user/user_entity.dart';
import 'package:quiz_app/data/services/user_storage_service.dart'; import 'package:quiz_app/data/services/user_storage_service.dart';
class UserController extends GetxController { class UserController extends GetxController {
@ -10,7 +12,8 @@ class UserController extends GetxController {
Rx<String> userName = "".obs; Rx<String> userName = "".obs;
Rx<String?> userImage = Rx<String?>(null); Rx<String?> userImage = Rx<String?>(null);
Rx<String> email = "".obs; Rx<String> email = "".obs;
String userId = "";
UserEntity? userData;
@override @override
void onInit() { void onInit() {
@ -21,12 +24,19 @@ class UserController extends GetxController {
Future<void> loadUser() async { Future<void> loadUser() async {
final data = await _userStorageService.loadUser(); final data = await _userStorageService.loadUser();
if (data != null) { if (data != null) {
userData = data;
userName.value = data.name; userName.value = data.name;
userImage.value = data.picUrl; userImage.value = data.picUrl;
email.value = data.email; email.value = data.email;
userId = data.id ?? "";
logC.i("user data $userId");
logC.i("Loaded user: ${data.toJson()}"); 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;
}
} }

View File

@ -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<String, dynamic> json) {
return UserEntity(
id: json['id'],
name: json['name'],
email: json['email'],
picUrl: json['pic_url'],
locale: json['locale'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'pic_url': picUrl,
'locale': locale,
};
}
}

View File

@ -25,7 +25,7 @@ class LoginResponseModel {
factory LoginResponseModel.fromJson(Map<String, dynamic> json) { factory LoginResponseModel.fromJson(Map<String, dynamic> json) {
return LoginResponseModel( return LoginResponseModel(
id: json['_id'], id: json['id'],
googleId: json['google_id'], googleId: json['google_id'],
email: json['email'], email: json['email'],
name: json['name'], name: json['name'],
@ -40,7 +40,7 @@ class LoginResponseModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'_id': id, 'id': id,
'google_id': googleId, 'google_id': googleId,
'email': email, 'email': email,
'name': name, 'name': name,

View File

@ -1,22 +1,22 @@
import 'dart:convert'; 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'; import 'package:shared_preferences/shared_preferences.dart';
class UserStorageService { class UserStorageService {
static const _userKey = 'user_data'; static const _userKey = 'user_data';
bool isLogged = false; bool isLogged = false;
Future<void> saveUser(LoginResponseModel user) async { Future<void> saveUser(UserEntity user) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setString(_userKey, jsonEncode(user.toJson())); await prefs.setString(_userKey, jsonEncode(user.toJson()));
} }
Future<LoginResponseModel?> loadUser() async { Future<UserEntity?> loadUser() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final jsonString = prefs.getString(_userKey); final jsonString = prefs.getString(_userKey);
if (jsonString == null) return null; if (jsonString == null) return null;
return LoginResponseModel.fromJson(jsonDecode(jsonString)); return UserEntity.fromJson(jsonDecode(jsonString));
} }
Future<void> clearUser() async { Future<void> clearUser() async {

View File

@ -1,5 +1,6 @@
import 'package:get/get_core/get_core.dart'; import 'package:get/get_core/get_core.dart';
import 'package:get/get_instance/get_instance.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/auth_service.dart';
import 'package:quiz_app/data/services/user_storage_service.dart'; import 'package:quiz_app/data/services/user_storage_service.dart';
import 'package:quiz_app/feature/login/controllers/login_controller.dart'; import 'package:quiz_app/feature/login/controllers/login_controller.dart';
@ -8,6 +9,6 @@ class LoginBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.lazyPut(() => AuthService()); Get.lazyPut(() => AuthService());
Get.lazyPut(() => LoginController(Get.find<AuthService>(), Get.find<UserStorageService>())); Get.lazyPut(() => LoginController(Get.find<AuthService>(), Get.find<UserStorageService>(), Get.find<UserController>()));
} }
} }

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.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/app/routes/app_pages.dart';
import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/component/global_button.dart';
import 'package:quiz_app/core/utils/logger.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_request_model.dart';
import 'package:quiz_app/data/models/login/login_response_model.dart'; import 'package:quiz_app/data/models/login/login_response_model.dart';
import 'package:quiz_app/data/services/auth_service.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 { class LoginController extends GetxController {
final AuthService _authService; final AuthService _authService;
final UserStorageService _userStorageService; final UserStorageService _userStorageService;
final UserController _userController;
LoginController(this._authService, this._userStorageService); LoginController(this._authService, this._userStorageService, this._userController);
final TextEditingController emailController = TextEditingController(); final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController(); final TextEditingController passwordController = TextEditingController();
final Rx<ButtonType> isButtonEnabled = ButtonType.disabled.obs; final Rx<ButtonType> isButtonEnabled = ButtonType.disabled.obs;
final RxBool isPasswordHidden = true.obs;
var isPasswordHidden = true.obs; final RxBool isLoading = false.obs;
var isLoading = false.obs;
final GoogleSignIn _googleSignIn = GoogleSignIn( final GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: ['email', 'profile', 'openid'], scopes: ['email', 'profile', 'openid'],
@ -37,22 +39,17 @@ class LoginController extends GetxController {
void _validateFields() { void _validateFields() {
final isEmailNotEmpty = emailController.text.trim().isNotEmpty; final isEmailNotEmpty = emailController.text.trim().isNotEmpty;
final isPasswordNotEmpty = passwordController.text.trim().isNotEmpty; final isPasswordNotEmpty = passwordController.text.trim().isNotEmpty;
print('its type'); isButtonEnabled.value = (isEmailNotEmpty && isPasswordNotEmpty) ? ButtonType.primary : ButtonType.disabled;
if (isEmailNotEmpty && isPasswordNotEmpty) {
isButtonEnabled.value = ButtonType.primary;
} else {
isButtonEnabled.value = ButtonType.disabled;
}
} }
void togglePasswordVisibility() { void togglePasswordVisibility() {
isPasswordHidden.value = !isPasswordHidden.value; isPasswordHidden.toggle();
} }
/// **🔹 Login via Email & Password** /// **🔹 Login via Email & Password**
Future<void> loginWithEmail() async { Future<void> loginWithEmail() async {
String email = emailController.text.trim(); final email = emailController.text.trim();
String password = passwordController.text.trim(); final password = passwordController.text.trim();
if (email.isEmpty || password.isEmpty) { if (email.isEmpty || password.isEmpty) {
Get.snackbar("Error", "Email and password are required"); Get.snackbar("Error", "Email and password are required");
@ -62,18 +59,17 @@ class LoginController extends GetxController {
try { try {
isLoading.value = true; isLoading.value = true;
LoginResponseModel response = await _authService.loginWithEmail( final LoginResponseModel response = await _authService.loginWithEmail(
LoginRequestModel( LoginRequestModel(email: email, password: password),
email: email,
password: password,
),
); );
await _userStorageService.saveUser(response); final userEntity = _convertLoginResponseToUserEntity(response);
await _userStorageService.saveUser(userEntity);
_userController.setUserFromEntity(userEntity);
_userStorageService.isLogged = true; _userStorageService.isLogged = true;
Get.toNamed(AppRoutes.mainPage); Get.offAllNamed(AppRoutes.mainPage);
} catch (e, stackTrace) { } catch (e, stackTrace) {
logC.e(e, stackTrace: stackTrace); logC.e(e, stackTrace: stackTrace);
Get.snackbar("Error", "Failed to connect to server"); Get.snackbar("Error", "Failed to connect to server");
@ -82,6 +78,7 @@ class LoginController extends GetxController {
} }
} }
/// **🔹 Login via Google**
Future<void> loginWithGoogle() async { Future<void> loginWithGoogle() async {
try { try {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
@ -91,25 +88,39 @@ class LoginController extends GetxController {
} }
final GoogleSignInAuthentication googleAuth = await googleUser.authentication; 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."); Get.snackbar("Error", "Google sign-in failed. No ID Token received.");
return; return;
} }
String idToken = googleAuth.idToken!; final LoginResponseModel response = await _authService.loginWithGoogle(idToken);
final response = await _authService.loginWithGoogle(idToken); final userEntity = _convertLoginResponseToUserEntity(response);
await _userStorageService.saveUser(response);
await _userStorageService.saveUser(userEntity);
_userController.setUserFromEntity(userEntity);
_userStorageService.isLogged = true; _userStorageService.isLogged = true;
Get.toNamed(AppRoutes.mainPage); Get.offAllNamed(AppRoutes.mainPage);
} catch (e, stackTrace) { } catch (e, stackTrace) {
logC.e("Google Sign-In Error: $e", stackTrace: stackTrace); logC.e("Google Sign-In Error: $e", stackTrace: stackTrace);
Get.snackbar("Error", "Google sign-in error"); 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,
);
}
} }

View File

@ -13,7 +13,7 @@ class ProfileView extends GetView<ProfileController> {
child: Padding( child: Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Obx(() { child: Obx(() {
return ListView( return Column(
children: [ children: [
const SizedBox(height: 20), const SizedBox(height: 20),
_buildAvatar(), _buildAvatar(),

View File

@ -57,7 +57,7 @@ class QuizPreviewController extends GetxController {
date: formattedDate, date: formattedDate,
totalQuiz: data.length, totalQuiz: data.length,
limitDuration: data.length * 30, limitDuration: data.length * 30,
authorId: _userController.userId, authorId: _userController.userData!.id,
questionListings: _mapQuestionsToListings(data), questionListings: _mapQuestionsToListings(data),
); );
final success = await _quizService.createQuiz(quizRequest); final success = await _quizService.createQuiz(quizRequest);