fix: adding loading and connection limitation

This commit is contained in:
akhdanre 2025-06-05 20:35:35 +07:00
parent d5de5fb712
commit ae49bb34d0
25 changed files with 317 additions and 151 deletions

View File

@ -1,6 +1,6 @@
class APIEndpoint { class APIEndpoint {
// static const String baseUrl = "http://192.168.107.43:5000"; static const String baseUrl = "http://192.168.1.13: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";

View File

@ -1,24 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
class CustomFloatingLoading { class CustomFloatingLoading {
static const String _dialogId = 'custom_loading'; static OverlayEntry? _overlayEntry;
static void showLoadingDialog() { static void showLoading(BuildContext context) {
Get.dialog( if (_overlayEntry != null) return;
PopScope(
canPop: false, _overlayEntry = OverlayEntry(
child: const Center(child: CircularProgressIndicator()), builder: (_) => Stack(
children: [
ModalBarrier(
dismissible: false,
color: Colors.black.withValues(alpha: 0.5),
),
const Center(
child: CircularProgressIndicator(),
),
],
), ),
barrierDismissible: false,
barrierColor: Colors.black.withAlpha(76),
name: _dialogId,
); );
Overlay.of(context).insert(_overlayEntry!);
} }
static void hideLoadingDialog() { static void hideLoading() {
if (Get.isOverlaysOpen) { if (_overlayEntry?.mounted == true) {
Get.until((route) => route.settings.name != _dialogId); _overlayEntry?.remove();
} }
_overlayEntry = null;
} }
} }

View File

@ -1,4 +1,5 @@
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';
@ -8,6 +9,11 @@ class DetailQuizBinding extends Bindings {
if (!Get.isRegistered<QuizService>()) { if (!Get.isRegistered<QuizService>()) {
Get.lazyPut<QuizService>(() => QuizService()); Get.lazyPut<QuizService>(() => QuizService());
} }
Get.lazyPut<DetailQuizController>(() => DetailQuizController(Get.find<QuizService>())); Get.lazyPut<DetailQuizController>(
() => DetailQuizController(
Get.find<QuizService>(),
Get.find<ConnectionService>(),
),
);
} }
} }

View File

@ -1,17 +1,20 @@
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); DetailQuizController(this._quizService, this._connectionService);
RxBool isLoading = true.obs; RxBool isLoading = true.obs;
late QuizData data; QuizData? data;
@override @override
void onInit() { void onInit() {
@ -21,6 +24,11 @@ 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);
} }
@ -32,5 +40,11 @@ class DetailQuizController extends GetxController {
isLoading.value = false; isLoading.value = false;
} }
void goToPlayPage() => Get.toNamed(AppRoutes.playQuizPage, arguments: data); void goToPlayPage() {
if (!_connectionService.isCurrentlyConnected) {
ConnectionNotification.noInternedConnection();
return;
}
Get.toNamed(AppRoutes.playQuizPage, arguments: data);
}
} }

View File

@ -30,16 +30,22 @@ 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(() {
() => controller.isLoading.value if (controller.isLoading.value) {
? const Center(child: LoadingWidget()) return const Center(child: LoadingWidget());
: SingleChildScrollView( }
if (controller.data == null) {
return const Center(child: Text("Tidak Ditemukan"));
}
return SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Header Section // Header Section
Text( Text(
controller.data.title, controller.data!.title,
style: const TextStyle( style: const TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -48,7 +54,7 @@ class DetailQuizView extends GetView<DetailQuizController> {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
controller.data.description ?? "", controller.data!.description ?? "",
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
color: AppColors.softGrayText, color: AppColors.softGrayText,
@ -60,14 +66,14 @@ class DetailQuizView extends GetView<DetailQuizController> {
const Icon(Icons.calendar_today_rounded, size: 16, color: AppColors.softGrayText), const Icon(Icons.calendar_today_rounded, size: 16, color: AppColors.softGrayText),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
controller.data.date ?? "", controller.data!.date ?? "",
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText), style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
const Icon(Icons.timer_rounded, size: 16, color: AppColors.softGrayText), const Icon(Icons.timer_rounded, size: 16, color: AppColors.softGrayText),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
'${controller.data.limitDuration ~/ 60} ${tr('minutes_suffix')}', '${controller.data!.limitDuration ~/ 60} ${tr('minutes_suffix')}',
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText), style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
), ),
], ],
@ -84,16 +90,16 @@ class DetailQuizView extends GetView<DetailQuizController> {
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemCount: controller.data.questionListings.length, itemCount: controller.data!.questionListings.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final question = controller.data.questionListings[index]; final question = controller.data!.questionListings[index];
return _buildQuestionItem(question, index + 1); return _buildQuestionItem(question, index + 1);
}, },
), ),
], ],
), ),
), );
), }),
), ),
), ),
); );

View File

@ -1,5 +1,6 @@
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';
@ -7,6 +8,12 @@ class HistoryBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.lazyPut<HistoryService>(() => HistoryService()); Get.lazyPut<HistoryService>(() => HistoryService());
Get.lazyPut(() => HistoryController(Get.find<HistoryService>(), Get.find<UserController>())); Get.lazyPut(
() => HistoryController(
Get.find<HistoryService>(),
Get.find<UserController>(),
Get.find<ConnectionService>(),
),
);
} }
} }

