import 'dart:async'; import 'dart:developer'; import 'package:absensi_qr/models/academic_period_model.dart'; import 'package:absensi_qr/models/user/student.dart'; import 'package:absensi_qr/models/user/teacher.dart'; import 'package:absensi_qr/models/user/user.dart'; import 'package:absensi_qr/services/endpoint_service.dart'; import 'package:absensi_qr/utils/app_util.dart'; import 'package:flutter/material.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; class MainController extends GetxController { // firebase messaging instance final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; // academic periods active period final Rx activeAcademicPeriod = Rx(null); // data user and profile. final Rx userData = Rx(null); final Rx studentData = Rx(null); final Rx teacherData = Rx(null); // refresh trigger obx final RxInt refreshHomeStudent = 0.obs; final RxInt triggerUpdateProfile = 0.obs; final RxInt refreshPermission = 0.obs; final RxInt refreshHistory = 0.obs; // connectivity internet status check final RxList _connectionStatus = [ConnectivityResult.none].obs; final RxBool hasInternetConnection = true.obs; final Connectivity _connectivity = Connectivity(); late StreamSubscription> _connectivitySubscription; late StreamSubscription _internetConnectionSubscription; // service for notification // testing button for subscribe and unsubscribe to notifications Future subscribeToNotifications(String topic) async { try { // ask permission on iOS/macOS; Android auto-grants await _firebaseMessaging.requestPermission(); // ensure token exists (useful for debugging) await _firebaseMessaging.getToken(); await _firebaseMessaging.subscribeToTopic(topic); log('Subscribed to $topic'); } catch (e) { log('Subscribe failed: $e'); } } // subscribe from list of topics Future subscribeToMultipleTopics(List topics) async { for (var topic in topics) { await subscribeToNotifications(topic); } } // unsubscribe from list of topics Future unsubscribeFromMultipleTopics(List topics) async { for (var topic in topics) { await unsubscribeFromNotifications(topic); } } // unsubscribe from notifications Future unsubscribeFromNotifications(String topic) async { try { await _firebaseMessaging.unsubscribeFromTopic(topic); log('Unsubscribed from $topic'); } catch (e) { log('Unsubscribe failed: $e'); } } // logout student Future logout() async { // clear all topic subscriptions (if needed) // get unsubscribe topic based on user data. List userTopicSubscribe = []; if (userData.value?.topicSubscribe != null && userData.value!.topicSubscribe != null && userData.value!.topicSubscribe != '') { // split topics by comma and trim whitespace userTopicSubscribe = userData.value!.topicSubscribe! .split(',') .map((topic) => topic.trim()) .toList(); } // unsubscribe from all user topics await unsubscribeFromMultipleTopics(userTopicSubscribe); await Get.find().clearAuthState(); // clear user data userData.value = null; studentData.value = null; teacherData.value = null; // refresh token to invalidate old subscriptions await refreshToken(); } // refresh token for resetting token when user logout Future refreshToken() async { try { await _firebaseMessaging.deleteToken(); String? newToken = await _firebaseMessaging.getToken(); return newToken ?? ''; } catch (e) { log('Failed to refresh token: $e'); return ''; } } @override void onInit() { // TODO: implement onInit super.onInit(); // initialize current connectivity initConnectivity(); // listen to connectivity changes _connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus); // listen to internet connection changes _internetConnectionSubscription = InternetConnectionChecker().onStatusChange.listen(_updateInternetStatus); } @override void onClose() { _connectivitySubscription.cancel(); _internetConnectionSubscription.cancel(); super.onClose(); } // Platform messages are asynchronous, so we initialize in an async method. Future initConnectivity() async { late List result; try { result = await _connectivity.checkConnectivity(); } on PlatformException catch (e) { log("Couldn't check connectivity status", error: e); return; } return _updateConnectionStatus(result); } Future _updateConnectionStatus(List result) async { _connectionStatus.assignAll(result.cast()); } // check actual internet connection (real connectivity test) Future _updateInternetStatus(InternetConnectionStatus status) async { hasInternetConnection.value = (status == InternetConnectionStatus.connected); if (!hasInternetConnection.value) { AppUtil.showGetSnackBar( 'Koneksi Terputus', 'Tidak ada koneksi internet. Beberapa fitur mungkin tidak berfungsi.', isError: true, ); } else { AppUtil.showGetSnackBar( 'Koneksi Tersambung', 'Koneksi internet tersedia.', ); } } }