fix: waiting room localization and adding room name

This commit is contained in:
akhdanre 2025-05-26 19:05:47 +07:00
parent a7f5f98cf5
commit 0283806cf3
11 changed files with 125 additions and 59 deletions

View File

@ -138,6 +138,18 @@
}, },
"get_ready": "Get Ready", "get_ready": "Get Ready",
"quiz_starting_soon" : "Quiz Starting Soon" "quiz_starting_soon": "Quiz Starting Soon",
"waiting_room": {
"title": "Waiting Room",
"participants_joined": "Participants Joined:",
"leave_room": "Leave Room",
"session_code": "Session Code:",
"copy_code": "Copy Code",
"quiz_info": "Quiz Information:",
"quiz_title": "Title",
"quiz_description": "Description",
"quiz_total_question": "Total Questions",
"quiz_duration": "Duration"
}
} }

View File

@ -122,5 +122,17 @@
}, },
"get_ready": "Bersiaplah", "get_ready": "Bersiaplah",
"quiz_starting_soon": "Kuis akan segera dimulai" "quiz_starting_soon": "Kuis akan segera dimulai",
"waiting_room": {
"title": "Ruang Tunggu",
"participants_joined": "Peserta Bergabung:",
"leave_room": "Keluar dari Ruangan",
"session_code": "Kode Sesi:",
"copy_code": "Salin Kode",
"quiz_info": "Informasi Kuis:",
"quiz_title": "Judul",
"quiz_description": "Deskripsi",
"quiz_total_question": "Total Pertanyaan",
"quiz_duration": "Durasi"
}
} }

View File

@ -124,5 +124,17 @@
}, },
"get_ready": "Bersedia", "get_ready": "Bersedia",
"quiz_starting_soon": "Kuiz akan bermula sebentar lagi" "quiz_starting_soon": "Kuiz akan bermula sebentar lagi",
"waiting_room": {
"title": "Bilik Menunggu",
"participants_joined": "Peserta Telah Sertai:",
"leave_room": "Tinggalkan Bilik",
"session_code": "Kod Sesi:",
"copy_code": "Salin Kod",
"quiz_info": "Maklumat Kuiz:",
"quiz_title": "Tajuk",
"quiz_description": "Penerangan",
"quiz_total_question": "Jumlah Soalan",
"quiz_duration": "Tempoh"
}
} }

View File

@ -0,0 +1,5 @@
extension StringCasingExtension on String {
String toTitleCase() {
return split(' ').map((word) => word.isNotEmpty ? '${word[0].toUpperCase()}${word.substring(1).toLowerCase()}' : '').join(' ');
}
}

View File

