From d5de5fb712289f977bc906ae369f08a59ccf2208 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Thu, 5 Jun 2025 12:43:01 +0700 Subject: [PATCH] feat: adding limitation on the join and create room --- lib/core/endpoint/api_endpoint.dart | 4 +- lib/core/utils/custom_floating_loading.dart | 29 +++---- lib/data/services/connection_service.dart | 2 +- .../join_room/binding/join_room_binding.dart | 9 ++- .../controller/join_room_controller.dart | 29 ++++--- .../join_room/view/join_room_view.dart | 2 +- .../login/controllers/login_controller.dart | 14 ++-- .../controller/update_profile_controller.dart | 4 +- .../controller/quiz_creation_controller.dart | 4 +- .../controller/quiz_preview_controller.dart | 4 +- .../controller/register_controller.dart | 6 +- .../binding/room_maker_binding.dart | 2 + .../controller/room_maker_controller.dart | 18 ++++- .../room_maker/view/room_maker_view.dart | 76 +++++++++---------- 14 files changed, 114 insertions(+), 89 deletions(-) diff --git a/lib/core/endpoint/api_endpoint.dart b/lib/core/endpoint/api_endpoint.dart index ab82f99..2cbebcd 100644 --- a/lib/core/endpoint/api_endpoint.dart +++ b/lib/core/endpoint/api_endpoint.dart @@ -1,6 +1,6 @@ class APIEndpoint { - static const String baseUrl = "http://192.168.1.18:5000"; - // static const String baseUrl = "http://103.193.178.121:5000"; + // static const String baseUrl = "http://192.168.107.43:5000"; + static const String baseUrl = "http://103.193.178.121:5000"; static const String api = "$baseUrl/api"; static const String login = "/login"; diff --git a/lib/core/utils/custom_floating_loading.dart b/lib/core/utils/custom_floating_loading.dart index 2ac487d..6b1a581 100644 --- a/lib/core/utils/custom_floating_loading.dart +++ b/lib/core/utils/custom_floating_loading.dart @@ -1,23 +1,24 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; class CustomFloatingLoading { - static void showLoadingDialog(BuildContext context) { - showDialog( - context: context, + static const String _dialogId = 'custom_loading'; + + static void showLoadingDialog() { + Get.dialog( + PopScope( + canPop: false, + child: const Center(child: CircularProgressIndicator()), + ), barrierDismissible: false, - barrierColor: Colors.black.withValues(alpha: 0.3), - builder: (BuildContext context) { - return PopScope( - canPop: false, - child: const Center( - child: CircularProgressIndicator(), - ), - ); - }, + barrierColor: Colors.black.withAlpha(76), + name: _dialogId, ); } - static void hideLoadingDialog(BuildContext context) { - Navigator.of(context).pop(); + static void hideLoadingDialog() { + if (Get.isOverlaysOpen) { + Get.until((route) => route.settings.name != _dialogId); + } } } diff --git a/lib/data/services/connection_service.dart b/lib/data/services/connection_service.dart index de5609f..d929023 100644 --- a/lib/data/services/connection_service.dart +++ b/lib/data/services/connection_service.dart @@ -42,7 +42,7 @@ class ConnectionService extends GetxService { isConnected.value = results.any((result) => result != ConnectivityResult.none); } - Future checkConnection() async { + Future isHaveConnection() async { final result = await _connectivity.checkConnectivity(); return !result.contains(ConnectivityResult.none); } diff --git a/lib/feature/join_room/binding/join_room_binding.dart b/lib/feature/join_room/binding/join_room_binding.dart index cde17c8..38f74aa 100644 --- a/lib/feature/join_room/binding/join_room_binding.dart +++ b/lib/feature/join_room/binding/join_room_binding.dart @@ -1,5 +1,6 @@ import 'package:get/get.dart'; import 'package:quiz_app/data/controllers/user_controller.dart'; +import 'package:quiz_app/data/services/connection_service.dart'; import 'package:quiz_app/data/services/socket_service.dart'; import 'package:quiz_app/feature/join_room/controller/join_room_controller.dart'; @@ -8,6 +9,12 @@ class JoinRoomBinding extends Bindings { void dependencies() { Get.put(SocketService()); - Get.lazyPut(() => JoinRoomController(Get.find(), Get.find())); + Get.lazyPut( + () => JoinRoomController( + Get.find(), + Get.find(), + Get.find(), + ), + ); } } diff --git a/lib/feature/join_room/controller/join_room_controller.dart b/lib/feature/join_room/controller/join_room_controller.dart index 611dc7c..934c32e 100644 --- a/lib/feature/join_room/controller/join_room_controller.dart +++ b/lib/feature/join_room/controller/join_room_controller.dart @@ -1,23 +1,35 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/routes/app_pages.dart'; +import 'package:quiz_app/core/helper/connection_check.dart'; import 'package:quiz_app/core/utils/custom_floating_loading.dart'; +import 'package:quiz_app/core/utils/custom_notification.dart'; import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/dto/waiting_room_dto.dart'; import 'package:quiz_app/data/models/quiz/quiz_info_model.dart'; import 'package:quiz_app/data/models/session/session_info_model.dart'; import 'package:quiz_app/data/models/session/session_response_model.dart'; +import 'package:quiz_app/data/services/connection_service.dart'; import 'package:quiz_app/data/services/socket_service.dart'; class JoinRoomController extends GetxController { final SocketService _socketService; final UserController _userController; + final ConnectionService _connectionService; - JoinRoomController(this._socketService, this._userController); + JoinRoomController( + this._socketService, + this._userController, + this._connectionService, + ); final TextEditingController codeController = TextEditingController(); - void joinRoom() { + void joinRoom(BuildContext context) { + if (!_connectionService.isCurrentlyConnected) { + ConnectionNotification.noInternedConnection(); + return; + } final code = codeController.text.trim(); if (code.isEmpty) { @@ -29,18 +41,13 @@ class JoinRoomController extends GetxController { ); return; } - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); _socketService.initSocketConnection(); _socketService.joinRoom(sessionCode: code, userId: _userController.userData!.id); _socketService.errors.listen((error) { - Get.snackbar( - "not found", - "Ruangan tidak ditemukan", - backgroundColor: Get.theme.colorScheme.error.withValues(alpha: 0.9), - colorText: Colors.white, - ); - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomNotification.error(title: "not found", message: "Ruangan tidak ditemukan"); + CustomFloatingLoading.hideLoadingDialog(); }); _socketService.roomMessages.listen((data) { @@ -49,7 +56,7 @@ class JoinRoomController extends GetxController { final Map sessionInfoJson = dataPayload["session_info"]; final Map quizInfoJson = dataPayload["quiz_info"]; - CustomFloatingLoading.hideLoadingDialog(Get.context!); + // CustomFloatingLoading.hideLoadingDialog(context); Get.toNamed( AppRoutes.waitRoomPage, arguments: WaitingRoomDTO( diff --git a/lib/feature/join_room/view/join_room_view.dart b/lib/feature/join_room/view/join_room_view.dart index ffcf3ea..7dd1777 100644 --- a/lib/feature/join_room/view/join_room_view.dart +++ b/lib/feature/join_room/view/join_room_view.dart @@ -182,7 +182,7 @@ class JoinRoomView extends GetView { const SizedBox(height: 30), GlobalButton( text: context.tr("join_quiz_now"), - onPressed: controller.joinRoom, + onPressed: () => controller.joinRoom(context), ), ], ), diff --git a/lib/feature/login/controllers/login_controller.dart b/lib/feature/login/controllers/login_controller.dart index 8dbcaad..ba8a8e0 100644 --- a/lib/feature/login/controllers/login_controller.dart +++ b/lib/feature/login/controllers/login_controller.dart @@ -88,7 +88,7 @@ class LoginController extends GetxController { } try { isLoading.value = true; - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); final LoginResponseModel response = await _authService.loginWithEmail( LoginRequestModel(email: email, password: password), @@ -99,11 +99,11 @@ class LoginController extends GetxController { await _userStorageService.saveUser(userEntity); _userController.setUserFromEntity(userEntity); _userStorageService.isLogged = true; - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); Get.offAllNamed(AppRoutes.mainPage); } catch (e, stackTrace) { logC.e(e, stackTrace: stackTrace); - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); CustomNotification.error(title: "Gagal", message: "Periksa kembali email dan kata sandi Anda"); } } @@ -114,12 +114,12 @@ class LoginController extends GetxController { return; } try { - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); final user = await _googleAuthService.signIn(); if (user == null) { Get.snackbar("Kesalahan", "Masuk dengan Google dibatalkan"); - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); return; } @@ -127,7 +127,7 @@ class LoginController extends GetxController { if (idToken == null || idToken.isEmpty) { Get.snackbar("Kesalahan", "Tidak menerima ID Token dari Google"); - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); return; } @@ -137,7 +137,7 @@ class LoginController extends GetxController { await _userStorageService.saveUser(userEntity); _userController.setUserFromEntity(userEntity); _userStorageService.isLogged = true; - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); Get.offAllNamed(AppRoutes.mainPage); } catch (e, stackTrace) { logC.e("Google Sign-In Error: $e", stackTrace: stackTrace); diff --git a/lib/feature/profile/controller/update_profile_controller.dart b/lib/feature/profile/controller/update_profile_controller.dart index 8f4ce8b..5d283ea 100644 --- a/lib/feature/profile/controller/update_profile_controller.dart +++ b/lib/feature/profile/controller/update_profile_controller.dart @@ -61,7 +61,7 @@ class UpdateProfileController extends GetxController { Future saveProfile() async { if (!_validateInputs()) return; - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); final isSuccessUpdate = await _userService.updateProfileData( _userController.userData!.id, @@ -98,7 +98,7 @@ class UpdateProfileController extends GetxController { Get.back(); CustomNotification.success(title: "Success", message: "Profile updated successfully"); - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); } bool _isValidDateFormat(String date) { diff --git a/lib/feature/quiz_creation/controller/quiz_creation_controller.dart b/lib/feature/quiz_creation/controller/quiz_creation_controller.dart index d4a3914..8886991 100644 --- a/lib/feature/quiz_creation/controller/quiz_creation_controller.dart +++ b/lib/feature/quiz_creation/controller/quiz_creation_controller.dart @@ -224,7 +224,7 @@ class QuizCreationController extends GetxController { } void generateQuiz() async { - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); try { BaseResponseModel> response = await _quizService.createQuizAuto(inputSentenceTC.text); @@ -262,7 +262,7 @@ class QuizCreationController extends GetxController { } catch (e) { logC.e("Error while generating quiz: $e"); } finally { - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); isGenerate.value = false; if (quizData.isNotEmpty && selectedQuizIndex.value == 0) { diff --git a/lib/feature/quiz_preview/controller/quiz_preview_controller.dart b/lib/feature/quiz_preview/controller/quiz_preview_controller.dart index 177caf6..a74d267 100644 --- a/lib/feature/quiz_preview/controller/quiz_preview_controller.dart +++ b/lib/feature/quiz_preview/controller/quiz_preview_controller.dart @@ -92,7 +92,7 @@ class QuizPreviewController extends GetxController { } isLoading.value = true; - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); final now = DateTime.now(); final String formattedDate = "${now.day.toString().padLeft(2, '0')}-${now.month.toString().padLeft(2, '0')}-${now.year}"; @@ -122,7 +122,7 @@ class QuizPreviewController extends GetxController { logC.e(e); } finally { isLoading.value = false; - // CustomFloatingLoading.hideLoadingDialog(Get.context!); + // CustomFloatingLoading.hideLoadingDialog(); } } diff --git a/lib/feature/register/controller/register_controller.dart b/lib/feature/register/controller/register_controller.dart index 506e211..13816d8 100644 --- a/lib/feature/register/controller/register_controller.dart +++ b/lib/feature/register/controller/register_controller.dart @@ -81,7 +81,7 @@ class RegisterController extends GetxController { } try { - CustomFloatingLoading.showLoadingDialog(Get.context!); + CustomFloatingLoading.showLoadingDialog(); await _authService.register( RegisterRequestModel( email: email, @@ -93,10 +93,10 @@ class RegisterController extends GetxController { ); Get.back(); - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); CustomNotification.success(title: "Pendaftaran Berhasil", message: "Akun berhasil dibuat"); } catch (e) { - CustomFloatingLoading.hideLoadingDialog(Get.context!); + CustomFloatingLoading.hideLoadingDialog(); String errorMessage = e.toString().replaceFirst("Exception: ", ""); diff --git a/lib/feature/room_maker/binding/room_maker_binding.dart b/lib/feature/room_maker/binding/room_maker_binding.dart index 24d6211..73af401 100644 --- a/lib/feature/room_maker/binding/room_maker_binding.dart +++ b/lib/feature/room_maker/binding/room_maker_binding.dart @@ -1,5 +1,6 @@ import 'package:get/get.dart'; import 'package:quiz_app/data/controllers/user_controller.dart'; +import 'package:quiz_app/data/services/connection_service.dart'; import 'package:quiz_app/data/services/quiz_service.dart'; import 'package:quiz_app/data/services/session_service.dart'; import 'package:quiz_app/data/services/socket_service.dart'; @@ -16,6 +17,7 @@ class RoomMakerBinding extends Bindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); } } diff --git a/lib/feature/room_maker/controller/room_maker_controller.dart b/lib/feature/room_maker/controller/room_maker_controller.dart index e0b0fdc..c64c2ac 100644 --- a/lib/feature/room_maker/controller/room_maker_controller.dart +++ b/lib/feature/room_maker/controller/room_maker_controller.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/routes/app_pages.dart'; +import 'package:quiz_app/core/helper/connection_check.dart'; +import 'package:quiz_app/core/utils/custom_notification.dart'; import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/dto/waiting_room_dto.dart'; import 'package:quiz_app/data/models/base/base_model.dart'; @@ -9,6 +11,7 @@ import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/models/session/session_info_model.dart'; import 'package:quiz_app/data/models/session/session_request_model.dart'; import 'package:quiz_app/data/models/session/session_response_model.dart'; +import 'package:quiz_app/data/services/connection_service.dart'; import 'package:quiz_app/data/services/quiz_service.dart'; import 'package:quiz_app/data/services/session_service.dart'; import 'package:quiz_app/data/services/socket_service.dart'; @@ -18,12 +21,14 @@ class RoomMakerController extends GetxController { final UserController _userController; final SocketService _socketService; final QuizService _quizService; + final ConnectionService _connectionService; RoomMakerController( this._sessionService, this._userController, this._socketService, this._quizService, + this._connectionService, ); final selectedQuiz = Rxn(); @@ -47,6 +52,10 @@ class RoomMakerController extends GetxController { } Future loadQuiz({bool reset = false}) async { + if (!await _connectionService.isHaveConnection()) { + ConnectionNotification.noInternedConnection(); + return; + } if (isLoading) return; isLoading = true; @@ -92,8 +101,13 @@ class RoomMakerController extends GetxController { } void onCreateRoom() async { - if (nameTC.text.trim().isEmpty || selectedQuiz.value == null) { - Get.snackbar("Gagal", "Nama room dan kuis harus dipilih."); + if (nameTC.text.trim().isEmpty || maxPlayerTC.text.trim().isEmpty || selectedQuiz.value == null) { + CustomNotification.error(title: "Gagal", message: "Nama room, maksimal pemain dan kuis harus dipilih."); + return; + } + + if (!await _connectionService.isHaveConnection()) { + ConnectionNotification.noInternedConnection(); return; } diff --git a/lib/feature/room_maker/view/room_maker_view.dart b/lib/feature/room_maker/view/room_maker_view.dart index 9c26e92..fe5eaea 100644 --- a/lib/feature/room_maker/view/room_maker_view.dart +++ b/lib/feature/room_maker/view/room_maker_view.dart @@ -588,52 +588,46 @@ class RoomMakerView extends GetView { } Widget _buildCreateRoomButton() { - return Obx(() { - final canCreate = controller.selectedQuiz.value != null && controller.nameTC.text.isNotEmpty && controller.maxPlayerTC.text.isNotEmpty; - - return AnimatedContainer( - duration: const Duration(milliseconds: 300), - width: MediaQuery.of(Get.context!).size.width - 32, - height: 56, - child: Material( - elevation: canCreate ? 8 : 2, + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: MediaQuery.of(Get.context!).size.width - 32, + height: 56, + child: Material( + elevation: 8, + borderRadius: BorderRadius.circular(16), + child: InkWell( borderRadius: BorderRadius.circular(16), - child: InkWell( - borderRadius: BorderRadius.circular(16), - onTap: canCreate ? controller.onCreateRoom : null, - child: Container( - decoration: BoxDecoration( - gradient: canCreate - ? LinearGradient( - colors: [AppColors.primaryBlue, AppColors.primaryBlue.withValues(alpha: 0.8)], - ) - : null, - color: !canCreate ? Colors.grey[300] : null, - borderRadius: BorderRadius.circular(16), + onTap: controller.onCreateRoom, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [AppColors.primaryBlue, AppColors.primaryBlue.withValues(alpha: 0.8)], ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.add_circle, - color: canCreate ? Colors.white : Colors.grey[500], - size: 24, + color: Colors.grey[300], + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.add_circle, + color: Colors.white, + size: 24, + ), + const SizedBox(width: 12), + Text( + "Buat Room Sekarang", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white, ), - const SizedBox(width: 12), - Text( - "Buat Room Sekarang", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: canCreate ? Colors.white : Colors.grey[500], - ), - ), - ], - ), + ), + ], ), ), ), - ); - }); + ), + ); } }