View File

@ -1,14 +1,21 @@
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(this._historyService, this._userController); HistoryController(
this._historyService,
this._userController,
this._connectionService,
);
RxBool isLoading = true.obs; RxBool isLoading = true.obs;
@ -17,10 +24,15 @@ class HistoryController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
loadDummyHistory(); loadHistory();
}
void loadHistory() async {
if (!await _connectionService.isHaveConnection()) {
ConnectionNotification.noInternedConnection();
return;
} }
void loadDummyHistory() async {
historyList.value = await _historyService.getHistory(_userController.userData!.id) ?? []; historyList.value = await _historyService.getHistory(_userController.userData!.id) ?? [];
isLoading.value = false; isLoading.value = false;
} }

View File

@ -1,4 +1,6 @@
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';
@ -10,8 +12,10 @@ 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>(),
), ),
); );
} }

View File

@ -1,24 +1,28 @@
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 = Get.find<UserController>(); final UserController _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;
@ -39,6 +43,10 @@ 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>);
@ -46,6 +54,7 @@ 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!);

View File

@ -5,6 +5,7 @@ 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,

View File

@ -41,13 +41,13 @@ class JoinRoomController extends GetxController {
); );
return; return;
} }
CustomFloatingLoading.showLoadingDialog(); CustomFloatingLoading.showLoading(Get.overlayContext!);
_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"); CustomNotification.error(title: "not found", message: "Ruangan tidak ditemukan");
CustomFloatingLoading.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
}); });
_socketService.roomMessages.listen((data) { _socketService.roomMessages.listen((data) {
@ -56,7 +56,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.hideLoadingDialog(context); // CustomFloatingLoading.showLoading(context);
Get.toNamed( Get.toNamed(
AppRoutes.waitRoomPage, AppRoutes.waitRoomPage,
arguments: WaitingRoomDTO( arguments: WaitingRoomDTO(

View File

@ -1,5 +1,6 @@
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';
@ -9,6 +10,10 @@ 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.find<QuizService>(), Get.find<UserController>())); Get.lazyPut<LibraryController>(() => LibraryController(
Get.find<QuizService>(),
Get.find<UserController>(),
Get.find<ConnectionService>(),
));
} }
} }

View File

@ -1,19 +1,26 @@
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
@ -23,6 +30,10 @@ 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);

View File

@ -37,13 +37,15 @@ 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);
ever(_connectionService.isConnected, (value) { _connectionWorker = ever(_connectionService.isConnected, (value) {
if (!value) { if (!value) {
ConnectionNotification.noInternedConnection(); ConnectionNotification.noInternedConnection();
} else { } else {
@ -88,7 +90,7 @@ class LoginController extends GetxController {
} }
try { try {
isLoading.value = true; isLoading.value = true;
CustomFloatingLoading.showLoadingDialog(); 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),
@ -99,11 +101,11 @@ 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.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
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.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
CustomNotification.error(title: "Gagal", message: "Periksa kembali email dan kata sandi Anda"); CustomNotification.error(title: "Gagal", message: "Periksa kembali email dan kata sandi Anda");
} }
} }
@ -114,12 +116,12 @@ class LoginController extends GetxController {
return; return;
} }
try { try {
CustomFloatingLoading.showLoadingDialog(); CustomFloatingLoading.showLoading(Get.overlayContext!);
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.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
return; return;
} }
@ -127,7 +129,7 @@ class LoginController extends GetxController {
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.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
return; return;
} }
@ -137,7 +139,7 @@ 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.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
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);
@ -160,4 +162,10 @@ class LoginController extends GetxController {
phone: response.phone, phone: response.phone,
); );
} }
@override
void onClose() {
_connectionWorker.dispose();
super.onClose();
}
} }

View File

@ -1,10 +1,11 @@
// 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.lazyPut<NavigationController>(() => NavigationController(Get.find<ConnectionService>()));
} }
} }

View File

@ -1,8 +1,14 @@
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();
@ -12,6 +18,18 @@ 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;
} }

View File

@ -1,5 +1,6 @@
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';
@ -15,6 +16,7 @@ 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>(),
)); ));
} }
} }

View File

@ -1,5 +1,6 @@
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';
@ -12,6 +13,7 @@ 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>(),
)); ));
} }
} }

View File

