Compare commits
No commits in common. "0a138793ae11a6b968e4c594cf155b2521a32c05" and "655103247e92bfe00c50356101bbd5e58ea81af7" have entirely different histories.
0a138793ae
...
655103247e
|
@ -38,8 +38,8 @@
|
||||||
"library_title": "Quiz Library",
|
"library_title": "Quiz Library",
|
||||||
"library_description": "A collection of quiz questions created for study.",
|
"library_description": "A collection of quiz questions created for study.",
|
||||||
"no_quiz_available": "No quizzes available yet.",
|
"no_quiz_available": "No quizzes available yet.",
|
||||||
"quiz_count_label": "Quiz",
|
"quiz_count_label": "Quizzes",
|
||||||
"quiz_count_named": "{total} Quiz",
|
"quiz_count_named": "{total} Quizzes",
|
||||||
|
|
||||||
"history_title": "Quiz History",
|
"history_title": "Quiz History",
|
||||||
"history_subtitle": "Review the quizzes you've taken",
|
"history_subtitle": "Review the quizzes you've taken",
|
||||||
|
@ -135,23 +135,5 @@
|
||||||
"second": "{} s",
|
"second": "{} s",
|
||||||
"minute": "{} m",
|
"minute": "{} m",
|
||||||
"hour": "{} h"
|
"hour": "{} h"
|
||||||
},
|
}
|
||||||
|
|
||||||
"get_ready": "Get Ready",
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
|
|
||||||
"save_changes" : "Save Changes"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,22 +119,5 @@
|
||||||
"second": "{} d",
|
"second": "{} d",
|
||||||
"minute": "{} m",
|
"minute": "{} m",
|
||||||
"hour": "{} j"
|
"hour": "{} j"
|
||||||
},
|
}
|
||||||
|
|
||||||
"get_ready": "Bersiaplah",
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"save_changes": "Simpan Perubahan"
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,21 +121,5 @@
|
||||||
"second": "{} s",
|
"second": "{} s",
|
||||||
"minute": "{} m",
|
"minute": "{} m",
|
||||||
"hour": "{} j"
|
"hour": "{} j"
|
||||||
},
|
}
|
||||||
|
|
||||||
"get_ready": "Bersedia",
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"save_changes": "Simpan Perubahan"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ class MyApp extends StatelessWidget {
|
||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
title: 'Quiz App',
|
title: 'Quiz App',
|
||||||
locale: Get.locale ?? context.locale,
|
locale: Get.locale ?? context.locale,
|
||||||
fallbackLocale: const Locale('id', 'ID'),
|
fallbackLocale: const Locale('en', 'US'),
|
||||||
localizationsDelegates: context.localizationDelegates,
|
localizationsDelegates: context.localizationDelegates,
|
||||||
supportedLocales: context.supportedLocales,
|
supportedLocales: context.supportedLocales,
|
||||||
initialBinding: InitialBindings(),
|
initialBinding: InitialBindings(),
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
extension StringCasingExtension on String {
|
|
||||||
String toTitleCase() {
|
|
||||||
return split(' ').map((word) => word.isNotEmpty ? '${word[0].toUpperCase()}${word.substring(1).toLowerCase()}' : '').join(' ');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -119,88 +119,4 @@ class AppDialog {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool?> showConfirmationDialog(
|
|
||||||
BuildContext context, {
|
|
||||||
required String title,
|
|
||||||
required String message,
|
|
||||||
String cancelText = "Batal",
|
|
||||||
String confirmText = "Yakin",
|
|
||||||
Color confirmColor = AppColors.primaryBlue,
|
|
||||||
}) async {
|
|
||||||
return showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: true,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return Dialog(
|
|
||||||
backgroundColor: AppColors.background,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.darkText,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
message,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: AppColors.softGrayText,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(context, false),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: AppColors.primaryBlue,
|
|
||||||
side: const BorderSide(color: AppColors.primaryBlue),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
),
|
|
||||||
child: Text(cancelText),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: confirmColor,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
),
|
|
||||||
child: Text(confirmText),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class APIEndpoint {
|
class APIEndpoint {
|
||||||
// static const String baseUrl = "http://192.168.1.13:5000";
|
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://103.193.178.121:5000";
|
||||||
static const String api = "$baseUrl/api";
|
static const String api = "$baseUrl/api";
|
||||||
|
|
||||||
static const String login = "/login";
|
static const String login = "/login";
|
||||||
|
|
|
@ -1,34 +1,23 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class CustomFloatingLoading {
|
class CustomFloatingLoading {
|
||||||
static OverlayEntry? _overlayEntry;
|
static void showLoadingDialog(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
static void showLoading(BuildContext context) {
|
context: context,
|
||||||
if (_overlayEntry != null) return;
|
barrierDismissible: false,
|
||||||
|
barrierColor: Colors.black.withValues(alpha: 0.3),
|
||||||
_overlayEntry = OverlayEntry(
|
builder: (BuildContext context) {
|
||||||
builder: (_) => Stack(
|
return PopScope(
|
||||||
children: [
|
canPop: false,
|
||||||
ModalBarrier(
|
child: const Center(
|
||||||
dismissible: false,
|
child: CircularProgressIndicator(),
|
||||||
color: Colors.black.withValues(alpha: 0.5),
|
|
||||||
),
|
),
|
||||||
const Center(
|
);
|
||||||
child: CircularProgressIndicator(
|
},
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Overlay.of(context).insert(_overlayEntry!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hideLoading() {
|
static void hideLoadingDialog(BuildContext context) {
|
||||||
if (_overlayEntry?.mounted == true) {
|
Navigator.of(context).pop();
|
||||||
_overlayEntry?.remove();
|
|
||||||
}
|
|
||||||
_overlayEntry = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,7 @@ class UserEntity {
|
||||||
'pic_url': picUrl,
|
'pic_url': picUrl,
|
||||||
'birth_date': birthDate,
|
'birth_date': birthDate,
|
||||||
'locale': locale,
|
'locale': locale,
|
||||||
'phone': phone,
|
"create_at": createdAt,
|
||||||
"created_at": createdAt,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ 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;
|
||||||
|
@ -17,7 +16,6 @@ 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,
|
||||||
|
@ -33,7 +31,6 @@ 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']),
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,7 +13,6 @@ 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'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +21,6 @@ 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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class ConnectionService extends GetxService {
|
||||||
isConnected.value = results.any((result) => result != ConnectivityResult.none);
|
isConnected.value = results.any((result) => result != ConnectivityResult.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isHaveConnection() async {
|
Future<bool> checkConnection() async {
|
||||||
final result = await _connectivity.checkConnectivity();
|
final result = await _connectivity.checkConnectivity();
|
||||||
return !result.contains(ConnectivityResult.none);
|
return !result.contains(ConnectivityResult.none);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
|
@ -85,8 +82,7 @@ class QuizService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<List<QuizListingModel>>?> populerQuiz({int page = 1, int amount = 3}) async {
|
Future<BaseResponseModel<List<QuizListingModel>>?> populerQuiz({int page = 1, int amount = 3}) async {
|
||||||
try {
|
try {
|
||||||
Locale locale = Localizations.localeOf(Get.context!);
|
final response = await dio.get("${APIEndpoint.quizPopuler}?page=$page&limit=$amount");
|
||||||
final response = await dio.get("${APIEndpoint.quizPopuler}?page=$page&limit=$amount&lang_code=${locale.languageCode}");
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
|
@ -106,8 +102,7 @@ class QuizService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<List<QuizListingModel>>?> recommendationQuiz({int page = 1, int amount = 3, String userId = ""}) async {
|
Future<BaseResponseModel<List<QuizListingModel>>?> recommendationQuiz({int page = 1, int amount = 3, String userId = ""}) async {
|
||||||
try {
|
try {
|
||||||
Locale locale = Localizations.localeOf(Get.context!);
|
final response = await dio.get("${APIEndpoint.quizRecommendation}?page=$page&limit=$amount&user_id$userId");
|
||||||
final response = await dio.get("${APIEndpoint.quizRecommendation}?page=$page&limit=$amount&user_id=$userId&lang_code=${locale.languageCode}");
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
|
|
|
@ -17,7 +17,11 @@ 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: data.toJson());
|
final response = await _dio.post(APIEndpoint.session, data: {
|
||||||
|
'quiz_id': data.quizId,
|
||||||
|
'host_id': data.hostId,
|
||||||
|
'limit_participan': data.limitParticipan,
|
||||||
|
});
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.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/quiz_service.dart';
|
||||||
import 'package:quiz_app/feature/detail_quiz/controller/detail_quiz_controller.dart';
|
import 'package:quiz_app/feature/detail_quiz/controller/detail_quiz_controller.dart';
|
||||||
|
|
||||||
|
@ -9,11 +8,6 @@ class DetailQuizBinding extends Bindings {
|
||||||
if (!Get.isRegistered<QuizService>()) {
|
if (!Get.isRegistered<QuizService>()) {
|
||||||
Get.lazyPut<QuizService>(() => QuizService());
|
Get.lazyPut<QuizService>(() => QuizService());
|
||||||
}
|
}
|
||||||
Get.lazyPut<DetailQuizController>(
|
Get.lazyPut<DetailQuizController>(() => DetailQuizController(Get.find<QuizService>()));
|
||||||
() => DetailQuizController(
|
|
||||||
Get.find<QuizService>(),
|
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
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/helper/connection_check.dart';
|
|
||||||
import 'package:quiz_app/data/models/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
import 'package:quiz_app/data/models/quiz/library_quiz_model.dart';
|
import 'package:quiz_app/data/models/quiz/library_quiz_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/quiz_service.dart';
|
||||||
|
|
||||||
class DetailQuizController extends GetxController {
|
class DetailQuizController extends GetxController {
|
||||||
final QuizService _quizService;
|
final QuizService _quizService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
DetailQuizController(this._quizService, this._connectionService);
|
DetailQuizController(this._quizService);
|
||||||
|
|
||||||
RxBool isLoading = true.obs;
|
RxBool isLoading = true.obs;
|
||||||
|
|
||||||
QuizData? data;
|
late QuizData data;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
|
@ -24,11 +21,6 @@ class DetailQuizController extends GetxController {
|
||||||
|
|
||||||
void loadData() async {
|
void loadData() async {
|
||||||
final quizId = Get.arguments as String;
|
final quizId = Get.arguments as String;
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
isLoading.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
getQuizData(quizId);
|
getQuizData(quizId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +32,5 @@ class DetailQuizController extends GetxController {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToPlayPage() {
|
void goToPlayPage() => Get.toNamed(AppRoutes.playQuizPage, arguments: data);
|
||||||
if (!_connectionService.isCurrentlyConnected) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Get.toNamed(AppRoutes.playQuizPage, arguments: data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,76 +30,70 @@ class DetailQuizView extends GetView<DetailQuizController> {
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
child: Obx(() {
|
child: Obx(
|
||||||
if (controller.isLoading.value) {
|
() => controller.isLoading.value
|
||||||
return const Center(child: LoadingWidget());
|
? const Center(child: LoadingWidget())
|
||||||
}
|
: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Header Section
|
||||||
|
Text(
|
||||||
|
controller.data.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.darkText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
controller.data.description ?? "",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: AppColors.softGrayText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.calendar_today_rounded, size: 16, color: AppColors.softGrayText),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
controller.data.date ?? "",
|
||||||
|
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Icon(Icons.timer_rounded, size: 16, color: AppColors.softGrayText),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
'${controller.data.limitDuration ~/ 60} ${tr('minutes_suffix')}',
|
||||||
|
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
if (controller.data == null) {
|
GlobalButton(text: tr('start_quiz'), onPressed: controller.goToPlayPage),
|
||||||
return const Center(child: Text("Tidak Ditemukan"));
|
const SizedBox(height: 20),
|
||||||
}
|
|
||||||
|
|
||||||
return SingleChildScrollView(
|
const Divider(thickness: 1.2, color: AppColors.borderLight),
|
||||||
child: Column(
|
const SizedBox(height: 20),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
// Soal Section
|
||||||
// Header Section
|
ListView.builder(
|
||||||
Text(
|
shrinkWrap: true,
|
||||||
controller.data!.title,
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
style: const TextStyle(
|
itemCount: controller.data.questionListings.length,
|
||||||
fontSize: 22,
|
itemBuilder: (context, index) {
|
||||||
fontWeight: FontWeight.bold,
|
final question = controller.data.questionListings[index];
|
||||||
color: AppColors.darkText,
|
return _buildQuestionItem(question, index + 1);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
),
|
||||||
Text(
|
|
||||||
controller.data!.description ?? "",
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: AppColors.softGrayText,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.calendar_today_rounded, size: 16, color: AppColors.softGrayText),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
controller.data!.date ?? "",
|
|
||||||
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
const Icon(Icons.timer_rounded, size: 16, color: AppColors.softGrayText),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
'${controller.data!.limitDuration ~/ 60} ${tr('minutes_suffix')}',
|
|
||||||
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
GlobalButton(text: tr('start_quiz'), onPressed: controller.goToPlayPage),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
const Divider(thickness: 1.2, color: AppColors.borderLight),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Soal Section
|
|
||||||
ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: controller.data!.questionListings.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final question = controller.data!.questionListings[index];
|
|
||||||
return _buildQuestionItem(question, index + 1);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +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/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/history_service.dart';
|
import 'package:quiz_app/data/services/history_service.dart';
|
||||||
import 'package:quiz_app/feature/history/controller/history_controller.dart';
|
import 'package:quiz_app/feature/history/controller/history_controller.dart';
|
||||||
|
|
||||||
|
@ -8,12 +7,6 @@ class HistoryBinding extends Bindings {
|
||||||
@override
|
@override
|
||||||
void dependencies() {
|
void dependencies() {
|
||||||
Get.lazyPut<HistoryService>(() => HistoryService());
|
Get.lazyPut<HistoryService>(() => HistoryService());
|
||||||
Get.lazyPut(
|
Get.lazyPut(() => HistoryController(Get.find<HistoryService>(), Get.find<UserController>()));
|
||||||
() => HistoryController(
|
|
||||||
Get.find<HistoryService>(),
|
|
||||||
Get.find<UserController>(),
|
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
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/helper/connection_check.dart';
|
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/models/history/quiz_history.dart';
|
import 'package:quiz_app/data/models/history/quiz_history.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/history_service.dart';
|
import 'package:quiz_app/data/services/history_service.dart';
|
||||||
|
|
||||||
class HistoryController extends GetxController {
|
class HistoryController extends GetxController {
|
||||||
final HistoryService _historyService;
|
final HistoryService _historyService;
|
||||||
final UserController _userController;
|
final UserController _userController;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
HistoryController(
|
HistoryController(this._historyService, this._userController);
|
||||||
this._historyService,
|
|
||||||
this._userController,
|
|
||||||
this._connectionService,
|
|
||||||
);
|
|
||||||
|
|
||||||
RxBool isLoading = true.obs;
|
RxBool isLoading = true.obs;
|
||||||
|
|
||||||
|
@ -24,15 +17,10 @@ class HistoryController extends GetxController {
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
loadHistory();
|
loadDummyHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadHistory() async {
|
void loadDummyHistory() async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
historyList.value = await _historyService.getHistory(_userController.userData!.id) ?? [];
|
historyList.value = await _historyService.getHistory(_userController.userData!.id) ?? [];
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,17 +43,15 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
|
||||||
|
|
||||||
List<Widget> quizListings() {
|
List<Widget> quizListings() {
|
||||||
return controller.quizAnswer.questionListings
|
return controller.quizAnswer.questionListings
|
||||||
.asMap()
|
.map((e) => QuizItemWAComponent(
|
||||||
.entries
|
index: e.index,
|
||||||
.map((entry) => QuizItemWAComponent(
|
isCorrect: e.isCorrect,
|
||||||
index: entry.key + 1,
|
question: e.question,
|
||||||
isCorrect: entry.value.isCorrect,
|
targetAnswer: e.targetAnswer,
|
||||||
question: entry.value.question,
|
timeSpent: e.timeSpent,
|
||||||
targetAnswer: entry.value.targetAnswer,
|
type: e.type,
|
||||||
timeSpent: entry.value.timeSpent,
|
userAnswer: e.userAnswer,
|
||||||
type: entry.value.type,
|
options: e.options,
|
||||||
userAnswer: entry.value.userAnswer,
|
|
||||||
options: entry.value.options,
|
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,11 @@ class HistoryView extends GetView<HistoryController> {
|
||||||
backgroundColor: AppColors.background,
|
backgroundColor: AppColors.background,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(context.tr("history_title"), style: AppTextStyles.title.copyWith(fontSize: 24)),
|
||||||
context.tr("history_title"),
|
|
||||||
style: AppTextStyles.title.copyWith(fontSize: 24),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
context.tr("history_subtitle"),
|
context.tr("history_subtitle"),
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
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/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/quiz_service.dart';
|
import 'package:quiz_app/data/services/quiz_service.dart';
|
||||||
import 'package:quiz_app/data/services/subject_service.dart';
|
import 'package:quiz_app/data/services/subject_service.dart';
|
||||||
import 'package:quiz_app/feature/home/controller/home_controller.dart';
|
import 'package:quiz_app/feature/home/controller/home_controller.dart';
|
||||||
|
@ -12,10 +10,8 @@ class HomeBinding extends Bindings {
|
||||||
Get.lazyPut<SubjectService>(() => SubjectService());
|
Get.lazyPut<SubjectService>(() => SubjectService());
|
||||||
Get.lazyPut<HomeController>(
|
Get.lazyPut<HomeController>(
|
||||||
() => HomeController(
|
() => HomeController(
|
||||||
Get.find<UserController>(),
|
|
||||||
Get.find<QuizService>(),
|
Get.find<QuizService>(),
|
||||||
Get.find<SubjectService>(),
|
Get.find<SubjectService>(),
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.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/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/models/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.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/subject/subject_model.dart';
|
import 'package:quiz_app/data/models/subject/subject_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/quiz_service.dart';
|
||||||
import 'package:quiz_app/data/services/subject_service.dart';
|
import 'package:quiz_app/data/services/subject_service.dart';
|
||||||
import 'package:quiz_app/feature/navigation/controllers/navigation_controller.dart';
|
import 'package:quiz_app/feature/navigation/controllers/navigation_controller.dart';
|
||||||
|
|
||||||
class HomeController extends GetxController {
|
class HomeController extends GetxController {
|
||||||
final UserController _userController;
|
final UserController _userController = Get.find<UserController>();
|
||||||
|
|
||||||
final QuizService _quizService;
|
final QuizService _quizService;
|
||||||
final SubjectService _subjectService;
|
final SubjectService _subjectService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
HomeController(
|
HomeController(
|
||||||
this._userController,
|
|
||||||
this._quizService,
|
this._quizService,
|
||||||
this._subjectService,
|
this._subjectService,
|
||||||
this._connectionService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
RxInt timeStatus = 1.obs;
|
RxInt timeStatus = 1.obs;
|
||||||
|
@ -43,10 +39,6 @@ class HomeController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getRecomendationQuiz() async {
|
void _getRecomendationQuiz() async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BaseResponseModel? response = await _quizService.recommendationQuiz(userId: _userController.userData!.id);
|
BaseResponseModel? response = await _quizService.recommendationQuiz(userId: _userController.userData!.id);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
data.assignAll(response.data as List<QuizListingModel>);
|
data.assignAll(response.data as List<QuizListingModel>);
|
||||||
|
@ -54,7 +46,6 @@ class HomeController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadSubjectData() async {
|
void loadSubjectData() async {
|
||||||
if (!_connectionService.isCurrentlyConnected) return;
|
|
||||||
try {
|
try {
|
||||||
final response = await _subjectService.getSubject();
|
final response = await _subjectService.getSubject();
|
||||||
subjects.assignAll(response.data!);
|
subjects.assignAll(response.data!);
|
||||||
|
|
|
@ -5,7 +5,6 @@ class UserGretingsComponent extends StatelessWidget {
|
||||||
final String userName;
|
final String userName;
|
||||||
final String? userImage;
|
final String? userImage;
|
||||||
final int greatingStatus;
|
final int greatingStatus;
|
||||||
|
|
||||||
const UserGretingsComponent({
|
const UserGretingsComponent({
|
||||||
super.key,
|
super.key,
|
||||||
required this.userName,
|
required this.userName,
|
||||||
|
|
|
@ -1,6 +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/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/data/services/socket_service.dart';
|
||||||
import 'package:quiz_app/feature/join_room/controller/join_room_controller.dart';
|
import 'package:quiz_app/feature/join_room/controller/join_room_controller.dart';
|
||||||
|
|
||||||
|
@ -9,12 +8,6 @@ class JoinRoomBinding extends Bindings {
|
||||||
void dependencies() {
|
void dependencies() {
|
||||||
Get.put(SocketService());
|
Get.put(SocketService());
|
||||||
|
|
||||||
Get.lazyPut(
|
Get.lazyPut(() => JoinRoomController(Get.find<SocketService>(), Get.find<UserController>()));
|
||||||
() => JoinRoomController(
|
|
||||||
Get.find<SocketService>(),
|
|
||||||
Get.find<UserController>(),
|
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,23 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/helper/connection_check.dart';
|
|
||||||
import 'package:quiz_app/core/utils/custom_floating_loading.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/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_info_model.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_info_model.dart';
|
||||||
import 'package:quiz_app/data/models/session/session_response_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';
|
import 'package:quiz_app/data/services/socket_service.dart';
|
||||||
|
|
||||||
class JoinRoomController extends GetxController {
|
class JoinRoomController extends GetxController {
|
||||||
final SocketService _socketService;
|
final SocketService _socketService;
|
||||||
final UserController _userController;
|
final UserController _userController;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
JoinRoomController(
|
JoinRoomController(this._socketService, this._userController);
|
||||||
this._socketService,
|
|
||||||
this._userController,
|
|
||||||
this._connectionService,
|
|
||||||
);
|
|
||||||
|
|
||||||
final TextEditingController codeController = TextEditingController();
|
final TextEditingController codeController = TextEditingController();
|
||||||
RxBool isLoading = false.obs;
|
|
||||||
|
|
||||||
void joinRoom(BuildContext context) {
|
void joinRoom() {
|
||||||
if (!_connectionService.isCurrentlyConnected) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final code = codeController.text.trim();
|
final code = codeController.text.trim();
|
||||||
|
|
||||||
if (code.isEmpty) {
|
if (code.isEmpty) {
|
||||||
|
@ -42,15 +29,18 @@ class JoinRoomController extends GetxController {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
CustomFloatingLoading.showLoadingDialog(Get.context!);
|
||||||
isLoading.value = true;
|
|
||||||
_socketService.initSocketConnection();
|
_socketService.initSocketConnection();
|
||||||
|
|
||||||
_socketService.joinRoom(sessionCode: code, userId: _userController.userData!.id);
|
_socketService.joinRoom(sessionCode: code, userId: _userController.userData!.id);
|
||||||
_socketService.errors.listen((error) {
|
_socketService.errors.listen((error) {
|
||||||
CustomNotification.error(title: "not found", message: "Ruangan tidak ditemukan");
|
Get.snackbar(
|
||||||
CustomFloatingLoading.hideLoading();
|
"not found",
|
||||||
isLoading.value = false;
|
"Ruangan tidak ditemukan",
|
||||||
|
backgroundColor: Get.theme.colorScheme.error.withValues(alpha: 0.9),
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
});
|
});
|
||||||
|
|
||||||
_socketService.roomMessages.listen((data) {
|
_socketService.roomMessages.listen((data) {
|
||||||
|
@ -59,8 +49,7 @@ class JoinRoomController extends GetxController {
|
||||||
final Map<String, dynamic> sessionInfoJson = dataPayload["session_info"];
|
final Map<String, dynamic> sessionInfoJson = dataPayload["session_info"];
|
||||||
final Map<String, dynamic> quizInfoJson = dataPayload["quiz_info"];
|
final Map<String, dynamic> quizInfoJson = dataPayload["quiz_info"];
|
||||||
|
|
||||||
CustomFloatingLoading.hideLoading();
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
isLoading.value = false;
|
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
AppRoutes.waitRoomPage,
|
AppRoutes.waitRoomPage,
|
||||||
arguments: WaitingRoomDTO(
|
arguments: WaitingRoomDTO(
|
||||||
|
@ -77,10 +66,6 @@ class JoinRoomController extends GetxController {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoBack() {
|
|
||||||
if (!isLoading.value) Get.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
codeController.dispose();
|
codeController.dispose();
|
||||||
|
|
|
@ -12,189 +12,185 @@ class JoinRoomView extends GetView<JoinRoomController> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return Scaffold(
|
||||||
canPop: false,
|
backgroundColor: Colors.white,
|
||||||
onPopInvokedWithResult: (didPop, result) => controller.onGoBack(),
|
extendBodyBehindAppBar: true,
|
||||||
child: Scaffold(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.transparent,
|
||||||
extendBodyBehindAppBar: true,
|
elevation: 0,
|
||||||
appBar: AppBar(
|
leading: IconButton(
|
||||||
backgroundColor: Colors.transparent,
|
icon: const Icon(LucideIcons.arrowLeft, color: Colors.black87),
|
||||||
elevation: 0,
|
onPressed: () => Get.back(),
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(LucideIcons.arrowLeft, color: Colors.black87),
|
|
||||||
onPressed: () => Get.back(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
body: Container(
|
),
|
||||||
color: Colors.white,
|
body: Container(
|
||||||
child: SafeArea(
|
color: Colors.white,
|
||||||
child: SingleChildScrollView(
|
child: SafeArea(
|
||||||
padding: const EdgeInsets.all(24),
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(24),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const SizedBox(height: 20),
|
children: [
|
||||||
|
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),
|
||||||
|
|
||||||
TweenAnimationBuilder<double>(
|
TweenAnimationBuilder<double>(
|
||||||
duration: const Duration(milliseconds: 800),
|
duration: const Duration(milliseconds: 800),
|
||||||
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 Opacity(
|
return Opacity(
|
||||||
opacity: value,
|
opacity: value,
|
||||||
child: Transform.translate(
|
child: Transform.translate(
|
||||||
offset: Offset(0, 20 * (1 - value)),
|
offset: Offset(0, 20 * (1 - value)),
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
child: Text(
|
||||||
|
context.tr("ready_to_compete"),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Animated Subtitle
|
||||||
|
TweenAnimationBuilder<double>(
|
||||||
|
duration: const Duration(milliseconds: 800),
|
||||||
|
tween: Tween(begin: 0.0, end: 1.0),
|
||||||
|
builder: (context, value, child) {
|
||||||
|
return Opacity(
|
||||||
|
opacity: value,
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: Offset(0, 20 * (1 - value)),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.tr("ready_to_compete"),
|
context.tr("enter_code_to_join"),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 28,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
color: Colors.black54,
|
||||||
color: Colors.black87,
|
height: 1.4,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 40),
|
||||||
|
|
||||||
// Animated Subtitle
|
TweenAnimationBuilder<double>(
|
||||||
TweenAnimationBuilder<double>(
|
duration: const Duration(milliseconds: 1000),
|
||||||
duration: const Duration(milliseconds: 800),
|
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 Opacity(
|
||||||
return Opacity(
|
opacity: value,
|
||||||
opacity: value,
|
child: Transform.translate(
|
||||||
child: Transform.translate(
|
offset: Offset(0, 30 * (1 - value)),
|
||||||
offset: Offset(0, 20 * (1 - value)),
|
child: child,
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
child: Text(
|
|
||||||
context.tr("enter_code_to_join"),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black54,
|
|
||||||
height: 1.4,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 30),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey.withValues(alpha: 0.08),
|
||||||
|
blurRadius: 15,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
const SizedBox(height: 40),
|
children: [
|
||||||
|
Row(
|
||||||
TweenAnimationBuilder<double>(
|
children: [
|
||||||
duration: const Duration(milliseconds: 1000),
|
Icon(
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
LucideIcons.keySquare,
|
||||||
builder: (context, value, child) {
|
color: AppColors.primaryBlue,
|
||||||
return Opacity(
|
size: 24,
|
||||||
opacity: value,
|
),
|
||||||
child: Transform.translate(
|
const SizedBox(width: 12),
|
||||||
offset: Offset(0, 30 * (1 - value)),
|
Text(
|
||||||
child: child,
|
context.tr("enter_room_code"),
|
||||||
),
|
style: const TextStyle(
|
||||||
);
|
fontSize: 20,
|
||||||
},
|
fontWeight: FontWeight.w600,
|
||||||
child: Container(
|
color: Colors.black87,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 30),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.grey.withValues(alpha: 0.08),
|
|
||||||
blurRadius: 15,
|
|
||||||
offset: const Offset(0, 5),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LucideIcons.keySquare,
|
|
||||||
color: AppColors.primaryBlue,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
|
||||||
context.tr("enter_room_code"),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
border: Border.all(
|
|
||||||
color: Colors.grey.shade200,
|
|
||||||
width: 1,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: GlobalTextField(
|
],
|
||||||
controller: controller.codeController,
|
),
|
||||||
hintText: context.tr("room_code_hint"),
|
const SizedBox(height: 25),
|
||||||
textInputType: TextInputType.text,
|
Container(
|
||||||
forceUpperCase: true,
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.grey.shade200,
|
||||||
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
child: GlobalTextField(
|
||||||
GlobalButton(
|
controller: controller.codeController,
|
||||||
text: context.tr("join_quiz_now"),
|
hintText: context.tr("room_code_hint"),
|
||||||
onPressed: () => controller.joinRoom(context),
|
textInputType: TextInputType.text,
|
||||||
|
forceUpperCase: true,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(height: 30),
|
||||||
|
GlobalButton(
|
||||||
|
text: context.tr("join_quiz_now"),
|
||||||
|
onPressed: controller.joinRoom,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +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/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/quiz_service.dart';
|
||||||
import 'package:quiz_app/feature/library/controller/library_controller.dart';
|
import 'package:quiz_app/feature/library/controller/library_controller.dart';
|
||||||
|
|
||||||
|
@ -10,10 +9,6 @@ class LibraryBinding extends Bindings {
|
||||||
if (!Get.isRegistered<QuizService>()) {
|
if (!Get.isRegistered<QuizService>()) {
|
||||||
Get.lazyPut<QuizService>(() => QuizService());
|
Get.lazyPut<QuizService>(() => QuizService());
|
||||||
}
|
}
|
||||||
Get.lazyPut<LibraryController>(() => LibraryController(
|
Get.lazyPut<LibraryController>(() => LibraryController(Get.find<QuizService>(), Get.find<UserController>()));
|
||||||
Get.find<QuizService>(),
|
|
||||||
Get.find<UserController>(),
|
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
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/helper/connection_check.dart';
|
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/models/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.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/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/quiz_service.dart';
|
import 'package:quiz_app/data/services/quiz_service.dart';
|
||||||
|
|
||||||
class LibraryController extends GetxController {
|
class LibraryController extends GetxController {
|
||||||
final QuizService _quizService;
|
|
||||||
final UserController _userController;
|
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
LibraryController(
|
|
||||||
this._quizService,
|
|
||||||
this._userController,
|
|
||||||
this._connectionService,
|
|
||||||
);
|
|
||||||
|
|
||||||
RxList<QuizListingModel> quizs = <QuizListingModel>[].obs;
|
RxList<QuizListingModel> quizs = <QuizListingModel>[].obs;
|
||||||
RxBool isLoading = true.obs;
|
RxBool isLoading = true.obs;
|
||||||
RxString emptyMessage = "".obs;
|
RxString emptyMessage = "".obs;
|
||||||
|
|
||||||
|
final QuizService _quizService;
|
||||||
|
final UserController _userController;
|
||||||
|
LibraryController(this._quizService, this._userController);
|
||||||
|
|
||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -30,10 +23,6 @@ class LibraryController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadUserQuiz() async {
|
void loadUserQuiz() async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
BaseResponseModel<List<QuizListingModel>>? response = await _quizService.userQuiz(_userController.userData!.id, currentPage);
|
BaseResponseModel<List<QuizListingModel>>? response = await _quizService.userQuiz(_userController.userData!.id, currentPage);
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
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:easy_localization/easy_localization.dart';
|
||||||
import 'package:quiz_app/app/const/text/text_style.dart';
|
|
||||||
import 'package:quiz_app/component/widget/container_skeleton_widget.dart';
|
import 'package:quiz_app/component/widget/container_skeleton_widget.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/feature/library/controller/library_controller.dart';
|
import 'package:quiz_app/feature/library/controller/library_controller.dart';
|
||||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
|
||||||
|
|
||||||
class LibraryView extends GetView<LibraryController> {
|
class LibraryView extends GetView<LibraryController> {
|
||||||
const LibraryView({super.key});
|
const LibraryView({super.key});
|
||||||
|
@ -13,7 +11,7 @@ class LibraryView extends GetView<LibraryController> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.background2,
|
backgroundColor: const Color(0xFFF9FAFB),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
@ -22,12 +20,19 @@ class LibraryView extends GetView<LibraryController> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
context.tr('library_title'),
|
context.tr('library_title'),
|
||||||
style: AppTextStyles.title.copyWith(fontSize: 24),
|
style: const TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
context.tr('library_description'),
|
context.tr('library_description'),
|
||||||
style: AppTextStyles.subtitle,
|
style: const TextStyle(
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -45,7 +50,7 @@ class LibraryView extends GetView<LibraryController> {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
context.tr('no_quiz_available'),
|
context.tr('no_quiz_available'),
|
||||||
style: AppTextStyles.caption,
|
style: const TextStyle(color: Colors.grey, fontSize: 14),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +79,7 @@ class LibraryView extends GetView<LibraryController> {
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.background,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
@ -90,7 +95,7 @@ class LibraryView extends GetView<LibraryController> {
|
||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.primaryBlue,
|
color: const Color(0xFF2563EB),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: const Icon(Icons.menu_book_rounded, color: Colors.white),
|
child: const Icon(Icons.menu_book_rounded, color: Colors.white),
|
||||||
|
@ -102,42 +107,46 @@ class LibraryView extends GetView<LibraryController> {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
quiz.title,
|
quiz.title,
|
||||||
style: AppTextStyles.body.copyWith(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
color: Colors.black,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
quiz.description,
|
quiz.description,
|
||||||
style: AppTextStyles.caption,
|
style: const TextStyle(
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 12,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.calendar_today_rounded, size: 14, color: AppColors.softGrayText),
|
const Icon(Icons.calendar_today_rounded, size: 14, color: Colors.grey),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
controller.formatDate(quiz.date),
|
controller.formatDate(quiz.date),
|
||||||
style: AppTextStyles.dateTime,
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
const Icon(Icons.list, size: 14, color: AppColors.softGrayText),
|
const Icon(Icons.list, size: 14, color: Colors.grey),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
context.tr('quiz_count_named', namedArgs: {'total': quiz.totalQuiz.toString()}),
|
context.tr('quiz_count_named', namedArgs: {'total': quiz.totalQuiz.toString()}),
|
||||||
style: AppTextStyles.dateTime,
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
const Icon(Icons.access_time, size: 14, color: AppColors.softGrayText),
|
const Icon(Icons.access_time, size: 14, color: Colors.grey),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
controller.formatDuration(quiz.duration),
|
controller.formatDuration(quiz.duration),
|
||||||
style: AppTextStyles.dateTime,
|
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,6 @@ 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/component/global_button.dart';
|
import 'package:quiz_app/component/global_button.dart';
|
||||||
import 'package:quiz_app/core/helper/connection_check.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/core/utils/custom_notification.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
|
@ -37,15 +36,13 @@ class LoginController extends GetxController {
|
||||||
final RxBool isPasswordHidden = true.obs;
|
final RxBool isPasswordHidden = true.obs;
|
||||||
final RxBool isLoading = false.obs;
|
final RxBool isLoading = false.obs;
|
||||||
|
|
||||||
late Worker _connectionWorker;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
emailController.addListener(validateFields);
|
emailController.addListener(validateFields);
|
||||||
passwordController.addListener(validateFields);
|
passwordController.addListener(validateFields);
|
||||||
|
|
||||||
_connectionWorker = ever(_connectionService.isConnected, (value) {
|
ever(_connectionService.isConnected, (value) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
ConnectionNotification.noInternedConnection();
|
ConnectionNotification.noInternedConnection();
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,7 +87,6 @@ class LoginController extends GetxController {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
|
||||||
|
|
||||||
final LoginResponseModel response = await _authService.loginWithEmail(
|
final LoginResponseModel response = await _authService.loginWithEmail(
|
||||||
LoginRequestModel(email: email, password: password),
|
LoginRequestModel(email: email, password: password),
|
||||||
|
@ -101,14 +97,13 @@ class LoginController extends GetxController {
|
||||||
await _userStorageService.saveUser(userEntity);
|
await _userStorageService.saveUser(userEntity);
|
||||||
_userController.setUserFromEntity(userEntity);
|
_userController.setUserFromEntity(userEntity);
|
||||||
_userStorageService.isLogged = true;
|
_userStorageService.isLogged = true;
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
Get.offAllNamed(AppRoutes.mainPage);
|
Get.offAllNamed(AppRoutes.mainPage);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
logC.e(e, stackTrace: stackTrace);
|
logC.e(e, stackTrace: stackTrace);
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
CustomNotification.error(title: "Gagal", message: "Periksa kembali email dan kata sandi Anda");
|
CustomNotification.error(title: "Gagal", message: "Periksa kembali email dan kata sandi Anda");
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,23 +113,15 @@ class LoginController extends GetxController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
|
||||||
isLoading.value = true;
|
|
||||||
final user = await _googleAuthService.signIn();
|
final user = await _googleAuthService.signIn();
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
Get.snackbar("Kesalahan", "Masuk dengan Google dibatalkan");
|
Get.snackbar("Kesalahan", "Masuk dengan Google dibatalkan");
|
||||||
|
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final idToken = await user.authentication.then((auth) => auth.idToken);
|
final idToken = await user.authentication.then((auth) => auth.idToken);
|
||||||
if (idToken == null || idToken.isEmpty) {
|
if (idToken == null || idToken.isEmpty) {
|
||||||
Get.snackbar("Kesalahan", "Tidak menerima ID Token dari Google");
|
Get.snackbar("Kesalahan", "Tidak menerima ID Token dari Google");
|
||||||
|
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,25 +131,18 @@ class LoginController extends GetxController {
|
||||||
await _userStorageService.saveUser(userEntity);
|
await _userStorageService.saveUser(userEntity);
|
||||||
_userController.setUserFromEntity(userEntity);
|
_userController.setUserFromEntity(userEntity);
|
||||||
_userStorageService.isLogged = true;
|
_userStorageService.isLogged = true;
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
Get.offAllNamed(AppRoutes.mainPage);
|
Get.offAllNamed(AppRoutes.mainPage);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
logC.e("Google Sign-In Error: $e", stackTrace: stackTrace);
|
logC.e("Google Sign-In Error: $e", stackTrace: stackTrace);
|
||||||
Get.snackbar("Error", "Google sign-in error");
|
Get.snackbar("Error", "Google sign-in error");
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoBack() {
|
|
||||||
if (!isLoading.value) Get.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void goToRegsPage() => Get.toNamed(AppRoutes.registerPage);
|
void goToRegsPage() => Get.toNamed(AppRoutes.registerPage);
|
||||||
|
|
||||||
UserEntity _convertLoginResponseToUserEntity(LoginResponseModel response) {
|
UserEntity _convertLoginResponseToUserEntity(LoginResponseModel response) {
|
||||||
logC.i("user data ${response.toJson()}");
|
logC.i("user id : ${response.id}");
|
||||||
return UserEntity(
|
return UserEntity(
|
||||||
id: response.id ?? '',
|
id: response.id ?? '',
|
||||||
name: response.name,
|
name: response.name,
|
||||||
|
@ -174,10 +154,4 @@ class LoginController extends GetxController {
|
||||||
phone: response.phone,
|
phone: response.phone,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void onClose() {
|
|
||||||
_connectionWorker.dispose();
|
|
||||||
super.onClose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,74 +15,70 @@ class LoginView extends GetView<LoginController> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return Scaffold(
|
||||||
canPop: false,
|
backgroundColor: AppColors.background,
|
||||||
onPopInvokedWithResult: (didPop, result) => controller.onGoBack(),
|
body: SafeArea(
|
||||||
child: Scaffold(
|
child: Padding(
|
||||||
backgroundColor: AppColors.background,
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||||
body: SafeArea(
|
child: ListView(
|
||||||
child: Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
const SizedBox(height: 40),
|
||||||
child: ListView(
|
const AppName(),
|
||||||
children: [
|
const SizedBox(height: 40),
|
||||||
const SizedBox(height: 40),
|
LabelTextField(
|
||||||
const AppName(),
|
label: context.tr("log_in"),
|
||||||
const SizedBox(height: 40),
|
fontSize: 28,
|
||||||
LabelTextField(
|
fontWeight: FontWeight.bold,
|
||||||
label: context.tr("log_in"),
|
color: Color(0xFF172B4D),
|
||||||
fontSize: 28,
|
),
|
||||||
fontWeight: FontWeight.bold,
|
const SizedBox(height: 24),
|
||||||
color: Color(0xFF172B4D),
|
LabelTextField(
|
||||||
|
label: context.tr("email"),
|
||||||
|
color: Color(0xFF6B778C),
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
GlobalTextField(
|
||||||
|
controller: controller.emailController,
|
||||||
|
hintText: context.tr("enter_your_email"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
LabelTextField(
|
||||||
|
label: context.tr("password"),
|
||||||
|
color: Color(0xFF6B778C),
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Obx(
|
||||||
|
() => GlobalTextField(
|
||||||
|
controller: controller.passwordController,
|
||||||
|
isPassword: true,
|
||||||
|
obscureText: controller.isPasswordHidden.value,
|
||||||
|
onToggleVisibility: controller.togglePasswordVisibility,
|
||||||
|
hintText: context.tr("enter_your_password"),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
),
|
||||||
LabelTextField(
|
const SizedBox(height: 32),
|
||||||
label: context.tr("email"),
|
Obx(() => GlobalButton(
|
||||||
color: Color(0xFF6B778C),
|
onPressed: controller.loginWithEmail,
|
||||||
fontSize: 14,
|
text: context.tr("sign_in"),
|
||||||
),
|
type: controller.isButtonEnabled.value,
|
||||||
const SizedBox(height: 6),
|
)),
|
||||||
GlobalTextField(
|
const SizedBox(height: 24),
|
||||||
controller: controller.emailController,
|
LabelTextField(
|
||||||
hintText: context.tr("enter_your_email"),
|
label: context.tr("or"),
|
||||||
),
|
alignment: Alignment.center,
|
||||||
const SizedBox(height: 20),
|
color: Color(0xFF6B778C),
|
||||||
LabelTextField(
|
),
|
||||||
label: context.tr("password"),
|
const SizedBox(height: 24),
|
||||||
color: Color(0xFF6B778C),
|
GoogleButton(
|
||||||
fontSize: 14,
|
onPress: controller.loginWithGoogle,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 32),
|
||||||
Obx(
|
RegisterTextButton(
|
||||||
() => GlobalTextField(
|
onTap: controller.goToRegsPage,
|
||||||
controller: controller.passwordController,
|
),
|
||||||
isPassword: true,
|
],
|
||||||
obscureText: controller.isPasswordHidden.value,
|
|
||||||
onToggleVisibility: controller.togglePasswordVisibility,
|
|
||||||
hintText: context.tr("enter_your_password"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Obx(() => GlobalButton(
|
|
||||||
onPressed: controller.loginWithEmail,
|
|
||||||
text: context.tr("sign_in"),
|
|
||||||
type: controller.isButtonEnabled.value,
|
|
||||||
)),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
LabelTextField(
|
|
||||||
label: context.tr("or"),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
color: Color(0xFF6B778C),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
GoogleButton(
|
|
||||||
onPress: controller.loginWithGoogle,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
RegisterTextButton(
|
|
||||||
onTap: controller.goToRegsPage,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// feature/navbar/binding/navbar_binding.dart
|
// feature/navbar/binding/navbar_binding.dart
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/feature/navigation/controllers/navigation_controller.dart';
|
import 'package:quiz_app/feature/navigation/controllers/navigation_controller.dart';
|
||||||
|
|
||||||
class NavbarBinding extends Bindings {
|
class NavbarBinding extends Bindings {
|
||||||
@override
|
@override
|
||||||
void dependencies() {
|
void dependencies() {
|
||||||
Get.lazyPut<NavigationController>(() => NavigationController(Get.find<ConnectionService>()));
|
Get.lazyPut<NavigationController>(() => NavigationController());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/core/helper/connection_check.dart';
|
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
|
|
||||||
class NavigationController extends GetxController {
|
class NavigationController extends GetxController {
|
||||||
RxInt selectedIndex = 0.obs;
|
RxInt selectedIndex = 0.obs;
|
||||||
|
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
NavigationController(this._connectionService);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
@ -18,18 +12,6 @@ class NavigationController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void onReady() {
|
|
||||||
ever(_connectionService.isConnected, (value) {
|
|
||||||
if (!value) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
} else {
|
|
||||||
ConnectionNotification.internetConnected();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
super.onReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
void changePage(int page) {
|
void changePage(int page) {
|
||||||
selectedIndex.value = page;
|
selectedIndex.value = page;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +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/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/google_auth_service.dart';
|
import 'package:quiz_app/data/services/google_auth_service.dart';
|
||||||
import 'package:quiz_app/data/services/user_service.dart';
|
import 'package:quiz_app/data/services/user_service.dart';
|
||||||
import 'package:quiz_app/data/services/user_storage_service.dart';
|
import 'package:quiz_app/data/services/user_storage_service.dart';
|
||||||
|
@ -16,7 +15,6 @@ class ProfileBinding extends Bindings {
|
||||||
Get.find<UserStorageService>(),
|
Get.find<UserStorageService>(),
|
||||||
Get.find<GoogleAuthService>(),
|
Get.find<GoogleAuthService>(),
|
||||||
Get.find<UserService>(),
|
Get.find<UserService>(),
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +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/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/user_service.dart';
|
import 'package:quiz_app/data/services/user_service.dart';
|
||||||
import 'package:quiz_app/data/services/user_storage_service.dart';
|
import 'package:quiz_app/data/services/user_storage_service.dart';
|
||||||
import 'package:quiz_app/feature/profile/controller/update_profile_controller.dart';
|
import 'package:quiz_app/feature/profile/controller/update_profile_controller.dart';
|
||||||
|
@ -13,7 +12,6 @@ class UpdateProfileBinding extends Bindings {
|
||||||
Get.find<UserService>(),
|
Get.find<UserService>(),
|
||||||
Get.find<UserController>(),
|
Get.find<UserController>(),
|
||||||
Get.find<UserStorageService>(),
|
Get.find<UserStorageService>(),
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,10 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/component/notification/pop_up_confirmation.dart';
|
|
||||||
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/models/user/user_stat_model.dart';
|
import 'package:quiz_app/data/models/user/user_stat_model.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/google_auth_service.dart';
|
import 'package:quiz_app/data/services/google_auth_service.dart';
|
||||||
import 'package:quiz_app/data/services/user_service.dart';
|
import 'package:quiz_app/data/services/user_service.dart';
|
||||||
import 'package:quiz_app/data/services/user_storage_service.dart';
|
import 'package:quiz_app/data/services/user_storage_service.dart';
|
||||||
|
@ -19,14 +17,12 @@ class ProfileController extends GetxController {
|
||||||
final UserStorageService _userStorageService;
|
final UserStorageService _userStorageService;
|
||||||
final GoogleAuthService _googleAuthService;
|
final GoogleAuthService _googleAuthService;
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
ProfileController(
|
ProfileController(
|
||||||
this._userController,
|
this._userController,
|
||||||
this._userStorageService,
|
this._userStorageService,
|
||||||
this._googleAuthService,
|
this._googleAuthService,
|
||||||
this._userService,
|
this._userService,
|
||||||
this._connectionService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// User basic info
|
// User basic info
|
||||||
|
@ -77,9 +73,6 @@ class ProfileController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadUserStat() async {
|
void loadUserStat() async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
final result = await _userService.getUserStat(_userController.userData!.id);
|
final result = await _userService.getUserStat(_userController.userData!.id);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
@ -90,34 +83,21 @@ class ProfileController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logout(BuildContext context) async {
|
void logout() async {
|
||||||
final confirm = await AppDialog.showConfirmationDialog(
|
try {
|
||||||
context,
|
await _googleAuthService.signOut();
|
||||||
title: "Keluar dari akun?",
|
await _userStorageService.clearUser();
|
||||||
message: "Apakah Anda yakin ingin logout dari akun ini?",
|
_userController.clearUser();
|
||||||
confirmText: "Logout",
|
_userStorageService.isLogged = false;
|
||||||
);
|
Get.offAllNamed(AppRoutes.loginPage);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
if (confirm == true) {
|
logC.e("Google Sign-Out Error: $e", stackTrace: stackTrace);
|
||||||
try {
|
Get.snackbar("Error", "Gagal logout dari Google");
|
||||||
await _googleAuthService.signOut();
|
|
||||||
await _userStorageService.clearUser();
|
|
||||||
_userController.clearUser();
|
|
||||||
_userStorageService.isLogged = false;
|
|
||||||
Get.offAllNamed(AppRoutes.loginPage);
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
logC.e("Google Sign-Out Error: $e", stackTrace: stackTrace);
|
|
||||||
Get.snackbar("Error", "Gagal logout dari Google");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editProfile() async {
|
void editProfile() {
|
||||||
final resultUpdate = await Get.toNamed(AppRoutes.updateProfilePage);
|
Get.toNamed(AppRoutes.updateProfilePage);
|
||||||
|
|
||||||
if (resultUpdate == true) {
|
|
||||||
loadUserProfileData();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeLanguage(BuildContext context, String languageCode, String countryCode) async {
|
void changeLanguage(BuildContext context, String languageCode, String countryCode) async {
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.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_floating_loading.dart';
|
||||||
import 'package:quiz_app/core/utils/custom_notification.dart';
|
import 'package:quiz_app/core/utils/custom_notification.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/entity/user/user_entity.dart';
|
import 'package:quiz_app/data/entity/user/user_entity.dart';
|
||||||
import 'package:quiz_app/data/services/connection_service.dart';
|
|
||||||
import 'package:quiz_app/data/services/user_service.dart';
|
import 'package:quiz_app/data/services/user_service.dart';
|
||||||
import 'package:quiz_app/data/services/user_storage_service.dart';
|
import 'package:quiz_app/data/services/user_storage_service.dart';
|
||||||
|
|
||||||
|
@ -14,13 +11,11 @@ class UpdateProfileController extends GetxController {
|
||||||
final UserController _userController;
|
final UserController _userController;
|
||||||
final UserStorageService _userStorageService;
|
final UserStorageService _userStorageService;
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
UpdateProfileController(
|
UpdateProfileController(
|
||||||
this._userService,
|
this._userService,
|
||||||
this._userController,
|
this._userController,
|
||||||
this._userStorageService,
|
this._userStorageService,
|
||||||
this._connectionService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final nameController = TextEditingController();
|
final nameController = TextEditingController();
|
||||||
|
@ -29,8 +24,6 @@ class UpdateProfileController extends GetxController {
|
||||||
|
|
||||||
var selectedLocale = 'en-US'.obs;
|
var selectedLocale = 'en-US'.obs;
|
||||||
|
|
||||||
RxBool isLoading = false.obs;
|
|
||||||
|
|
||||||
final Map<String, String> localeMap = {
|
final Map<String, String> localeMap = {
|
||||||
'English': 'en-US',
|
'English': 'en-US',
|
||||||
'Indonesian': 'id-ID',
|
'Indonesian': 'id-ID',
|
||||||
|
@ -51,86 +44,65 @@ class UpdateProfileController extends GetxController {
|
||||||
final name = nameController.text.trim();
|
final name = nameController.text.trim();
|
||||||
final phone = phoneController.text.trim();
|
final phone = phoneController.text.trim();
|
||||||
final birthDate = birthDateController.text.trim();
|
final birthDate = birthDateController.text.trim();
|
||||||
|
print(birthDate);
|
||||||
|
|
||||||
if (name.isEmpty || phone.isEmpty || birthDate.isEmpty) {
|
if (name.isEmpty || phone.isEmpty || birthDate.isEmpty) {
|
||||||
CustomNotification.error(
|
Get.snackbar('Validation Error', 'All fields must be filled.', snackPosition: SnackPosition.TOP);
|
||||||
title: 'Validation Error',
|
|
||||||
message: 'All fields must be filled.',
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isValidDateFormat(birthDate)) {
|
if (!_isValidDateFormat(birthDate)) {
|
||||||
CustomNotification.error(
|
Get.snackbar('Validation Error', 'birth date must valid.', snackPosition: SnackPosition.TOP);
|
||||||
title: 'Validation Error',
|
|
||||||
message: 'birth date must valid.',
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveProfile() async {
|
Future<void> saveProfile() async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_validateInputs()) return;
|
if (!_validateInputs()) return;
|
||||||
|
|
||||||
try {
|
CustomFloatingLoading.showLoadingDialog(Get.context!);
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
|
||||||
isLoading.value = true;
|
|
||||||
final isSuccessUpdate = await _userService.updateProfileData(
|
|
||||||
_userController.userData!.id,
|
|
||||||
nameController.text.trim(),
|
|
||||||
birthDate: birthDateController.text.trim(),
|
|
||||||
phone: phoneController.text.trim(),
|
|
||||||
locale: selectedLocale.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isSuccessUpdate) {
|
final isSuccessUpdate = await _userService.updateProfileData(
|
||||||
final response = await _userService.getUserData(_userController.userData!.id);
|
_userController.userData!.id,
|
||||||
|
nameController.text.trim(),
|
||||||
|
birthDate: birthDateController.text.trim(),
|
||||||
|
phone: phoneController.text.trim(),
|
||||||
|
locale: selectedLocale.value,
|
||||||
|
);
|
||||||
|
|
||||||
if (response?.data != null) {
|
if (isSuccessUpdate) {
|
||||||
final userNew = response!.data!;
|
final response = await _userService.getUserData(_userController.userData!.id);
|
||||||
final newUser = UserEntity(
|
|
||||||
id: userNew.id,
|
|
||||||
email: userNew.email,
|
|
||||||
name: userNew.name,
|
|
||||||
birthDate: userNew.birthDate,
|
|
||||||
locale: userNew.locale,
|
|
||||||
picUrl: userNew.picUrl,
|
|
||||||
phone: userNew.phone,
|
|
||||||
createdAt: userNew.createdAt,
|
|
||||||
);
|
|
||||||
|
|
||||||
_userStorageService.saveUser(newUser);
|
if (response?.data != null) {
|
||||||
_userController.userData = newUser;
|
final userNew = response!.data!;
|
||||||
|
final newUser = UserEntity(
|
||||||
|
id: userNew.id,
|
||||||
|
email: userNew.email,
|
||||||
|
name: userNew.name,
|
||||||
|
birthDate: userNew.birthDate,
|
||||||
|
locale: userNew.locale,
|
||||||
|
picUrl: userNew.picUrl,
|
||||||
|
phone: userNew.phone,
|
||||||
|
);
|
||||||
|
|
||||||
_userController.email.value = userNew.email;
|
_userStorageService.saveUser(newUser);
|
||||||
_userController.userName.value = userNew.name;
|
_userController.userData = newUser;
|
||||||
_userController.userImage.value = userNew.picUrl;
|
|
||||||
}
|
_userController.email.value = userNew.email;
|
||||||
|
_userController.userName.value = userNew.name;
|
||||||
|
_userController.userImage.value = userNew.picUrl;
|
||||||
}
|
}
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
isLoading.value = false;
|
|
||||||
|
|
||||||
Get.back(result: true);
|
|
||||||
CustomNotification.success(title: "Success", message: "Profile updated successfully");
|
|
||||||
} catch (e) {
|
|
||||||
CustomNotification.success(title: "something wrong", message: "failed to update profile");
|
|
||||||
isLoading.value = false;
|
|
||||||
logC.e(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Get.back();
|
||||||
|
|
||||||
|
CustomNotification.success(title: "Success", message: "Profile updated successfully");
|
||||||
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isValidDateFormat(String date) {
|
bool _isValidDateFormat(String date) {
|
||||||
final regex = RegExp(r'^([0-2][0-9]|(3)[0-1])\-((0[1-9])|(1[0-2]))\-\d{4}$');
|
final regex = RegExp(r'^([0-2][0-9]|(3)[0-1])\-((0[1-9])|(1[0-2]))\-\d{4}$');
|
||||||
return regex.hasMatch(date);
|
return regex.hasMatch(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoBack() {
|
|
||||||
if (!isLoading.value) Get.back();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class ProfileView extends GetView<ProfileController> {
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
_profileDetails(cardRadius: cardRadius),
|
_profileDetails(cardRadius: cardRadius),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
_settingsSection(context, cardRadius: cardRadius),
|
_settingsSection(cardRadius: cardRadius),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
_legalSection(cardRadius: cardRadius),
|
_legalSection(cardRadius: cardRadius),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
@ -161,7 +161,7 @@ class ProfileView extends GetView<ProfileController> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _settingsSection(BuildContext context, {required BorderRadius cardRadius}) => Card(
|
Widget _settingsSection({required BorderRadius cardRadius}) => Card(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
shadowColor: AppColors.shadowPrimary,
|
shadowColor: AppColors.shadowPrimary,
|
||||||
|
@ -177,7 +177,7 @@ class ProfileView extends GetView<ProfileController> {
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
_settingsTile(Get.context!, icon: LucideIcons.languages, title: tr('change_language'), onTap: () => _showLanguageDialog(Get.context!)),
|
_settingsTile(Get.context!, icon: LucideIcons.languages, title: tr('change_language'), onTap: () => _showLanguageDialog(Get.context!)),
|
||||||
_settingsTile(Get.context!,
|
_settingsTile(Get.context!,
|
||||||
icon: LucideIcons.logOut, title: tr('logout'), iconColor: Colors.red, textColor: Colors.red, onTap: () => controller.logout(context)),
|
icon: LucideIcons.logOut, title: tr('logout'), iconColor: Colors.red, textColor: Colors.red, onTap: controller.logout),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
|
||||||
import 'package:quiz_app/component/global_button.dart';
|
import 'package:quiz_app/component/global_button.dart';
|
||||||
import 'package:quiz_app/component/global_dropdown_field.dart';
|
import 'package:quiz_app/component/global_dropdown_field.dart';
|
||||||
import 'package:quiz_app/component/global_text_field.dart';
|
import 'package:quiz_app/component/global_text_field.dart';
|
||||||
|
@ -11,62 +9,56 @@ import 'package:quiz_app/feature/profile/controller/update_profile_controller.da
|
||||||
class UpdateProfilePage extends GetView<UpdateProfileController> {
|
class UpdateProfilePage extends GetView<UpdateProfileController> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return Scaffold(
|
||||||
canPop: false,
|
appBar: AppBar(
|
||||||
onPopInvokedWithResult: (didPop, result) => controller.onGoBack(),
|
title: Text('Update Profile'),
|
||||||
child: Scaffold(
|
centerTitle: true,
|
||||||
backgroundColor: AppColors.background2,
|
),
|
||||||
appBar: AppBar(
|
body: Padding(
|
||||||
backgroundColor: AppColors.background2,
|
padding: const EdgeInsets.all(16.0),
|
||||||
title: Text('Update Profile'),
|
child: ListView(
|
||||||
centerTitle: true,
|
children: [
|
||||||
),
|
LabelTextField(label: "Name"),
|
||||||
body: Padding(
|
GlobalTextField(controller: controller.nameController),
|
||||||
padding: const EdgeInsets.all(16.0),
|
SizedBox(height: 16),
|
||||||
child: ListView(
|
LabelTextField(label: "Phone"),
|
||||||
children: [
|
GlobalTextField(
|
||||||
LabelTextField(label: "Name"),
|
controller: controller.phoneController,
|
||||||
GlobalTextField(controller: controller.nameController),
|
hintText: 'Enter your phone number',
|
||||||
SizedBox(height: 16),
|
),
|
||||||
LabelTextField(label: "Phone"),
|
SizedBox(height: 16),
|
||||||
GlobalTextField(
|
LabelTextField(label: "Birth Date"),
|
||||||
controller: controller.phoneController,
|
GlobalTextField(
|
||||||
hintText: 'Enter your phone number',
|
controller: controller.birthDateController,
|
||||||
),
|
hintText: 'Enter your birth date',
|
||||||
SizedBox(height: 16),
|
),
|
||||||
LabelTextField(label: "Birth Date"),
|
SizedBox(height: 16),
|
||||||
GlobalTextField(
|
LabelTextField(label: "Locale"),
|
||||||
controller: controller.birthDateController,
|
Obx(() => GlobalDropdownField<String>(
|
||||||
hintText: 'Enter your birth date',
|
value: controller.selectedLocale.value,
|
||||||
),
|
items: controller.localeMap.entries.map<DropdownMenuItem<String>>((entry) {
|
||||||
SizedBox(height: 16),
|
return DropdownMenuItem<String>(
|
||||||
LabelTextField(label: "Locale"),
|
value: entry.value,
|
||||||
Obx(() => GlobalDropdownField<String>(
|
child: Text(entry.key), // Display country name
|
||||||
value: controller.selectedLocale.value,
|
);
|
||||||
items: controller.localeMap.entries.map<DropdownMenuItem<String>>((entry) {
|
}).toList(),
|
||||||
return DropdownMenuItem<String>(
|
onChanged: (String? newValue) {
|
||||||
value: entry.value,
|
if (newValue != null) {
|
||||||
child: Text(entry.key), // Display country name
|
controller.selectedLocale.value = newValue;
|
||||||
);
|
final parts = newValue.split('-');
|
||||||
}).toList(),
|
if (parts.length == 2) {
|
||||||
onChanged: (String? newValue) {
|
Get.updateLocale(Locale(parts[0], parts[1]));
|
||||||
if (newValue != null) {
|
} else {
|
||||||
controller.selectedLocale.value = newValue;
|
Get.updateLocale(Locale(newValue));
|
||||||
final parts = newValue.split('-');
|
|
||||||
if (parts.length == 2) {
|
|
||||||
Get.updateLocale(Locale(parts[0], parts[1]));
|
|
||||||
} else {
|
|
||||||
Get.updateLocale(Locale(newValue));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)),
|
},
|
||||||
SizedBox(height: 32),
|
)),
|
||||||
Center(
|
SizedBox(height: 32),
|
||||||
child: GlobalButton(text: tr("save_changes"), onPressed: controller.saveProfile),
|
Center(
|
||||||
),
|
child: GlobalButton(text: "save_changes", onPressed: controller.saveProfile),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import "package:get/get.dart";
|
import "package:get/get.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/quiz_service.dart";
|
||||||
import "package:quiz_app/feature/quiz_creation/controller/quiz_creation_controller.dart";
|
import "package:quiz_app/feature/quiz_creation/controller/quiz_creation_controller.dart";
|
||||||
|
|
||||||
|
@ -10,7 +9,6 @@ class QuizCreationBinding extends Bindings {
|
||||||
Get.lazyPut<QuizCreationController>(
|
Get.lazyPut<QuizCreationController>(
|
||||||
() => QuizCreationController(
|
() => QuizCreationController(
|
||||||
Get.find<QuizService>(),
|
Get.find<QuizService>(),
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,16 @@ import 'package:quiz_app/app/const/enums/question_type.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.dart';
|
import 'package:quiz_app/app/routes/app_pages.dart';
|
||||||
import 'package:quiz_app/component/notification/delete_confirmation.dart';
|
import 'package:quiz_app/component/notification/delete_confirmation.dart';
|
||||||
import 'package:quiz_app/component/notification/pop_up_confirmation.dart';
|
import 'package:quiz_app/component/notification/pop_up_confirmation.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_floating_loading.dart';
|
||||||
import 'package:quiz_app/core/utils/custom_notification.dart';
|
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
import 'package:quiz_app/data/models/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
import 'package:quiz_app/data/models/quiz/quiestion_data_model.dart';
|
import 'package:quiz_app/data/models/quiz/quiestion_data_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/quiz_service.dart';
|
||||||
|
|
||||||
class QuizCreationController extends GetxController {
|
class QuizCreationController extends GetxController {
|
||||||
final QuizService _quizService;
|
final QuizService _quizService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
QuizCreationController(
|
QuizCreationController(this._quizService);
|
||||||
this._quizService,
|
|
||||||
this._connectionService,
|
|
||||||
);
|
|
||||||
|
|
||||||
final TextEditingController inputSentenceTC = TextEditingController();
|
final TextEditingController inputSentenceTC = TextEditingController();
|
||||||
final TextEditingController questionTC = TextEditingController();
|
final TextEditingController questionTC = TextEditingController();
|
||||||
|
@ -35,8 +29,6 @@ class QuizCreationController extends GetxController {
|
||||||
|
|
||||||
RxInt currentDuration = 30.obs;
|
RxInt currentDuration = 30.obs;
|
||||||
|
|
||||||
RxBool isLoading = false.obs;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
@ -201,7 +193,7 @@ class QuizCreationController extends GetxController {
|
||||||
|
|
||||||
void onBack(BuildContext context) {
|
void onBack(BuildContext context) {
|
||||||
if (quizData.length <= 1) {
|
if (quizData.length <= 1) {
|
||||||
Get.back();
|
Navigator.pop(context);
|
||||||
} else {
|
} else {
|
||||||
AppDialog.showExitConfirmationDialog(context);
|
AppDialog.showExitConfirmationDialog(context);
|
||||||
}
|
}
|
||||||
|
@ -232,77 +224,49 @@ class QuizCreationController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateQuiz() async {
|
void generateQuiz() async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
CustomFloatingLoading.showLoadingDialog(Get.context!);
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputSentenceTC.text.trim().isEmpty) {
|
|
||||||
CustomNotification.error(title: "Gagal", message: "kalimat atau paragraph tidak boleh kosong");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
|
||||||
isLoading.value = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BaseResponseModel<List<RawQuizModel>> response = await _quizService.createQuizAuto(inputSentenceTC.text);
|
BaseResponseModel<List<RawQuizModel>> response = await _quizService.createQuizAuto(inputSentenceTC.text);
|
||||||
|
|
||||||
if (response.data != null && response.data!.isNotEmpty) {
|
if (response.data != null) {
|
||||||
// Check if we should remove the initial empty question
|
final previousLength = quizData.length;
|
||||||
bool shouldRemoveInitial = quizData.length == 1 && quizData[0].question == null && quizData[0].answer == null;
|
|
||||||
|
|
||||||
if (shouldRemoveInitial) {
|
if (previousLength == 1) quizData.removeAt(0);
|
||||||
quizData.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new questions
|
for (final i in response.data!) {
|
||||||
for (final quizItem in response.data!) {
|
|
||||||
QuestionType type = QuestionType.fillTheBlank;
|
QuestionType type = QuestionType.fillTheBlank;
|
||||||
|
|
||||||
if (quizItem.answer.toString().toLowerCase() == 'true' || quizItem.answer.toString().toLowerCase() == 'false') {
|
if (i.answer.toString().toLowerCase() == 'true' || i.answer.toString().toLowerCase() == 'false') {
|
||||||
type = QuestionType.trueOrFalse;
|
type = QuestionType.trueOrFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
quizData.add(QuestionData(
|
quizData.add(QuestionData(
|
||||||
index: quizData.length + 1,
|
index: quizData.length + 1,
|
||||||
question: quizItem.qustion,
|
question: i.qustion,
|
||||||
answer: quizItem.answer,
|
answer: i.answer,
|
||||||
type: type,
|
type: type,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the selected index to the first newly added question
|
if (response.data!.isNotEmpty) {
|
||||||
if (shouldRemoveInitial) {
|
selectedQuizIndex.value = previousLength;
|
||||||
selectedQuizIndex.value = 0;
|
|
||||||
} else {
|
|
||||||
// If we didn't remove initial data, select the first new question
|
|
||||||
selectedQuizIndex.value = quizData.length - response.data!.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI with the selected question data
|
|
||||||
if (selectedQuizIndex.value < quizData.length) {
|
|
||||||
final data = quizData[selectedQuizIndex.value];
|
final data = quizData[selectedQuizIndex.value];
|
||||||
questionTC.text = data.question ?? "";
|
questionTC.text = data.question ?? "";
|
||||||
answerTC.text = data.answer ?? "";
|
answerTC.text = data.answer ?? "";
|
||||||
currentDuration.value = data.duration;
|
currentDuration.value = data.duration;
|
||||||
currentQuestionType.value = data.type ?? QuestionType.fillTheBlank;
|
currentQuestionType.value = data.type ?? QuestionType.fillTheBlank;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logC.e("Error while generating quiz: $e");
|
logC.e("Error while generating quiz: $e");
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
} finally {
|
} finally {
|
||||||
CustomFloatingLoading.hideLoading();
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
isLoading.value = false;
|
|
||||||
isGenerate.value = false;
|
isGenerate.value = false;
|
||||||
inputSentenceTC.text = "";
|
|
||||||
|
|
||||||
if (quizData.isNotEmpty && selectedQuizIndex.value >= quizData.length) {
|
if (quizData.isNotEmpty && selectedQuizIndex.value == 0) {
|
||||||
selectedQuizIndex.value = 0;
|
final data = quizData[0];
|
||||||
}
|
|
||||||
|
|
||||||
if (quizData.isNotEmpty) {
|
|
||||||
final data = quizData[selectedQuizIndex.value];
|
|
||||||
questionTC.text = data.question ?? "";
|
questionTC.text = data.question ?? "";
|
||||||
answerTC.text = data.answer ?? "";
|
answerTC.text = data.answer ?? "";
|
||||||
currentDuration.value = data.duration;
|
currentDuration.value = data.duration;
|
||||||
|
@ -310,8 +274,4 @@ class QuizCreationController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onGoBack(BuildContext context, bool didPop) {
|
|
||||||
if (!isLoading.value) onBack(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,39 +11,35 @@ class QuizCreationView extends GetView<QuizCreationController> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return Scaffold(
|
||||||
canPop: false,
|
backgroundColor: AppColors.background,
|
||||||
onPopInvokedWithResult: (didPop, result) => controller.onGoBack(context, didPop),
|
appBar: AppBar(
|
||||||
child: Scaffold(
|
|
||||||
backgroundColor: AppColors.background,
|
backgroundColor: AppColors.background,
|
||||||
appBar: AppBar(
|
elevation: 0,
|
||||||
backgroundColor: AppColors.background,
|
title: Text(
|
||||||
elevation: 0,
|
context.tr('create_quiz_title'),
|
||||||
title: Text(
|
style: const TextStyle(
|
||||||
context.tr('create_quiz_title'),
|
fontWeight: FontWeight.bold,
|
||||||
style: const TextStyle(
|
color: AppColors.darkText,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.darkText,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
leading: IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back_ios_new_rounded, color: AppColors.darkText),
|
|
||||||
onPressed: () => controller.onBack(context),
|
|
||||||
),
|
|
||||||
centerTitle: true,
|
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
leading: IconButton(
|
||||||
child: Padding(
|
icon: const Icon(Icons.arrow_back_ios_new_rounded, color: AppColors.darkText),
|
||||||
padding: const EdgeInsets.all(20.0),
|
onPressed: () => controller.onBack(context),
|
||||||
child: SingleChildScrollView(
|
),
|
||||||
child: Column(
|
centerTitle: true,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: [
|
body: SafeArea(
|
||||||
_buildModeSelector(context),
|
child: Padding(
|
||||||
const SizedBox(height: 20),
|
padding: const EdgeInsets.all(20.0),
|
||||||
Obx(() => controller.isGenerate.value ? const GenerateComponent() : const CustomQuestionComponent()),
|
child: SingleChildScrollView(
|
||||||
],
|
child: Column(
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildModeSelector(context),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Obx(() => controller.isGenerate.value ? const GenerateComponent() : const CustomQuestionComponent()),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +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/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/quiz_service.dart';
|
||||||
import 'package:quiz_app/data/services/subject_service.dart';
|
import 'package:quiz_app/data/services/subject_service.dart';
|
||||||
import 'package:quiz_app/feature/quiz_preview/controller/quiz_preview_controller.dart';
|
import 'package:quiz_app/feature/quiz_preview/controller/quiz_preview_controller.dart';
|
||||||
|
@ -14,7 +13,6 @@ class QuizPreviewBinding extends Bindings {
|
||||||
Get.find<QuizService>(),
|
Get.find<QuizService>(),
|
||||||
Get.find<UserController>(),
|
Get.find<UserController>(),
|
||||||
Get.find<SubjectService>(),
|
Get.find<SubjectService>(),
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/app/const/enums/question_type.dart';
|
import 'package:quiz_app/app/const/enums/question_type.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.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_floating_loading.dart';
|
||||||
import 'package:quiz_app/core/utils/custom_notification.dart';
|
import 'package:quiz_app/core/utils/custom_notification.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
|
@ -11,7 +10,6 @@ import 'package:quiz_app/data/models/quiz/question_create_request.dart';
|
||||||
import 'package:quiz_app/data/models/quiz/question_listings_model.dart';
|
import 'package:quiz_app/data/models/quiz/question_listings_model.dart';
|
||||||
import 'package:quiz_app/data/models/quiz/quiestion_data_model.dart';
|
import 'package:quiz_app/data/models/quiz/quiestion_data_model.dart';
|
||||||
import 'package:quiz_app/data/models/subject/subject_model.dart';
|
import 'package:quiz_app/data/models/subject/subject_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/quiz_service.dart';
|
||||||
import 'package:quiz_app/data/services/subject_service.dart';
|
import 'package:quiz_app/data/services/subject_service.dart';
|
||||||
|
|
||||||
|
@ -22,13 +20,11 @@ class QuizPreviewController extends GetxController {
|
||||||
final QuizService _quizService;
|
final QuizService _quizService;
|
||||||
final UserController _userController;
|
final UserController _userController;
|
||||||
final SubjectService _subjectService;
|
final SubjectService _subjectService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
QuizPreviewController(
|
QuizPreviewController(
|
||||||
this._quizService,
|
this._quizService,
|
||||||
this._userController,
|
this._userController,
|
||||||
this._subjectService,
|
this._subjectService,
|
||||||
this._connectionService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
RxBool isPublic = false.obs;
|
RxBool isPublic = false.obs;
|
||||||
|
@ -74,10 +70,6 @@ class QuizPreviewController extends GetxController {
|
||||||
|
|
||||||
Future<void> onSaveQuiz() async {
|
Future<void> onSaveQuiz() async {
|
||||||
try {
|
try {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isLoading.value) return;
|
if (isLoading.value) return;
|
||||||
|
|
||||||
final title = titleController.text.trim();
|
final title = titleController.text.trim();
|
||||||
|
@ -96,12 +88,11 @@ class QuizPreviewController extends GetxController {
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
message: 'Jumlah soal harus 10 atau lebih',
|
message: 'Jumlah soal harus 10 atau lebih',
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
CustomFloatingLoading.showLoadingDialog(Get.context!);
|
||||||
|
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final String formattedDate = "${now.day.toString().padLeft(2, '0')}-${now.month.toString().padLeft(2, '0')}-${now.year}";
|
final String formattedDate = "${now.day.toString().padLeft(2, '0')}-${now.month.toString().padLeft(2, '0')}-${now.year}";
|
||||||
|
@ -125,14 +116,13 @@ class QuizPreviewController extends GetxController {
|
||||||
message: 'Kuis berhasil disimpan!',
|
message: 'Kuis berhasil disimpan!',
|
||||||
);
|
);
|
||||||
|
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
Get.offAllNamed(AppRoutes.mainPage, arguments: 2);
|
Get.offAllNamed(AppRoutes.mainPage, arguments: 2);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
CustomFloatingLoading.hideLoading();
|
|
||||||
logC.e(e);
|
logC.e(e);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
// CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,10 +170,6 @@ class QuizPreviewController extends GetxController {
|
||||||
subjectIndex.value = index;
|
subjectIndex.value = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBack() {
|
|
||||||
if (!isLoading.value) Get.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
titleController.dispose();
|
titleController.dispose();
|
||||||
|
|
|
@ -34,7 +34,6 @@ class SubjectDropdownComponent extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dropdownColor: Colors.white,
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
|
|
|
@ -14,17 +14,13 @@ class QuizPreviewPage extends GetView<QuizPreviewController> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return Scaffold(
|
||||||
canPop: false,
|
backgroundColor: AppColors.background,
|
||||||
onPopInvokedWithResult: (didPop, result) => controller.onBack(),
|
appBar: _buildAppBar(context),
|
||||||
child: Scaffold(
|
body: SafeArea(
|
||||||
backgroundColor: AppColors.background,
|
child: Padding(
|
||||||
appBar: _buildAppBar(context),
|
padding: const EdgeInsets.all(20.0),
|
||||||
body: SafeArea(
|
child: _buildContent(context),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(20.0),
|
|
||||||
child: _buildContent(context),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -95,7 +95,7 @@ class QuizResultView extends GetView<QuizResultController> {
|
||||||
final parsed = _parseAnswer(question, answer.selectedAnswer);
|
final parsed = _parseAnswer(question, answer.selectedAnswer);
|
||||||
|
|
||||||
return QuizItemWAComponent(
|
return QuizItemWAComponent(
|
||||||
index: index + 1,
|
index: index,
|
||||||
isCorrect: answer.isCorrect,
|
isCorrect: answer.isCorrect,
|
||||||
question: question.question,
|
question: question.question,
|
||||||
targetAnswer: parsed.targetAnswer,
|
targetAnswer: parsed.targetAnswer,
|
||||||
|
|
|
@ -26,8 +26,6 @@ class RegisterController extends GetxController {
|
||||||
var isPasswordHidden = true.obs;
|
var isPasswordHidden = true.obs;
|
||||||
var isConfirmPasswordHidden = true.obs;
|
var isConfirmPasswordHidden = true.obs;
|
||||||
|
|
||||||
RxBool isLoading = false.obs;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
if (!_connectionService.isCurrentlyConnected) {
|
if (!_connectionService.isCurrentlyConnected) {
|
||||||
|
@ -83,8 +81,7 @@ class RegisterController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CustomFloatingLoading.showLoading(Get.overlayContext!);
|
CustomFloatingLoading.showLoadingDialog(Get.context!);
|
||||||
isLoading.value = true;
|
|
||||||
await _authService.register(
|
await _authService.register(
|
||||||
RegisterRequestModel(
|
RegisterRequestModel(
|
||||||
email: email,
|
email: email,
|
||||||
|
@ -96,12 +93,10 @@ class RegisterController extends GetxController {
|
||||||
);
|
);
|
||||||
|
|
||||||
Get.back();
|
Get.back();
|
||||||
CustomFloatingLoading.hideLoading();
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
isLoading.value = false;
|
|
||||||
CustomNotification.success(title: "Pendaftaran Berhasil", message: "Akun berhasil dibuat");
|
CustomNotification.success(title: "Pendaftaran Berhasil", message: "Akun berhasil dibuat");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
CustomFloatingLoading.hideLoading();
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
isLoading.value = false;
|
|
||||||
|
|
||||||
String errorMessage = e.toString().replaceFirst("Exception: ", "");
|
String errorMessage = e.toString().replaceFirst("Exception: ", "");
|
||||||
|
|
||||||
|
|
|
@ -1,6 +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/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/quiz_service.dart';
|
||||||
import 'package:quiz_app/data/services/session_service.dart';
|
import 'package:quiz_app/data/services/session_service.dart';
|
||||||
import 'package:quiz_app/data/services/socket_service.dart';
|
import 'package:quiz_app/data/services/socket_service.dart';
|
||||||
|
@ -17,7 +16,6 @@ class RoomMakerBinding extends Bindings {
|
||||||
Get.find<UserController>(),
|
Get.find<UserController>(),
|
||||||
Get.find<SocketService>(),
|
Get.find<SocketService>(),
|
||||||
Get.find<QuizService>(),
|
Get.find<QuizService>(),
|
||||||
Get.find<ConnectionService>(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/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/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/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
|
@ -11,7 +9,6 @@ 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_info_model.dart';
|
||||||
import 'package:quiz_app/data/models/session/session_request_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/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/quiz_service.dart';
|
||||||
import 'package:quiz_app/data/services/session_service.dart';
|
import 'package:quiz_app/data/services/session_service.dart';
|
||||||
import 'package:quiz_app/data/services/socket_service.dart';
|
import 'package:quiz_app/data/services/socket_service.dart';
|
||||||
|
@ -21,14 +18,12 @@ class RoomMakerController extends GetxController {
|
||||||
final UserController _userController;
|
final UserController _userController;
|
||||||
final SocketService _socketService;
|
final SocketService _socketService;
|
||||||
final QuizService _quizService;
|
final QuizService _quizService;
|
||||||
final ConnectionService _connectionService;
|
|
||||||
|
|
||||||
RoomMakerController(
|
RoomMakerController(
|
||||||
this._sessionService,
|
this._sessionService,
|
||||||
this._userController,
|
this._userController,
|
||||||
this._socketService,
|
this._socketService,
|
||||||
this._quizService,
|
this._quizService,
|
||||||
this._connectionService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final selectedQuiz = Rxn<QuizListingModel>();
|
final selectedQuiz = Rxn<QuizListingModel>();
|
||||||
|
@ -52,10 +47,6 @@ class RoomMakerController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadQuiz({bool reset = false}) async {
|
Future<void> loadQuiz({bool reset = false}) async {
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isLoading) return;
|
if (isLoading) return;
|
||||||
|
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
@ -101,21 +92,8 @@ class RoomMakerController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onCreateRoom() async {
|
void onCreateRoom() async {
|
||||||
if (nameTC.text.trim().isEmpty || maxPlayerTC.text.trim().isEmpty || selectedQuiz.value == null) {
|
if (nameTC.text.trim().isEmpty || selectedQuiz.value == null) {
|
||||||
CustomNotification.error(title: "Gagal", message: "Nama room, maksimal pemain dan kuis harus dipilih.");
|
Get.snackbar("Gagal", "Nama room dan kuis harus dipilih.");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (int.tryParse(maxPlayerTC.text) == null) {
|
|
||||||
CustomNotification.error(
|
|
||||||
title: "Input tidak valid",
|
|
||||||
message: "Jumlah pemain harus berupa angka tanpa karakter huruf atau simbol.",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await _connectionService.isHaveConnection()) {
|
|
||||||
ConnectionNotification.noInternedConnection();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +103,6 @@ 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),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -588,46 +588,52 @@ class RoomMakerView extends GetView<RoomMakerController> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCreateRoomButton() {
|
Widget _buildCreateRoomButton() {
|
||||||
return AnimatedContainer(
|
return Obx(() {
|
||||||
duration: const Duration(milliseconds: 300),
|
final canCreate = controller.selectedQuiz.value != null && controller.nameTC.text.isNotEmpty && controller.maxPlayerTC.text.isNotEmpty;
|
||||||
width: MediaQuery.of(Get.context!).size.width - 32,
|
|
||||||
height: 56,
|
return AnimatedContainer(
|
||||||
child: Material(
|
duration: const Duration(milliseconds: 300),
|
||||||
elevation: 8,
|
width: MediaQuery.of(Get.context!).size.width - 32,
|
||||||
borderRadius: BorderRadius.circular(16),
|
height: 56,
|
||||||
child: InkWell(
|
child: Material(
|
||||||
|
elevation: canCreate ? 8 : 2,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
onTap: controller.onCreateRoom,
|
child: InkWell(
|
||||||
child: Container(
|
borderRadius: BorderRadius.circular(16),
|
||||||
decoration: BoxDecoration(
|
onTap: canCreate ? controller.onCreateRoom : null,
|
||||||
gradient: LinearGradient(
|
child: Container(
|
||||||
colors: [AppColors.primaryBlue, AppColors.primaryBlue.withValues(alpha: 0.8)],
|
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),
|
||||||
),
|
),
|
||||||
color: Colors.grey[300],
|
child: Row(
|
||||||
borderRadius: BorderRadius.circular(16),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
children: [
|
||||||
child: Row(
|
Icon(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Icons.add_circle,
|
||||||
children: [
|
color: canCreate ? Colors.white : Colors.grey[500],
|
||||||
Icon(
|
size: 24,
|
||||||
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],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ 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;
|
||||||
|
@ -43,7 +42,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
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';
|
||||||
|
@ -14,9 +11,7 @@ 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(
|
appBar: AppBar(title: const Text("Waiting Room")),
|
||||||
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(() {
|
||||||
|
@ -27,39 +22,25 @@ 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),
|
||||||
Text(
|
const Text("Peserta yang Bergabung:", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
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),
|
||||||
controller.isAdmin.value
|
if (controller.isAdmin.value)
|
||||||
? GlobalButton(
|
GlobalButton(
|
||||||
text: tr("start_quiz"),
|
text: "Mulai Kuis",
|
||||||
onPressed: controller.startQuiz,
|
onPressed: controller.startQuiz,
|
||||||
)
|
)
|
||||||
: GlobalButton(
|
else
|
||||||
text: tr("waiting_room.leave_room"),
|
GlobalButton(
|
||||||
onPressed: controller.leaveRoom,
|
text: "Tinggalkan Ruangan",
|
||||||
baseColor: const Color.fromARGB(255, 204, 14, 0),
|
onPressed: controller.leaveRoom,
|
||||||
)
|
baseColor: const Color.fromARGB(255, 204, 14, 0),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -71,19 +52,18 @@ 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.accentBlue.withOpacity(0.1),
|
color: AppColors.primaryBlue.withValues(alpha: 0.05),
|
||||||
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: [
|
||||||
Text(tr("waiting_room.session_code"), style: AppTextStyles.statValue),
|
const Text("Session Code: ", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(width: 4),
|
SelectableText(code, style: const TextStyle(fontSize: 16)),
|
||||||
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: tr("waiting_room.copy_code"),
|
tooltip: 'Salin Kode',
|
||||||
onPressed: () => controller.copySessionCode(context),
|
onPressed: () => controller.copySessionCode(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -92,6 +72,7 @@ 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,
|
||||||
|
@ -103,12 +84,12 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(tr("waiting_room.quiz_info"), style: AppTextStyles.subtitle.copyWith(fontWeight: FontWeight.bold)),
|
const Text("Informasi Kuis:", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text("${tr("waiting_room.quiz_title")}: ${quiz.title}", style: AppTextStyles.body),
|
Text("Judul: ${quiz.title}"),
|
||||||
Text("${tr("waiting_room.quiz_description")}: ${quiz.description}", style: AppTextStyles.body),
|
Text("Deskripsi: ${quiz.description}"),
|
||||||
Text("${tr("waiting_room.quiz_total_question")}: ${quiz.totalQuiz}", style: AppTextStyles.body),
|
Text("Jumlah Soal: ${quiz.totalQuiz}"),
|
||||||
Text("${tr("waiting_room.quiz_duration")}: ${quiz.limitDuration ~/ 60} min", style: AppTextStyles.body),
|
Text("Durasi: ${quiz.limitDuration ~/ 60} menit"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -129,9 +110,9 @@ class WaitingRoomView extends GetView<WaitingRoomController> {
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(child: Text(user.username[0].toUpperCase())),
|
CircleAvatar(child: Text(user.username[0])),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(user.username, style: AppTextStyles.body.copyWith(fontSize: 16)),
|
Text(user.username, style: const TextStyle(fontSize: 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,8 +29,7 @@ void main() {
|
||||||
Locale('ms', 'MY'),
|
Locale('ms', 'MY'),
|
||||||
],
|
],
|
||||||
path: 'assets/translations',
|
path: 'assets/translations',
|
||||||
fallbackLocale: Locale('id', 'ID'),
|
fallbackLocale: Locale('en', 'US'),
|
||||||
startLocale: Locale('id', 'ID'),
|
|
||||||
useOnlyLangCode: false,
|
useOnlyLangCode: false,
|
||||||
child: MyApp(),
|
child: MyApp(),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue