diff --git a/lib/data/dto/waiting_room_dto.dart b/lib/data/dto/waiting_room_dto.dart index f72541f..bad643e 100644 --- a/lib/data/dto/waiting_room_dto.dart +++ b/lib/data/dto/waiting_room_dto.dart @@ -1,8 +1,17 @@ +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'; class WaitingRoomDTO { final bool isAdmin; final SessionResponseModel data; + final SessionInfo sessionInfo; + final QuizInfo quizInfo; - WaitingRoomDTO(this.isAdmin, this.data); + WaitingRoomDTO({ + required this.isAdmin, + required this.data, + required this.sessionInfo, + required this.quizInfo, + }); } diff --git a/lib/data/models/quiz/quiz_info_model.dart b/lib/data/models/quiz/quiz_info_model.dart new file mode 100644 index 0000000..5a54afd --- /dev/null +++ b/lib/data/models/quiz/quiz_info_model.dart @@ -0,0 +1,31 @@ +class QuizInfo { + final String title; + final String description; + final int totalQuiz; + final int limitDuration; + + QuizInfo({ + required this.title, + required this.description, + required this.totalQuiz, + required this.limitDuration, + }); + + factory QuizInfo.fromJson(Map json) { + return QuizInfo( + title: json['title'], + description: json['description'], + totalQuiz: json['total_quiz'], + limitDuration: json['limit_duration'], + ); + } + + Map toJson() { + return { + 'title': title, + 'description': description, + 'question_count': totalQuiz, + 'limit_duration': limitDuration, + }; + } +} diff --git a/lib/data/models/session/session_info_model.dart b/lib/data/models/session/session_info_model.dart new file mode 100644 index 0000000..cca5d1d --- /dev/null +++ b/lib/data/models/session/session_info_model.dart @@ -0,0 +1,61 @@ +import 'package:quiz_app/data/models/user/user_model.dart'; + +class SessionInfo { + final String id; + final String sessionCode; + final String quizId; + final String hostId; + final DateTime createdAt; + final DateTime? startedAt; + final DateTime? endedAt; + final bool isActive; + final int participantLimit; + final List participants; + final int currentQuestionIndex; + + SessionInfo({ + required this.id, + required this.sessionCode, + required this.quizId, + required this.hostId, + required this.createdAt, + this.startedAt, + this.endedAt, + required this.isActive, + required this.participantLimit, + required this.participants, + required this.currentQuestionIndex, + }); + + factory SessionInfo.fromJson(Map json) { + return SessionInfo( + id: json['id'], + sessionCode: json['session_code'], + quizId: json['quiz_id'], + hostId: json['host_id'], + createdAt: DateTime.parse(json['created_at']), + startedAt: json['started_at'] != null ? DateTime.parse(json['started_at']) : null, + endedAt: json['ended_at'] != null ? DateTime.parse(json['ended_at']) : null, + isActive: json['is_active'], + participantLimit: json['participan_limit'], + participants: (json['participants'] as List?)?.map((e) => UserModel.fromJson(e as Map)).toList() ?? [], + currentQuestionIndex: json['current_question_index'], + ); + } + + Map toJson() { + return { + 'id': id, + 'session_code': sessionCode, + 'quiz_id': quizId, + 'host_id': hostId, + 'created_at': createdAt.toIso8601String(), + 'started_at': startedAt?.toIso8601String(), + 'ended_at': endedAt?.toIso8601String(), + 'is_active': isActive, + 'participant_limit': participantLimit, + 'participants': participants, + 'current_question_index': currentQuestionIndex, + }; + } +} diff --git a/lib/data/models/user/user_model.dart b/lib/data/models/user/user_model.dart index 6ece638..72769d4 100644 --- a/lib/data/models/user/user_model.dart +++ b/lib/data/models/user/user_model.dart @@ -1,11 +1,22 @@ class UserModel { final String id; - final String name; + final String username; + final String userPic; + final DateTime joinedAt; UserModel({ required this.id, - required this.name, + required this.username, + required this.userPic, + required this.joinedAt, }); + + factory UserModel.fromJson(Map json) { + return UserModel( + id: json['id'], + username: json['username'], + userPic: json['user_pic'] ?? "", + joinedAt: DateTime.parse(json['joined_at']), + ); + } } - - diff --git a/lib/feature/join_room/controller/join_room_controller.dart b/lib/feature/join_room/controller/join_room_controller.dart index d5c1b26..5577158 100644 --- a/lib/feature/join_room/controller/join_room_controller.dart +++ b/lib/feature/join_room/controller/join_room_controller.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/routes/app_pages.dart'; +import 'package:quiz_app/core/utils/logger.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/socket_service.dart'; @@ -27,9 +30,32 @@ 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))); + _socketService.errors.listen((error) { + logC.i(error); + }); + + _socketService.roomMessages.listen((data) { + if (data["type"] == "join") { + final Map dataPayload = data["data"]; + final Map sessionInfoJson = dataPayload["session_info"]; + final Map quizInfoJson = dataPayload["quiz_info"]; + + Get.toNamed( + AppRoutes.waitRoomPage, + arguments: WaitingRoomDTO( + isAdmin: false, + data: SessionResponseModel( + sessionId: sessionInfoJson["id"], + sessionCode: sessionInfoJson["session_code"], + ), + sessionInfo: SessionInfo.fromJson(sessionInfoJson), + quizInfo: QuizInfo.fromJson(quizInfoJson), + ), + ); + } + }); } @override diff --git a/lib/feature/monitor_quiz/controller/monitor_quiz_controller.dart b/lib/feature/monitor_quiz/controller/monitor_quiz_controller.dart index 53fba55..f4946d8 100644 --- a/lib/feature/monitor_quiz/controller/monitor_quiz_controller.dart +++ b/lib/feature/monitor_quiz/controller/monitor_quiz_controller.dart @@ -31,7 +31,7 @@ class MonitorQuizController extends GetxController { userList.map( (user) => ParticipantAnswerPoint( id: user.id, - name: user.name, + name: user.username, ), ), ); diff --git a/lib/feature/room_maker/controller/room_maker_controller.dart b/lib/feature/room_maker/controller/room_maker_controller.dart index 4e0e0cf..904027f 100644 --- a/lib/feature/room_maker/controller/room_maker_controller.dart +++ b/lib/feature/room_maker/controller/room_maker_controller.dart @@ -4,8 +4,11 @@ import 'package:quiz_app/app/routes/app_pages.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'; +import 'package:quiz_app/data/models/quiz/quiz_info_model.dart'; 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/quiz_service.dart'; import 'package:quiz_app/data/services/session_service.dart'; import 'package:quiz_app/data/services/socket_service.dart'; @@ -66,7 +69,26 @@ class RoomMakerController extends GetxController { _socketService.initSocketConnection(); _socketService.joinRoom(sessionCode: response.data!.sessionCode, userId: _userController.userData!.id); - Get.toNamed(AppRoutes.waitRoomPage, arguments: WaitingRoomDTO(true, response.data!)); + _socketService.roomMessages.listen((data) { + if (data["type"] == "join") { + final Map dataPayload = data["data"]; + final Map sessionInfoJson = dataPayload["session_info"]; + final Map quizInfoJson = dataPayload["quiz_info"]; + + Get.toNamed( + AppRoutes.waitRoomPage, + arguments: WaitingRoomDTO( + isAdmin: true, + data: SessionResponseModel( + sessionId: sessionInfoJson["id"], + sessionCode: sessionInfoJson["session_code"], + ), + sessionInfo: SessionInfo.fromJson(sessionInfoJson), + quizInfo: QuizInfo.fromJson(quizInfoJson), + ), + ); + } + }); } } diff --git a/lib/feature/waiting_room/controller/waiting_room_controller.dart b/lib/feature/waiting_room/controller/waiting_room_controller.dart index f483d77..6295739 100644 --- a/lib/feature/waiting_room/controller/waiting_room_controller.dart +++ b/lib/feature/waiting_room/controller/waiting_room_controller.dart @@ -5,7 +5,7 @@ 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/quiz/quiz_info_model.dart'; import 'package:quiz_app/data/models/session/session_response_model.dart'; import 'package:quiz_app/data/models/user/user_model.dart'; import 'package:quiz_app/data/services/socket_service.dart'; @@ -16,7 +16,7 @@ class WaitingRoomController extends GetxController { WaitingRoomController(this._socketService, this._userController); final sessionCode = ''.obs; - final quizMeta = Rx(null); + final quizMeta = Rx(null); final joinedUsers = [].obs; final isAdmin = true.obs; @@ -39,32 +39,44 @@ class WaitingRoomController extends GetxController { sessionCode.value = roomData!.sessionCode; - quizMeta.value = QuizListingModel( - quizId: "q123", - authorId: "a123", - authorName: "Admin", - title: "Uji Coba Kuis", - description: "Kuis untuk testing", - date: DateTime.now().toIso8601String(), - totalQuiz: 5, - duration: 900, - ); + quizMeta.value = data.quizInfo; + + joinedUsers.assignAll(data.sessionInfo.participants); } void _registerSocketListeners() { _socketService.roomMessages.listen((data) { - 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"] == "participan_join" || data["type"] == "participan_leave") { + joinedUsers.clear(); + + final dataPayload = data["data"]; + final participants = dataPayload["participants"] as List?; + + if (participants != null && participants.isNotEmpty) { + final users = participants.map((e) => UserModel.fromJson(e as Map)).toList(); + + joinedUsers.addAll(users); + + if (data["type"] == "participan_join") { + CustomNotification.success( + title: "Participant Joined", + message: data["message"] ?? "A participant has joined the room.", + ); + } else if (data["type"] == "participan_leave") { + CustomNotification.warning( + title: "Participant Left", + message: data["message"] ?? "A participant has left the 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); + CustomNotification.warning( + title: "Participant Leave", + message: "Participant left the room", + ); + // joinedUsers.removeWhere((e) => e.id == userId); } }); @@ -101,7 +113,11 @@ class WaitingRoomController extends GetxController { } void leaveRoom() async { - _socketService.leaveRoom(sessionId: roomData!.sessionId, userId: _userController.userData!.id); + _socketService.leaveRoom( + sessionId: roomData!.sessionId, + userId: _userController.userData!.id, + username: _userController.userName.value, + ); Get.offAllNamed(AppRoutes.mainPage); await Future.delayed(Duration(seconds: 2)); diff --git a/lib/feature/waiting_room/view/waiting_room_view.dart b/lib/feature/waiting_room/view/waiting_room_view.dart index 66caca3..860110a 100644 --- a/lib/feature/waiting_room/view/waiting_room_view.dart +++ b/lib/feature/waiting_room/view/waiting_room_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/component/global_button.dart'; +import 'package:quiz_app/data/models/quiz/quiz_info_model.dart'; import 'package:quiz_app/data/models/user/user_model.dart'; import 'package:quiz_app/feature/waiting_room/controller/waiting_room_controller.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -21,7 +22,7 @@ class WaitingRoomView extends GetView { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildQuizMeta(quiz), + _buildQuizMeta(quiz!), const SizedBox(height: 20), _buildSessionCode(context, session), const SizedBox(height: 20), @@ -70,8 +71,8 @@ class WaitingRoomView extends GetView { ); } - Widget _buildQuizMeta(dynamic quiz) { - if (quiz == null) return const SizedBox.shrink(); + Widget _buildQuizMeta(QuizInfo quiz) { + // if (quiz == null) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.all(16), width: double.infinity, @@ -88,7 +89,7 @@ class WaitingRoomView extends GetView { Text("Judul: ${quiz.title}"), Text("Deskripsi: ${quiz.description}"), Text("Jumlah Soal: ${quiz.totalQuiz}"), - Text("Durasi: ${quiz.duration ~/ 60} menit"), + Text("Durasi: ${quiz.limitDuration ~/ 60} menit"), ], ), ); @@ -109,9 +110,9 @@ class WaitingRoomView extends GetView { ), child: Row( children: [ - CircleAvatar(child: Text(user.name[0])), + CircleAvatar(child: Text(user.username[0])), const SizedBox(width: 12), - Text(user.name, style: const TextStyle(fontSize: 16)), + Text(user.username, style: const TextStyle(fontSize: 16)), ], ), );