@ -8,6 +8,7 @@ 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';
@ -18,12 +19,14 @@ 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
@ -74,6 +77,9 @@ 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) {

View File

@ -1,9 +1,12 @@
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';
@ -11,11 +14,13 @@ 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();
@ -44,25 +49,37 @@ 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) {
Get.snackbar('Validation Error', 'All fields must be filled.', snackPosition: SnackPosition.TOP); CustomNotification.error(
title: 'Validation Error',
message: 'All fields must be filled.',
);
return false; return false;
} }
if (!_isValidDateFormat(birthDate)) { if (!_isValidDateFormat(birthDate)) {
Get.snackbar('Validation Error', 'birth date must valid.', snackPosition: SnackPosition.TOP); CustomNotification.error(
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()) {
// Get.back();
ConnectionNotification.noInternedConnection();
return;
}
if (!_validateInputs()) return; if (!_validateInputs()) return;
CustomFloatingLoading.showLoadingDialog(); try {
CustomFloatingLoading.showLoading(Get.overlayContext!);
final isSuccessUpdate = await _userService.updateProfileData( final isSuccessUpdate = await _userService.updateProfileData(
_userController.userData!.id, _userController.userData!.id,
nameController.text.trim(), nameController.text.trim(),
@ -94,11 +111,14 @@ class UpdateProfileController extends GetxController {
_userController.userImage.value = userNew.picUrl; _userController.userImage.value = userNew.picUrl;
} }
} }
CustomFloatingLoading.hideLoading();
Get.back(); Get.back();
CustomNotification.success(title: "Success", message: "Profile updated successfully"); CustomNotification.success(title: "Success", message: "Profile updated successfully");
CustomFloatingLoading.hideLoadingDialog(); } catch (e) {
CustomNotification.success(title: "something wrong", message: "failed to update profile");
logC.e(e);
}
} }
bool _isValidDateFormat(String date) { bool _isValidDateFormat(String date) {

View File

@ -1,4 +1,5 @@
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";
@ -9,6 +10,7 @@ class QuizCreationBinding extends Bindings {
Get.lazyPut<QuizCreationController>( Get.lazyPut<QuizCreationController>(
() => QuizCreationController( () => QuizCreationController(
Get.find<QuizService>(), Get.find<QuizService>(),
Get.find<ConnectionService>(),
), ),
); );
} }

View File

@ -5,16 +5,22 @@ 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(this._quizService); QuizCreationController(
this._quizService,
this._connectionService,
);
final TextEditingController inputSentenceTC = TextEditingController(); final TextEditingController inputSentenceTC = TextEditingController();
final TextEditingController questionTC = TextEditingController(); final TextEditingController questionTC = TextEditingController();
@ -224,7 +230,16 @@ class QuizCreationController extends GetxController {
} }
void generateQuiz() async { void generateQuiz() async {
CustomFloatingLoading.showLoadingDialog(); if (!await _connectionService.isHaveConnection()) {
ConnectionNotification.noInternedConnection();
return;
}
if (inputSentenceTC.text.trim().isEmpty) {
CustomNotification.error(title: "Gagal", message: "kalimat atau paragraph tidak boleh kosong");
return;
}
CustomFloatingLoading.showLoading(Get.overlayContext!);
try { try {
BaseResponseModel<List<RawQuizModel>> response = await _quizService.createQuizAuto(inputSentenceTC.text); BaseResponseModel<List<RawQuizModel>> response = await _quizService.createQuizAuto(inputSentenceTC.text);
@ -262,7 +277,7 @@ class QuizCreationController extends GetxController {
} catch (e) { } catch (e) {
logC.e("Error while generating quiz: $e"); logC.e("Error while generating quiz: $e");
} finally { } finally {
CustomFloatingLoading.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
isGenerate.value = false; isGenerate.value = false;
if (quizData.isNotEmpty && selectedQuizIndex.value == 0) { if (quizData.isNotEmpty && selectedQuizIndex.value == 0) {

View File

@ -1,5 +1,6 @@
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';
@ -13,6 +14,7 @@ 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>(),
)); ));
} }
} }

View File

@ -2,6 +2,7 @@ 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';
@ -10,6 +11,7 @@ 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';
@ -20,11 +22,13 @@ 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;
@ -70,6 +74,10 @@ 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();
@ -92,7 +100,7 @@ class QuizPreviewController extends GetxController {
} }
isLoading.value = true; isLoading.value = true;
CustomFloatingLoading.showLoadingDialog(); CustomFloatingLoading.showLoading(Get.overlayContext!);
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}";
@ -122,7 +130,6 @@ class QuizPreviewController extends GetxController {
logC.e(e); logC.e(e);
} finally { } finally {
isLoading.value = false; isLoading.value = false;
// CustomFloatingLoading.hideLoadingDialog();
} }
} }

View File

@ -81,7 +81,7 @@ class RegisterController extends GetxController {
} }
try { try {
CustomFloatingLoading.showLoadingDialog(); CustomFloatingLoading.showLoading(Get.overlayContext!);
await _authService.register( await _authService.register(
RegisterRequestModel( RegisterRequestModel(
email: email, email: email,
@ -93,10 +93,10 @@ class RegisterController extends GetxController {
); );
Get.back(); Get.back();
CustomFloatingLoading.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
CustomNotification.success(title: "Pendaftaran Berhasil", message: "Akun berhasil dibuat"); CustomNotification.success(title: "Pendaftaran Berhasil", message: "Akun berhasil dibuat");
} catch (e) { } catch (e) {
CustomFloatingLoading.hideLoadingDialog(); CustomFloatingLoading.hideLoading();
String errorMessage = e.toString().replaceFirst("Exception: ", ""); String errorMessage = e.toString().replaceFirst("Exception: ", "");