feat: adding leave page in the waiting room

This commit is contained in:
akhdanre 2025-05-12 22:41:14 +07:00
parent da6597f42b
commit 5f54ca6c8c
9 changed files with 60 additions and 16 deletions

View File

@ -11,7 +11,7 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetMaterialApp( return GetMaterialApp(
title: 'Quiz App', 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'), fallbackLocale: const Locale('en', 'US'),
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,

View File

@ -6,11 +6,13 @@ class GlobalButton extends StatelessWidget {
final VoidCallback? onPressed; final VoidCallback? onPressed;
final String text; final String text;
final ButtonType type; final ButtonType type;
final Color baseColor;
const GlobalButton({ const GlobalButton({
super.key, super.key,
required this.text, required this.text,
required this.onPressed, required this.onPressed,
this.baseColor = const Color(0xFF0052CC),
this.type = ButtonType.primary, this.type = ButtonType.primary,
}); });
@ -24,12 +26,12 @@ class GlobalButton extends StatelessWidget {
switch (type) { switch (type) {
case ButtonType.primary: case ButtonType.primary:
backgroundColor = const Color(0xFF0052CC); backgroundColor = baseColor;
foregroundColor = Colors.white; foregroundColor = Colors.white;
break; break;
case ButtonType.secondary: case ButtonType.secondary:
backgroundColor = Colors.white; backgroundColor = Colors.white;
foregroundColor = const Color(0xFF0052CC); foregroundColor = baseColor;
borderColor = const Color(0xFF0052CC); borderColor = const Color(0xFF0052CC);
break; break;
case ButtonType.disabled: case ButtonType.disabled:

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class GlobalTextField extends StatelessWidget { class GlobalTextField extends StatelessWidget {
final TextEditingController controller; final TextEditingController controller;
@ -10,6 +9,7 @@ class GlobalTextField extends StatelessWidget {
final bool obscureText; final bool obscureText;
final VoidCallback? onToggleVisibility; final VoidCallback? onToggleVisibility;
final TextInputType textInputType; final TextInputType textInputType;
final bool forceUpperCase;
const GlobalTextField( const GlobalTextField(
{super.key, {super.key,
@ -20,6 +20,7 @@ class GlobalTextField extends StatelessWidget {
this.isPassword = false, this.isPassword = false,
this.obscureText = false, this.obscureText = false,
this.onToggleVisibility, this.onToggleVisibility,
this.forceUpperCase = false,
this.textInputType = TextInputType.text}); this.textInputType = TextInputType.text});
@override @override
@ -28,7 +29,8 @@ class GlobalTextField extends StatelessWidget {
controller: controller, controller: controller,
keyboardType: textInputType, keyboardType: textInputType,
obscureText: isPassword ? obscureText : false, obscureText: isPassword ? obscureText : false,
maxLines: limitTextLine, // <-- ini tambahan dari limitTextLine maxLines: limitTextLine,
textCapitalization: forceUpperCase ? TextCapitalization.characters : TextCapitalization.none,
decoration: InputDecoration( decoration: InputDecoration(
labelText: labelText, labelText: labelText,
labelStyle: const TextStyle( labelStyle: const TextStyle(

View File

@ -58,15 +58,21 @@ class SocketService {
} }
void joinRoom({required String sessionCode, required String userId}) { void joinRoom({required String sessionCode, required String userId}) {
socket.emit('join_room', { var data = {
'session_code': sessionCode, 'session_code': sessionCode,
'user_id': userId, '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', { socket.emit('leave_room', {
'session_id': sessionId, 'session_id': sessionId,
'user_id': userId,
'username': username, 'username': username,
}); });
} }

View File

@ -27,6 +27,7 @@ class JoinRoomController extends GetxController {
return; return;
} }
_socketService.initSocketConnection(); _socketService.initSocketConnection();
_socketService.joinRoom(sessionCode: code, userId: _userController.userData!.id); _socketService.joinRoom(sessionCode: code, userId: _userController.userData!.id);
Get.toNamed(AppRoutes.waitRoomPage, arguments: WaitingRoomDTO(false, SessionResponseModel(sessionId: "", sessionCode: code))); Get.toNamed(AppRoutes.waitRoomPage, arguments: WaitingRoomDTO(false, SessionResponseModel(sessionId: "", sessionCode: code)));
} }

View File

@ -54,6 +54,7 @@ class JoinRoomView extends GetView<JoinRoomController> {
controller: controller.codeController, controller: controller.codeController,
hintText: context.tr("room_code_hint"), hintText: context.tr("room_code_hint"),
textInputType: TextInputType.text, textInputType: TextInputType.text,
forceUpperCase: true,
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
GlobalButton( GlobalButton(

View File

@ -1,4 +1,5 @@
import 'package:get/get.dart'; 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/data/services/socket_service.dart';
import 'package:quiz_app/feature/waiting_room/controller/waiting_room_controller.dart'; import 'package:quiz_app/feature/waiting_room/controller/waiting_room_controller.dart';
@ -6,6 +7,9 @@ class WaitingRoomBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
if (!Get.isRegistered<SocketService>()) Get.put(SocketService()); if (!Get.isRegistered<SocketService>()) Get.put(SocketService());
Get.lazyPut<WaitingRoomController>(() => WaitingRoomController(Get.find<SocketService>())); Get.lazyPut<WaitingRoomController>(() => WaitingRoomController(
Get.find<SocketService>(),
Get.find<UserController>(),
));
} }
} }

View File

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/app/routes/app_pages.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/dto/waiting_room_dto.dart';
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
import 'package:quiz_app/data/models/session/session_response_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 { class WaitingRoomController extends GetxController {
final SocketService _socketService; final SocketService _socketService;
WaitingRoomController(this._socketService); final UserController _userController;
WaitingRoomController(this._socketService, this._userController);
final sessionCode = ''.obs; final sessionCode = ''.obs;
final quizMeta = Rx<QuizListingModel?>(null); final quizMeta = Rx<QuizListingModel?>(null);
@ -20,6 +23,7 @@ class WaitingRoomController extends GetxController {
final quizQuestions = <Map<String, dynamic>>[].obs; final quizQuestions = <Map<String, dynamic>>[].obs;
final isQuizStarted = false.obs; final isQuizStarted = false.obs;
SessionResponseModel? roomData;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -30,10 +34,10 @@ class WaitingRoomController extends GetxController {
void _loadInitialData() { void _loadInitialData() {
final data = Get.arguments as WaitingRoomDTO; final data = Get.arguments as WaitingRoomDTO;
SessionResponseModel? roomData = data.data; roomData = data.data;
isAdmin.value = data.isAdmin; isAdmin.value = data.isAdmin;
sessionCode.value = roomData.sessionCode; sessionCode.value = roomData!.sessionCode;
quizMeta.value = QuizListingModel( quizMeta.value = QuizListingModel(
quizId: "q123", quizId: "q123",
@ -49,9 +53,18 @@ class WaitingRoomController extends GetxController {
void _registerSocketListeners() { void _registerSocketListeners() {
_socketService.roomMessages.listen((data) { _socketService.roomMessages.listen((data) {
final user = data["data"]; if (data["type"] == "join") {
if (user != null) { final user = data["data"];
joinedUsers.assign(UserModel(id: user['user_id'], name: user['username'])); 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() { void startQuiz() {
_socketService.startQuiz(sessionCode: sessionCode.value); _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();
}
} }

View File

@ -33,7 +33,13 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
GlobalButton( GlobalButton(
text: "Mulai Kuis", text: "Mulai Kuis",
onPressed: controller.startQuiz, 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<WaitingRoomController> {
if (quiz == null) return const SizedBox.shrink(); if (quiz == null) return const SizedBox.shrink();
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.background, color: AppColors.background,
border: Border.all(color: AppColors.borderLight), border: Border.all(color: AppColors.borderLight),