From 5f54ca6c8c4c3f9af09a2d3a7c5dafa3778d5eed Mon Sep 17 00:00:00 2001 From: akhdanre Date: Mon, 12 May 2025 22:41:14 +0700 Subject: [PATCH] feat: adding leave page in the waiting room --- lib/app/app.dart | 2 +- lib/component/global_button.dart | 6 ++-- lib/component/global_text_field.dart | 6 ++-- lib/data/services/socket_service.dart | 12 +++++-- .../controller/join_room_controller.dart | 1 + .../join_room/view/join_room_view.dart | 1 + .../binding/waiting_room_binding.dart | 6 +++- .../controller/waiting_room_controller.dart | 33 +++++++++++++++---- .../waiting_room/view/waiting_room_view.dart | 9 ++++- 9 files changed, 60 insertions(+), 16 deletions(-) diff --git a/lib/app/app.dart b/lib/app/app.dart index 9c22969..e1cf154 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -11,7 +11,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return GetMaterialApp( title: 'Quiz App', - locale: Get.locale ?? context.locale, // 🔁 This ensures GetX reacts to locale changes + locale: Get.locale ?? context.locale, fallbackLocale: const Locale('en', 'US'), localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, diff --git a/lib/component/global_button.dart b/lib/component/global_button.dart index eb54c81..9effaae 100644 --- a/lib/component/global_button.dart +++ b/lib/component/global_button.dart @@ -6,11 +6,13 @@ class GlobalButton extends StatelessWidget { final VoidCallback? onPressed; final String text; final ButtonType type; + final Color baseColor; const GlobalButton({ super.key, required this.text, required this.onPressed, + this.baseColor = const Color(0xFF0052CC), this.type = ButtonType.primary, }); @@ -24,12 +26,12 @@ class GlobalButton extends StatelessWidget { switch (type) { case ButtonType.primary: - backgroundColor = const Color(0xFF0052CC); + backgroundColor = baseColor; foregroundColor = Colors.white; break; case ButtonType.secondary: backgroundColor = Colors.white; - foregroundColor = const Color(0xFF0052CC); + foregroundColor = baseColor; borderColor = const Color(0xFF0052CC); break; case ButtonType.disabled: diff --git a/lib/component/global_text_field.dart b/lib/component/global_text_field.dart index 73ffe19..2e25c7f 100644 --- a/lib/component/global_text_field.dart +++ b/lib/component/global_text_field.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; class GlobalTextField extends StatelessWidget { final TextEditingController controller; @@ -10,6 +9,7 @@ class GlobalTextField extends StatelessWidget { final bool obscureText; final VoidCallback? onToggleVisibility; final TextInputType textInputType; + final bool forceUpperCase; const GlobalTextField( {super.key, @@ -20,6 +20,7 @@ class GlobalTextField extends StatelessWidget { this.isPassword = false, this.obscureText = false, this.onToggleVisibility, + this.forceUpperCase = false, this.textInputType = TextInputType.text}); @override @@ -28,7 +29,8 @@ class GlobalTextField extends StatelessWidget { controller: controller, keyboardType: textInputType, obscureText: isPassword ? obscureText : false, - maxLines: limitTextLine, // <-- ini tambahan dari limitTextLine + maxLines: limitTextLine, + textCapitalization: forceUpperCase ? TextCapitalization.characters : TextCapitalization.none, decoration: InputDecoration( labelText: labelText, labelStyle: const TextStyle( diff --git a/lib/data/services/socket_service.dart b/lib/data/services/socket_service.dart index b2b8d27..3def272 100644 --- a/lib/data/services/socket_service.dart +++ b/lib/data/services/socket_service.dart @@ -58,15 +58,21 @@ class SocketService { } void joinRoom({required String sessionCode, required String userId}) { - socket.emit('join_room', { + var data = { 'session_code': sessionCode, 'user_id': userId, - }); + }; + print(data); + socket.emit( + 'join_room', + data, + ); } - void leaveRoom({required String sessionId, String username = "anonymous"}) { + void leaveRoom({required String sessionId, required String userId, String username = "anonymous"}) { socket.emit('leave_room', { 'session_id': sessionId, + 'user_id': userId, 'username': username, }); } diff --git a/lib/feature/join_room/controller/join_room_controller.dart b/lib/feature/join_room/controller/join_room_controller.dart index a92ecf4..d5c1b26 100644 --- a/lib/feature/join_room/controller/join_room_controller.dart +++ b/lib/feature/join_room/controller/join_room_controller.dart @@ -27,6 +27,7 @@ class JoinRoomController extends GetxController { return; } _socketService.initSocketConnection(); + _socketService.joinRoom(sessionCode: code, userId: _userController.userData!.id); Get.toNamed(AppRoutes.waitRoomPage, arguments: WaitingRoomDTO(false, SessionResponseModel(sessionId: "", sessionCode: code))); } diff --git a/lib/feature/join_room/view/join_room_view.dart b/lib/feature/join_room/view/join_room_view.dart index 663529a..45103a4 100644 --- a/lib/feature/join_room/view/join_room_view.dart +++ b/lib/feature/join_room/view/join_room_view.dart @@ -54,6 +54,7 @@ class JoinRoomView extends GetView { controller: controller.codeController, hintText: context.tr("room_code_hint"), textInputType: TextInputType.text, + forceUpperCase: true, ), const SizedBox(height: 30), GlobalButton( diff --git a/lib/feature/waiting_room/binding/waiting_room_binding.dart b/lib/feature/waiting_room/binding/waiting_room_binding.dart index ccd1b1c..1fb5fbc 100644 --- a/lib/feature/waiting_room/binding/waiting_room_binding.dart +++ b/lib/feature/waiting_room/binding/waiting_room_binding.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/services/socket_service.dart'; import 'package:quiz_app/feature/waiting_room/controller/waiting_room_controller.dart'; @@ -6,6 +7,9 @@ class WaitingRoomBinding extends Bindings { @override void dependencies() { if (!Get.isRegistered()) Get.put(SocketService()); - Get.lazyPut(() => WaitingRoomController(Get.find())); + Get.lazyPut(() => WaitingRoomController( + Get.find(), + Get.find(), + )); } } diff --git a/lib/feature/waiting_room/controller/waiting_room_controller.dart b/lib/feature/waiting_room/controller/waiting_room_controller.dart index 856231c..72bac11 100644 --- a/lib/feature/waiting_room/controller/waiting_room_controller.dart +++ b/lib/feature/waiting_room/controller/waiting_room_controller.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/routes/app_pages.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_listing_model.dart'; import 'package:quiz_app/data/models/session/session_response_model.dart'; @@ -10,7 +12,8 @@ import 'package:quiz_app/data/services/socket_service.dart'; class WaitingRoomController extends GetxController { final SocketService _socketService; - WaitingRoomController(this._socketService); + final UserController _userController; + WaitingRoomController(this._socketService, this._userController); final sessionCode = ''.obs; final quizMeta = Rx(null); @@ -20,6 +23,7 @@ class WaitingRoomController extends GetxController { final quizQuestions = >[].obs; final isQuizStarted = false.obs; + SessionResponseModel? roomData; @override void onInit() { super.onInit(); @@ -30,10 +34,10 @@ class WaitingRoomController extends GetxController { void _loadInitialData() { final data = Get.arguments as WaitingRoomDTO; - SessionResponseModel? roomData = data.data; + roomData = data.data; isAdmin.value = data.isAdmin; - sessionCode.value = roomData.sessionCode; + sessionCode.value = roomData!.sessionCode; quizMeta.value = QuizListingModel( quizId: "q123", @@ -49,9 +53,18 @@ class WaitingRoomController extends GetxController { void _registerSocketListeners() { _socketService.roomMessages.listen((data) { - final user = data["data"]; - if (user != null) { - joinedUsers.assign(UserModel(id: user['user_id'], name: user['username'])); + if (data["type"] == "join") { + final user = data["data"]; + if (user != null) { + joinedUsers.assign(UserModel(id: user['user_id'], name: user['username'])); + CustomNotification.success(title: "Participan Joined", message: "${user['username']} has joined to room"); + } + } + + if (data["type"] == "leave") { + final userId = data["data"]; + CustomNotification.warning(title: "Participan Leave", message: "participan leave the room"); + joinedUsers.removeWhere((e) => e.id == userId); } }); @@ -86,4 +99,12 @@ class WaitingRoomController extends GetxController { void startQuiz() { _socketService.startQuiz(sessionCode: sessionCode.value); } + + void leaveRoom() async { + _socketService.leaveRoom(sessionId: roomData!.sessionId, userId: _userController.userData!.id); + Get.offAllNamed(AppRoutes.mainPage); + + await Future.delayed(Duration(seconds: 2)); + _socketService.dispose(); + } } diff --git a/lib/feature/waiting_room/view/waiting_room_view.dart b/lib/feature/waiting_room/view/waiting_room_view.dart index e7f66e8..66caca3 100644 --- a/lib/feature/waiting_room/view/waiting_room_view.dart +++ b/lib/feature/waiting_room/view/waiting_room_view.dart @@ -33,7 +33,13 @@ class WaitingRoomView extends GetView { GlobalButton( text: "Mulai Kuis", onPressed: controller.startQuiz, - ), + ) + else + GlobalButton( + text: "Tinggalkan Ruangan", + onPressed: controller.leaveRoom, + baseColor: const Color.fromARGB(255, 204, 14, 0), + ) ], ); }), @@ -68,6 +74,7 @@ class WaitingRoomView extends GetView { if (quiz == null) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.all(16), + width: double.infinity, decoration: BoxDecoration( color: AppColors.background, border: Border.all(color: AppColors.borderLight),