TIFNGK_E41222905/lib/feature/others/main_controller/main_controller.dart

327 lines
9.8 KiB
Dart

import 'dart:typed_data';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:epic_story_app/core/services/firebase_service.dart';
import 'package:epic_story_app/core/services/google_auth_service.dart';
import 'package:epic_story_app/core/services/isar_service.dart';
import 'package:epic_story_app/core/utils/epic_log.dart';
import 'package:epic_story_app/core/utils/epic_snackbar.dart';
import 'package:epic_story_app/core/widgets/dialogs/loading_dialog.dart';
import 'package:epic_story_app/core/widgets/dialogs/reward_photo_viewer_dialog.dart';
import 'package:epic_story_app/domain/entities/children_entity.dart';
import 'package:epic_story_app/domain/usecases/book_usecase.dart';
import 'package:epic_story_app/domain/usecases/children_usecase.dart';
import 'package:epic_story_app/domain/usecases/flashcard_usecase.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gal/gal.dart';
import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart';
class MainController extends GetxController {
final FirebaseService firebaseService = Get.find<FirebaseService>();
final authService = Get.find<GoogleAuthService>();
final isarService = Get.find<IsarService>();
final RxBool _isLoadingPage = false.obs;
int _loadingDialogToken = 0;
bool get isLoadingPage => _isLoadingPage.value;
var childrenData = Rxn<ChildrenEntity>();
final ChildrenUsecase childrenUsecase;
final FlashCardUsecase flashCardUsecase;
final BookUsecase bookUsecase;
MainController({
required this.childrenUsecase,
required this.flashCardUsecase,
required this.bookUsecase,
});
@override
void onInit() {
super.onInit();
// Safety net for hot-restart/resume where a modal barrier can remain visible.
WidgetsBinding.instance.addPostFrameCallback((_) {
hideLoadingPage(force: true);
});
}
Future<bool> isConnectedToInternet() async {
try {
var connectivityResult = await Connectivity().checkConnectivity();
// Check if device has network connectivity
if (connectivityResult.contains(ConnectivityResult.mobile) ||
connectivityResult.contains(ConnectivityResult.wifi)) {
// Additional check: try to reach a reliable server
try {
return true;
// final result = await InternetAddress.lookup('google.com');
// if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
// EpicLog.debug('✅ Internet connection: Available');
// return true;
// }
} catch (e) {
EpicLog.debug('❌ Internet connection: DNS lookup failed - $e');
return false;
}
} else {
EpicLog.debug('❌ Internet connection: No network connectivity');
return false;
}
} catch (e) {
EpicLog.debug('❌ Internet connection: Error checking connectivity - $e');
return false;
}
}
void showLoadingPage() {
if (!_isLoadingPage.value && !(Get.isDialogOpen ?? false)) {
_isLoadingPage.value = true;
final token = ++_loadingDialogToken;
try {
Get.dialog(
const UnconstrainedBox(
child: Material(
color: Colors.transparent,
child: LoadingDialog(),
),
),
barrierDismissible: false, // Prevent dismissing by tapping outside
barrierColor: Colors.black.withOpacity(0.5),
useSafeArea: true,
);
Future.delayed(
const Duration(seconds: 10),
() {
if (_isLoadingPage.value && token == _loadingDialogToken) {
hideLoadingPage();
}
},
);
} catch (e) {
_isLoadingPage.value = false;
EpicLog.debug('Error showing loading dialog: $e');
}
}
}
void hideLoadingPage({bool force = false}) {
if (!_isLoadingPage.value && !force) return;
_isLoadingPage.value = false;
if (Get.isDialogOpen ?? false) {
try {
if (Get.isSnackbarOpen) {
Get.closeCurrentSnackbar();
}
Get.back();
} catch (e) {
EpicLog.debug('Error closing loading dialog: $e');
}
}
}
void showRewardPhotoViewer({
required List<String?> imageUrls,
int initialIndex = 0,
}) {
final urls = imageUrls
.whereType<String>()
.map((url) => url.trim())
.where((url) => url.isNotEmpty)
.toList(growable: false);
if (urls.isEmpty) {
EpicSnackBar.showWarningSnackBar(
'Peringatan',
'Gambar reward tidak tersedia.',
);
return;
}
final safeInitialIndex = initialIndex.clamp(0, urls.length - 1).toInt();
Get.dialog(
RewardPhotoViewerDialog(
imageUrls: urls,
initialIndex: safeInitialIndex,
onDownload: downloadRewardImage,
),
barrierDismissible: true,
barrierColor: Colors.black.withOpacity(0.85),
useSafeArea: true,
);
}
Future<void> downloadRewardImage(String imageUrl) async {
if (!GetPlatform.isAndroid && !GetPlatform.isIOS) {
EpicSnackBar.showWarningSnackBar(
'Tidak Didukung',
'Fitur simpan gambar hanya tersedia di Android/iOS.',
);
return;
}
try {
EpicSnackBar.showPendingSnackBar(
'Download',
'Menyimpan gambar reward ke perangkat...',
showLog: false,
);
final response = await Dio().get<List<int>>(
imageUrl,
options: Options(responseType: ResponseType.bytes),
);
final bytes = response.data;
if (bytes == null || bytes.isEmpty) {
EpicSnackBar.showErrorSnackBar(
'Gagal',
'Gagal mengunduh gambar reward.',
);
return;
}
final hasAccess = await Gal.hasAccess();
if (!hasAccess) {
final isGranted = await Gal.requestAccess();
if (!isGranted) {
EpicSnackBar.showWarningSnackBar(
'Izin Ditolak',
'Aplikasi butuh izin galeri untuk menyimpan gambar reward.',
);
return;
}
}
final fileName = 'reward_${DateTime.now().millisecondsSinceEpoch}';
await Gal.putImageBytes(
Uint8List.fromList(bytes),
name: fileName,
album: 'Epic Story',
);
EpicSnackBar.showSuccessSnackBar(
'Berhasil',
'Gambar reward berhasil disimpan di galeri.',
showLog: false,
);
} on MissingPluginException catch (e, s) {
EpicLog.exception(e, s, this, 'downloadRewardImage - missing plugin');
EpicSnackBar.showWarningSnackBar(
'Perlu Restart Aplikasi',
'Plugin simpan gambar belum aktif. Tutup aplikasi lalu jalankan ulang penuh.',
);
} on GalException catch (e, s) {
EpicLog.exception(e, s, this, 'downloadRewardImage - gal exception');
if (e.type == GalExceptionType.accessDenied) {
EpicSnackBar.showWarningSnackBar(
'Izin Ditolak',
'Akses galeri ditolak. Izinkan akses galeri untuk menyimpan reward.',
);
return;
}
EpicSnackBar.showErrorSnackBar(
'Gagal',
'Tidak bisa menyimpan gambar reward. Coba lagi.',
);
} catch (e, s) {
EpicLog.exception(e, s, this, 'downloadRewardImage');
EpicSnackBar.showErrorSnackBar(
'Gagal',
'Terjadi kesalahan saat menyimpan gambar reward.',
);
}
}
Future<bool> loginGoogle({
GoogleSignInAccount? googleSignIn,
}) async {
try {
showLoadingPage();
final bool isConnected = await isConnectedToInternet();
if (!isConnected) {
EpicLog.debug(
'loginGoogle - Google Login Error: No internet connection');
EpicSnackBar.showErrorSnackBar(
"Failure", "Please check your internet connection.");
return false;
}
GoogleSignInAccount? googleUser = googleSignIn;
if (googleUser == null) {
await authService.signOut();
googleUser = await authService.signIn();
if (googleUser == null) {
EpicLog.debug(
'loginGoogle - Google Login Error: User cancelled sign in');
return false;
}
}
if (!await firebaseService.loginWithGoogle(
googleSignIn: googleUser, ignoreAnonymous: false)) {
EpicLog.debug(
'loginGoogle - Google Login Error: Firebase authentication failed');
EpicSnackBar.showErrorSnackBar(
"Failure", "Failed to authenticate with Firebase.");
}
await isarService.clearAllData();
final remoteStudent = await childrenUsecase.getChildrenRemoteModel();
childrenData.value = remoteStudent;
EpicLog.debug('loginGoogle - Google Login: Login successful');
return true;
} catch (e, s) {
EpicLog.exception(e, s, this,
'loginGoogle - Error occurred while logging in with Google');
return false;
} finally {
hideLoadingPage();
}
}
Future<void> studentLogout() async {
try {
showLoadingPage();
await Future.wait([
childrenUsecase.logout(),
firebaseService.logout(),
authService.signOut(),
isarService.clearAllData(),
]);
EpicLog.debug('✅ Logout: User logged out successfully');
} catch (e, s) {
EpicLog.exception(e, s, this, 'studentLogout');
} finally {
hideLoadingPage();
}
}
Future<void> testFetchFlashcardHistories({
bool refreshLocal = false,
}) async {
try {
EpicLog.debug('controller getFlashcardHistories');
final histories = await childrenUsecase.testGetFlashcardHistories(
refreshLocal: refreshLocal,
);
EpicLog.debug('controller getFlashcardHistories - result: $histories');
} catch (e, s) {
EpicLog.exception(e, s, this, 'testFetchFlashcardHistories');
}
}
}