@ -3,6 +3,7 @@ import 'package:quiz_app/data/models/user/user_model.dart';
class SessionInfo { class SessionInfo {
final String id; final String id;
final String sessionCode; final String sessionCode;
final String roomName;
final String quizId; final String quizId;
final String hostId; final String hostId;
final DateTime createdAt; final DateTime createdAt;
@ -16,6 +17,7 @@ class SessionInfo {
SessionInfo({ SessionInfo({
required this.id, required this.id,
required this.sessionCode, required this.sessionCode,
required this.roomName,
required this.quizId, required this.quizId,
required this.hostId, required this.hostId,
required this.createdAt, required this.createdAt,
@ -31,6 +33,7 @@ class SessionInfo {
return SessionInfo( return SessionInfo(
id: json['id'], id: json['id'],
sessionCode: json['session_code'], sessionCode: json['session_code'],
roomName: json["room_name"],
quizId: json['quiz_id'], quizId: json['quiz_id'],
hostId: json['host_id'], hostId: json['host_id'],
createdAt: DateTime.parse(json['created_at']), createdAt: DateTime.parse(json['created_at']),

View File

@ -1,11 +1,13 @@
class SessionRequestModel { class SessionRequestModel {
final String quizId; final String quizId;
final String hostId; final String hostId;
final String roomName;
final int limitParticipan; final int limitParticipan;
SessionRequestModel({ SessionRequestModel({
required this.quizId, required this.quizId,
required this.hostId, required this.hostId,
required this.roomName,
required this.limitParticipan, required this.limitParticipan,
}); });
@ -13,6 +15,7 @@ class SessionRequestModel {
return SessionRequestModel( return SessionRequestModel(
quizId: json['quiz_id'], quizId: json['quiz_id'],
hostId: json['host_id'], hostId: json['host_id'],
roomName: json['room_name'],
limitParticipan: json['limit_participan'], limitParticipan: json['limit_participan'],
); );
} }
@ -21,6 +24,7 @@ class SessionRequestModel {
return { return {
'quiz_id': quizId, 'quiz_id': quizId,
'host_id': hostId, 'host_id': hostId,
'room_name': roomName,
'limit_participan': limitParticipan, 'limit_participan': limitParticipan,
}; };
} }

View File

@ -17,11 +17,7 @@ class SessionService extends GetxService {
Future<BaseResponseModel<SessionResponseModel>?> createSession(SessionRequestModel data) async { Future<BaseResponseModel<SessionResponseModel>?> createSession(SessionRequestModel data) async {
try { try {
final response = await _dio.post(APIEndpoint.session, data: { final response = await _dio.post(APIEndpoint.session, data: data.toJson());
'quiz_id': data.quizId,
'host_id': data.hostId,
'limit_participan': data.limitParticipan,
});
if (response.statusCode != 201) { if (response.statusCode != 201) {
return null; return null;
} }

View File

@ -33,32 +33,32 @@ class JoinRoomView extends GetView<JoinRoomController> {
children: [ children: [
const SizedBox(height: 20), const SizedBox(height: 20),
TweenAnimationBuilder<double>( // TweenAnimationBuilder<double>(
duration: const Duration(seconds: 1), // duration: const Duration(seconds: 1),
tween: Tween(begin: 0.0, end: 1.0), // tween: Tween(begin: 0.0, end: 1.0),
builder: (context, value, child) { // builder: (context, value, child) {
return Transform.scale( // return Transform.scale(
scale: value, // scale: value,
child: child, // child: child,
); // );
}, // },
child: Container( // child: Container(
padding: EdgeInsets.all(22), // padding: EdgeInsets.all(22),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: AppColors.primaryBlue.withValues(alpha: 0.05), // color: AppColors.primaryBlue.withValues(alpha: 0.05),
shape: BoxShape.circle, // shape: BoxShape.circle,
border: Border.all( // border: Border.all(
color: AppColors.primaryBlue.withValues(alpha: 0.15), // color: AppColors.primaryBlue.withValues(alpha: 0.15),
width: 2, // width: 2,
), // ),
), // ),
child: Icon( // child: Icon(
LucideIcons.trophy, // LucideIcons.trophy,
size: 70, // size: 70,
color: AppColors.primaryBlue, // color: AppColors.primaryBlue,
), // ),
), // ),
), // ),
const SizedBox(height: 30), const SizedBox(height: 30),

View File

@ -103,6 +103,7 @@ class RoomMakerController extends GetxController {
SessionRequestModel( SessionRequestModel(
quizId: quiz.quizId, quizId: quiz.quizId,
hostId: _userController.userData!.id, hostId: _userController.userData!.id,
roomName: nameTC.text,
limitParticipan: int.parse(maxPlayerTC.text), limitParticipan: int.parse(maxPlayerTC.text),
), ),
); );

View File

@ -16,6 +16,7 @@ class WaitingRoomController extends GetxController {
WaitingRoomController(this._socketService, this._userController); WaitingRoomController(this._socketService, this._userController);
final sessionCode = ''.obs; final sessionCode = ''.obs;
final roomName = "".obs;
String sessionId = ''; String sessionId = '';
final quizMeta = Rx<QuizInfo?>(null); final quizMeta = Rx<QuizInfo?>(null);
final joinedUsers = <UserModel>[].obs; final joinedUsers = <UserModel>[].obs;
@ -42,6 +43,7 @@ class WaitingRoomController extends GetxController {
sessionId = roomData!.sessionId; sessionId = roomData!.sessionId;
quizMeta.value = data.quizInfo; quizMeta.value = data.quizInfo;
roomName.value = data.sessionInfo.roomName;
joinedUsers.assignAll(data.sessionInfo.participants); joinedUsers.assignAll(data.sessionInfo.participants);
} }

View File

@ -1,5 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:quiz_app/app/const/text/string_extension.dart';
import 'package:quiz_app/app/const/text/text_style.dart';
import 'package:quiz_app/component/global_button.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/quiz/quiz_info_model.dart';
import 'package:quiz_app/data/models/user/user_model.dart'; import 'package:quiz_app/data/models/user/user_model.dart';
@ -11,7 +14,9 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
appBar: AppBar(title: const Text("Waiting Room")), appBar: AppBar(
title: Text(tr("waiting_room.title"), style: AppTextStyles.title),
),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Obx(() { child: Obx(() {
@ -22,22 +27,36 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 20),
Center(
child: Obx(() => Text(
controller.roomName.value.toTitleCase(),
style: AppTextStyles.title,
)),
),
const SizedBox(height: 20),
_buildQuizMeta(quiz!), _buildQuizMeta(quiz!),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildSessionCode(context, session), _buildSessionCode(context, session),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text("Peserta yang Bergabung:", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), Text(
tr("waiting_room.participants_joined"),
style: AppTextStyles.subtitle.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.darkText,
),
),
const SizedBox(height: 10), const SizedBox(height: 10),
Expanded(child: Obx(() => _buildUserList(users.toList()))), Expanded(child: Obx(() => _buildUserList(users.toList()))),
const SizedBox(height: 16), const SizedBox(height: 16),
if (controller.isAdmin.value) controller.isAdmin.value
GlobalButton( ? GlobalButton(
text: "Mulai Kuis", text: tr("start_quiz"),
onPressed: controller.startQuiz, onPressed: controller.startQuiz,
) )
else : GlobalButton(
GlobalButton( text: tr("waiting_room.leave_room"),
text: "Tinggalkan Ruangan",
onPressed: controller.leaveRoom, onPressed: controller.leaveRoom,
baseColor: const Color.fromARGB(255, 204, 14, 0), baseColor: const Color.fromARGB(255, 204, 14, 0),
) )
@ -52,18 +71,19 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primaryBlue.withValues(alpha: 0.05), color: AppColors.accentBlue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColors.primaryBlue), border: Border.all(color: AppColors.primaryBlue),
), ),
child: Row( child: Row(
children: [ children: [
const Text("Session Code: ", style: TextStyle(fontWeight: FontWeight.bold)), Text(tr("waiting_room.session_code"), style: AppTextStyles.statValue),
SelectableText(code, style: const TextStyle(fontSize: 16)), const SizedBox(width: 4),
SelectableText(code, style: AppTextStyles.body.copyWith(fontSize: 16)),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: const Icon(Icons.copy), icon: const Icon(Icons.copy),
tooltip: 'Salin Kode', tooltip: tr("waiting_room.copy_code"),
onPressed: () => controller.copySessionCode(context), onPressed: () => controller.copySessionCode(context),
), ),
], ],
@ -72,7 +92,6 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
} }
Widget _buildQuizMeta(QuizInfo quiz) { Widget _buildQuizMeta(QuizInfo quiz) {
// if (quiz == null) return const SizedBox.shrink();
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
width: double.infinity, width: double.infinity,
@ -84,12 +103,12 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text("Informasi Kuis:", style: TextStyle(fontWeight: FontWeight.bold)), Text(tr("waiting_room.quiz_info"), style: AppTextStyles.subtitle.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 8), const SizedBox(height: 8),
Text("Judul: ${quiz.title}"), Text("${tr("waiting_room.quiz_title")}: ${quiz.title}", style: AppTextStyles.body),
Text("Deskripsi: ${quiz.description}"), Text("${tr("waiting_room.quiz_description")}: ${quiz.description}", style: AppTextStyles.body),
Text("Jumlah Soal: ${quiz.totalQuiz}"), Text("${tr("waiting_room.quiz_total_question")}: ${quiz.totalQuiz}", style: AppTextStyles.body),
Text("Durasi: ${quiz.limitDuration ~/ 60} menit"), Text("${tr("waiting_room.quiz_duration")}: ${quiz.limitDuration ~/ 60} min", style: AppTextStyles.body),
], ],
), ),
); );
@ -110,9 +129,9 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
), ),
child: Row( child: Row(
children: [ children: [
CircleAvatar(child: Text(user.username[0])), CircleAvatar(child: Text(user.username[0].toUpperCase())),
const SizedBox(width: 12), const SizedBox(width: 12),
Text(user.username, style: const TextStyle(fontSize: 16)), Text(user.username, style: AppTextStyles.body.copyWith(fontSize: 16)),
], ],
), ),
); );