// import 'dart:async'; // import 'dart:convert'; // import 'package:flutter/material.dart'; // import 'package:onufitness/services/logger_service/logger_service.dart'; // import 'package:get/get.dart'; // import 'package:onufitness/constants/api_endpoints.dart'; // import 'package:onufitness/screens/rise/models/get_challenges_response_model.dart'; // import 'package:onufitness/screens/rise/models/participient_leaderboard_response_model.dart'; // import 'package:onufitness/services/api_services/base_api_services.dart'; // import 'package:onufitness/utils/custom_sneakbar.dart'; // class RiseController extends GetxController { // final logger = LoggerService(); // @override // void onInit() { // upcomingSearchController = TextEditingController(); // createdSearchController = TextEditingController(); // joinedSearchController = TextEditingController(); // inputValueController = TextEditingController(); // ongoingSearchController = TextEditingController(); // super.onInit(); // fetchOngoingChallenges(); // fetchUpcomingChallenges(); // fetchCreatedChallenges(); // fetchJoinedChallenges(); // Future.delayed(Duration(milliseconds: 900), () { // _ongoingInitialized = true; // _upcomingInitialized = true; // _createdInitialized = true; // _joinedInitialized = true; // ongoingSearchController.addListener(_onOngoingSearchChanged); // upcomingSearchController.addListener(_onUpcomingSearchChanged); // createdSearchController.addListener(_onCreatedSearchChanged); // joinedSearchController.addListener(_onJoinedSearchChanged); // }); // } // @override // void onClose() { // _ongoingSearchTimer?.cancel(); // _upcomingSearchTimer?.cancel(); // _createdSearchTimer?.cancel(); // _joinedSearchTimer?.cancel(); // ongoingSearchController.removeListener(_onOngoingSearchChanged); // upcomingSearchController.removeListener(_onUpcomingSearchChanged); // createdSearchController.removeListener(_onCreatedSearchChanged); // joinedSearchController.removeListener(_onJoinedSearchChanged); // ongoingSearchController.dispose(); // upcomingSearchController.dispose(); // createdSearchController.dispose(); // joinedSearchController.dispose(); // super.onClose(); // } // //---------------------------------------------------------------------------------------------------- // //......Ongoing Challenges............................................................................ // //---------------------------------------------------------------------------------------------------- // var ongoingChallenges = [].obs; // var isOngoingLoading = false.obs; // var ongoingCurrentPage = 1; // var ongoingHasMoreData = true.obs; // TextEditingController ongoingSearchController = TextEditingController(); // final ongoingSearchText = "".obs; // Timer? _ongoingSearchTimer; // bool _ongoingInitialized = false; // void _onOngoingSearchChanged() { // if (!_ongoingInitialized) return; // _ongoingSearchTimer?.cancel(); // _ongoingSearchTimer = Timer(Duration(milliseconds: 800), () { // ongoingSearchText.value = ongoingSearchController.text; // fetchOngoingChallenges(isRefresh: true); // }); // } // Future fetchOngoingChallenges({bool isRefresh = false}) async { // if (isRefresh) { // ongoingCurrentPage = 1; // ongoingChallenges.clear(); // ongoingHasMoreData.value = true; // } // if (isOngoingLoading.value || !ongoingHasMoreData.value) return; // isOngoingLoading.value = true; // try { // var url = // "${ApiUrl.fetchOngoingChallenges}?PageNumber=$ongoingCurrentPage&pageSize=20"; // if (ongoingSearchController.text.isNotEmpty || // ongoingSearchText.value.isNotEmpty) { // url += "&SearchType=Name&SearchValue=${ongoingSearchText.value}"; // } // final response = await ApiBase.getRequest(extendedURL: url); // if (response.statusCode == 200) { // final challengesResponse = challengesResponseModelFromJson( // response.body, // ); // if (challengesResponse.isSuccess == true && // challengesResponse.data != null) { // List newChallenges = // challengesResponse.data!.items ?? []; // if (newChallenges.isNotEmpty) { // if (isRefresh) { // ongoingChallenges.value = newChallenges; // } else { // ongoingChallenges.addAll(newChallenges); // } // ongoingCurrentPage++; // } else { // ongoingHasMoreData.value = false; // } // } else { // if (isRefresh) ongoingChallenges.clear(); // } // } else { // customSnackbar( // title: "Error", // message: "Failed to fetch ongoing challenges", // ); // } // } catch (e) { // logger.error('Error fetching ongoing challenges: $e'); // customSnackbar( // title: "Error", // message: "Failed to fetch ongoing challenges", // ); // } finally { // isOngoingLoading.value = false; // } // } // Future refreshOngoingChallenges() async { // await fetchOngoingChallenges(isRefresh: true); // } // void loadMoreOngoingChallenges() { // if (!isOngoingLoading.value && ongoingHasMoreData.value) { // fetchOngoingChallenges(); // } // } // void clearOngoingSearch() { // ongoingSearchController.clear(); // ongoingSearchText.value = ""; // } // //---------------------------------------------------------------------------------------------------- // //......Upcoming Challenges........................................................................... // //---------------------------------------------------------------------------------------------------- // var upcomingChallenges = [].obs; // var isUpcomingLoading = false.obs; // var upcomingCurrentPage = 1; // var upcomingHasMoreData = true.obs; // TextEditingController upcomingSearchController = TextEditingController(); // final upcomingSearchText = "".obs; // Timer? _upcomingSearchTimer; // bool _upcomingInitialized = false; // void _onUpcomingSearchChanged() { // if (!_upcomingInitialized) return; // _upcomingSearchTimer?.cancel(); // _upcomingSearchTimer = Timer(Duration(milliseconds: 800), () { // upcomingSearchText.value = upcomingSearchController.text; // fetchUpcomingChallenges(isRefresh: true); // }); // } // Future fetchUpcomingChallenges({bool isRefresh = false}) async { // if (isRefresh) { // upcomingCurrentPage = 1; // upcomingChallenges.clear(); // upcomingHasMoreData.value = true; // } // if (isUpcomingLoading.value || !upcomingHasMoreData.value) return; // isUpcomingLoading.value = true; // try { // var url = // "${ApiUrl.fetchUpcomingChallenges}?PageNumber=$upcomingCurrentPage&pageSize=20"; // if (upcomingSearchController.text.isNotEmpty || // upcomingSearchText.value.isNotEmpty) { // url += "&SearchType=Name&SearchValue=${upcomingSearchText.value}"; // } // final response = await ApiBase.getRequest(extendedURL: url); // if (response.statusCode == 200) { // final challengesResponse = challengesResponseModelFromJson( // response.body, // ); // if (challengesResponse.isSuccess == true && // challengesResponse.data != null) { // List newChallenges = // challengesResponse.data!.items ?? []; // if (newChallenges.isNotEmpty) { // if (isRefresh) { // upcomingChallenges.value = newChallenges; // } else { // upcomingChallenges.addAll(newChallenges); // } // upcomingCurrentPage++; // } else { // upcomingHasMoreData.value = false; // } // } else { // if (isRefresh) upcomingChallenges.clear(); // } // } else { // customSnackbar( // title: "Error", // message: "Failed to fetch upcoming challenges", // ); // } // } catch (e) { // logger.error('Error fetching upcoming challenges: $e'); // customSnackbar( // title: "Error", // message: "Failed to fetch upcoming challenges", // ); // } finally { // isUpcomingLoading.value = false; // } // } // void refreshUpcomingChallenges() async { // await fetchUpcomingChallenges(isRefresh: true); // } // void loadMoreUpcomingChallenges() { // if (!isUpcomingLoading.value && upcomingHasMoreData.value) { // fetchUpcomingChallenges(); // } // } // void clearUpcomingSearch() { // upcomingSearchController.clear(); // upcomingSearchText.value = ""; // } // //---------------------------------------------------------------------------------------------------- // //......Created Challenges............................................................................ // //---------------------------------------------------------------------------------------------------- // var createdChallenges = [].obs; // var isCreatedLoading = false.obs; // var createdCurrentPage = 1; // var createdHasMoreData = true.obs; // TextEditingController createdSearchController = TextEditingController(); // final createdSearchText = "".obs; // Timer? _createdSearchTimer; // bool _createdInitialized = false; // void _onCreatedSearchChanged() { // if (!_createdInitialized) return; // _createdSearchTimer?.cancel(); // _createdSearchTimer = Timer(Duration(milliseconds: 800), () { // createdSearchText.value = createdSearchController.text; // fetchCreatedChallenges(isRefresh: true); // }); // } // Future fetchCreatedChallenges({bool isRefresh = false}) async { // if (isRefresh) { // createdCurrentPage = 1; // createdChallenges.clear(); // createdHasMoreData.value = true; // } // if (isCreatedLoading.value || !createdHasMoreData.value) return; // isCreatedLoading.value = true; // try { // var url = // "${ApiUrl.fetchCreatedbyMeChallenges}?PageNumber=$createdCurrentPage&PageSize=10"; // if (createdSearchController.text.isNotEmpty || // createdSearchText.value.isNotEmpty) { // url += "&SearchType=Name&SearchValue=${createdSearchText.value}"; // } // final response = await ApiBase.getRequest(extendedURL: url); // if (response.statusCode == 200) { // final challengesResponse = challengesResponseModelFromJson( // response.body, // ); // if (challengesResponse.isSuccess == true && // challengesResponse.data != null) { // List newChallenges = // challengesResponse.data!.items ?? []; // if (newChallenges.isNotEmpty) { // if (isRefresh) { // createdChallenges.value = newChallenges; // } else { // createdChallenges.addAll(newChallenges); // } // createdCurrentPage++; // } else { // createdHasMoreData.value = false; // } // } else { // if (isRefresh) createdChallenges.clear(); // } // } else { // customSnackbar(title: "Error", message: "Failed to fetch challenges"); // } // } catch (e) { // logger.error('Error fetching created challenges: $e'); // customSnackbar(title: "Error", message: "Failed to fetch challenges"); // } finally { // isCreatedLoading.value = false; // } // } // void refreshCreatedChallenges() async { // await fetchCreatedChallenges(isRefresh: true); // } // void loadMoreCreatedChallenges() { // if (!isCreatedLoading.value && createdHasMoreData.value) { // fetchCreatedChallenges(); // } // } // void clearCreatedSearch() { // createdSearchController.clear(); // createdSearchText.value = ""; // } // //---------------------------------------------------------------------------------------------------- // //......Joined Challenges list............................................................................. // //---------------------------------------------------------------------------------------------------- // var joinedChallenges = [].obs; // var isJoinedLoading = false.obs; // var joinedCurrentPage = 1; // var joinedHasMoreData = true.obs; // TextEditingController joinedSearchController = TextEditingController(); // final joinedSearchText = "".obs; // Timer? _joinedSearchTimer; // bool _joinedInitialized = false; // void _onJoinedSearchChanged() { // if (!_joinedInitialized) return; // _joinedSearchTimer?.cancel(); // _joinedSearchTimer = Timer(Duration(milliseconds: 800), () { // joinedSearchText.value = joinedSearchController.text; // fetchJoinedChallenges(isRefresh: true); // }); // } // Future fetchJoinedChallenges({bool isRefresh = false}) async { // if (isRefresh) { // joinedCurrentPage = 1; // joinedChallenges.clear(); // joinedHasMoreData.value = true; // } // if (isJoinedLoading.value || !joinedHasMoreData.value) return; // isJoinedLoading.value = true; // try { // var url = // "${ApiUrl.fetchJoinedChallenges}?PageNumber=$joinedCurrentPage&PageSize=10"; // if (joinedSearchController.text.isNotEmpty || // joinedSearchText.value.isNotEmpty) { // url += "&SearchType=Name&SearchValue=${joinedSearchText.value}"; // } // final response = await ApiBase.getRequest(extendedURL: url); // if (response.statusCode == 200) { // final challengesResponse = challengesResponseModelFromJson( // response.body, // ); // if (challengesResponse.isSuccess == true && // challengesResponse.data != null) { // List newChallenges = // challengesResponse.data!.items ?? []; // if (newChallenges.isNotEmpty) { // if (isRefresh) { // joinedChallenges.value = newChallenges; // } else { // joinedChallenges.addAll(newChallenges); // } // joinedCurrentPage++; // } else { // joinedHasMoreData.value = false; // } // } else { // if (isRefresh) joinedChallenges.clear(); // } // } // } catch (e) { // logger.error('Error fetching joined challenges: $e'); // } finally { // isJoinedLoading.value = false; // } // } // void refreshJoinedChallenges() async { // await fetchJoinedChallenges(isRefresh: true); // } // void loadMoreJoinedChallenges() { // if (!isJoinedLoading.value && joinedHasMoreData.value) { // fetchJoinedChallenges(); // } // } // void clearJoinedSearch() { // joinedSearchController.clear(); // joinedSearchText.value = ""; // } // // Join a Challenge...................................................................... // RxList isJoiningChallengeLoading = [].obs; // var joinChallengeError = ''.obs; // Future joinChallenge({required int challengeID}) async { // bool ret = false; // try { // isJoiningChallengeLoading.add(challengeID); // joinChallengeError.value = ''; // final response = await ApiBase.postRequest( // extendedURL: ApiUrl.joinChallenge, // body: {"challengeID": challengeID}, // ); // if (response.statusCode == 200 || response.statusCode == 201) { // ret = true; // customSnackbar( // title: 'Success', // message: 'Successfully joined the challenge!', // duration: 1, // ); // } else { // ret = false; // Map responseData = json.decode(response.body); // String errorMessage = // responseData['message'] ?? // 'An error occurred while joining the challenge'; // joinChallengeError.value = errorMessage; // customSnackbar(title: 'Error', message: errorMessage, duration: 1); // } // } catch (e) { // logger.error('Error joining challenge: $e'); // ret = false; // String errorMessage = 'An error occurred while joining the challenge'; // joinChallengeError.value = errorMessage; // customSnackbar(title: 'Error', message: errorMessage, duration: 1); // } finally { // isJoiningChallengeLoading.remove(challengeID); // } // return ret; // } // //......Input Performance............................................................................ // TextEditingController inputValueController = TextEditingController(); // RxBool isChallengeInputLoading = false.obs; // Future inputChallengeTaskTracking({ // required int challengeID, // required int challengeTaskID, // required String metricValue, // }) async { // bool ret = false; // isChallengeInputLoading(true); // try { // final requestBody = { // "challengeID": challengeID, // "challengeTaskID": challengeTaskID, // "metricValue": double.tryParse(metricValue) ?? 0, // "trackingDate": selectedDate.value?.toUtc().toIso8601String(), // }; // final response = await ApiBase.postRequest( // extendedURL: ApiUrl.inputChallengeTask, // body: requestBody, // ); // final responseData = jsonDecode(response.body); // if (response.statusCode == 200 || response.statusCode == 201) { // ret = true; // inputValueController.clear(); // customSnackbar( // title: "Successful!", // message: // responseData['message'] ?? "Performance submitted successfully!", // duration: 1, // ); // } else { // ret = false; // customSnackbar( // title: "Failed!", // message: responseData['message'] ?? "Performance submission failed.", // ); // } // } catch (e) { // logger.error('Error submitting challenge task: $e'); // ret = false; // customSnackbar( // title: "Failed!", // message: "An error occurred. Please try again.", // ); // } finally { // isChallengeInputLoading(false); // } // return ret; // } // //---------------------------------------------------------------------------------------------------- // //......Challenge Participants (Stride Participants)................................................ // //---------------------------------------------------------------------------------------------------- // var challengeParticipants = [].obs; // var isParticipantsLoading = false.obs; // var participantsCurrentPage = 1; // var participantsHasMoreData = true.obs; // var currentChallengeId = 0; // Future fetchChallengeParticipants({ // required int challengeId, // bool isRefresh = false, // }) async { // if (isRefresh) { // participantsCurrentPage = 1; // challengeParticipants.clear(); // participantsHasMoreData.value = true; // currentChallengeId = challengeId; // } // // If it's a different challenge, reset everything // if (currentChallengeId != challengeId) { // participantsCurrentPage = 1; // challengeParticipants.clear(); // participantsHasMoreData.value = true; // currentChallengeId = challengeId; // } // if (isParticipantsLoading.value || !participantsHasMoreData.value) return; // isParticipantsLoading.value = true; // try { // var url = // "${ApiUrl.fetchLeaderBoard}/$challengeId?PageNumber=$participantsCurrentPage&PageSize=10"; // final response = await ApiBase.getRequest(extendedURL: url); // if (response.statusCode == 200) { // final participantsResponse = // leaderboardParticipentListResponseModelFromJson(response.body); // if (participantsResponse.isSuccess == true && // participantsResponse.data != null) { // List newParticipants = // participantsResponse.data!.items ?? []; // if (newParticipants.isNotEmpty) { // if (isRefresh) { // challengeParticipants.value = newParticipants; // } else { // challengeParticipants.addAll(newParticipants); // } // participantsCurrentPage++; // } else { // participantsHasMoreData.value = false; // } // } else { // if (isRefresh) challengeParticipants.clear(); // } // } else { // customSnackbar( // title: 'Error', // message: 'Failed to fetch challenge participants', // duration: 2, // ); // } // } catch (e) { // logger.error('Error fetching challenge participants: $e'); // customSnackbar( // title: 'Error', // message: 'Failed to fetch challenge participants', // duration: 2, // ); // } finally { // isParticipantsLoading.value = false; // } // } // void refreshChallengeParticipants(int challengeId) async { // await fetchChallengeParticipants(challengeId: challengeId, isRefresh: true); // } // void loadMoreChallengeParticipants() { // if (!isParticipantsLoading.value && // participantsHasMoreData.value && // currentChallengeId != 0) { // fetchChallengeParticipants(challengeId: currentChallengeId); // } // } // //---------------------------------------------------------------------------------------------------- // //......Common Logic.................................................................................. // //---------------------------------------------------------------------------------------------------- // RxString showSelectedDateToUI = "".obs; // final Rx selectedDate = Rx(null); // Future selectDate(BuildContext context) async { // final DateTime? pickedDate = await showDatePicker( // context: context, // initialDate: selectedDate.value ?? DateTime.now(), // firstDate: DateTime(2000), // lastDate: DateTime.now(), // ); // if (!context.mounted) return; // if (pickedDate != null) { // final now = DateTime.now(); // final DateTime selectedDateTime = DateTime( // pickedDate.year, // pickedDate.month, // pickedDate.day, // now.hour, // now.minute, // ); // if (selectedDateTime != selectedDate.value) { // updateSelectedDate(selectedDateTime); // } // } // } // String formatDate(DateTime date) { // return "${date.month.toString().padLeft(2, '0')}/${date.day.toString().padLeft(2, '0')}/${date.year}"; // } // void updateSelectedDate(DateTime date) { // selectedDate.value = date; // showSelectedDateToUI.value = formatDate(date); // } // } //................ Above one is working code till 11th June 2026 ................................ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:onufitness/services/logger_service/logger_service.dart'; import 'package:get/get.dart'; import 'package:onufitness/constants/api_endpoints.dart'; import 'package:onufitness/screens/rise/models/get_challenges_response_model.dart'; import 'package:onufitness/screens/rise/models/participient_leaderboard_response_model.dart'; import 'package:onufitness/services/api_services/base_api_services.dart'; import 'package:onufitness/utils/custom_sneakbar.dart'; class RiseController extends GetxController { final logger = LoggerService(); /// When set, all challenge fetches use the tribe-wise endpoints for this /// tribe. Set this to a tribe id before fetching tribe-scoped data, and /// set it back to null before fetching the normal (non-tribe) lists. int? activeTribeId; /// [baseUrl] MUST be the bare endpoint (no query string) — e.g. /// ApiUrl.fetchJoinedChallenges. Append your query params to the /// RETURNED string, never before calling this — appending first breaks /// the map lookup below and you'll silently always get the normal /// (non-tribe) URL back, no matter what activeTribeId is. /// String isTribeChallenge(String baseUrl) { if (activeTribeId == null) return baseUrl; const tribeWiseMap = { ApiUrl.fetchOngoingChallenges: '/api/Challenges/get-ongoing-challenges-tribe-wise', ApiUrl.fetchUpcomingChallenges: '/api/Challenges/get-upcoming-challenges-tribe-wise', ApiUrl.fetchJoinedChallenges: '/api/Challenges/get-joined-challenges-tribe-wise', ApiUrl.fetchCreatedbyMeChallenges: '/api/Challenges/get-created-challenges-tribe-wise', }; final tribeUrl = tribeWiseMap[baseUrl]; if (tribeUrl == null) { log("No tribe-wise mapping for: $baseUrl"); return baseUrl; } final result = '$tribeUrl/$activeTribeId'; log("Tribe-specific URL: $result"); return result; } @override void onInit() { upcomingSearchController = TextEditingController(); createdSearchController = TextEditingController(); joinedSearchController = TextEditingController(); inputValueController = TextEditingController(); ongoingSearchController = TextEditingController(); super.onInit(); fetchOngoingChallenges(); fetchUpcomingChallenges(); fetchCreatedChallenges(); fetchJoinedChallenges(); Future.delayed(const Duration(milliseconds: 900), () { _ongoingInitialized = true; _upcomingInitialized = true; _createdInitialized = true; _joinedInitialized = true; ongoingSearchController.addListener(_onOngoingSearchChanged); upcomingSearchController.addListener(_onUpcomingSearchChanged); createdSearchController.addListener(_onCreatedSearchChanged); joinedSearchController.addListener(_onJoinedSearchChanged); }); } @override void onClose() { _ongoingSearchTimer?.cancel(); _upcomingSearchTimer?.cancel(); _createdSearchTimer?.cancel(); _joinedSearchTimer?.cancel(); ongoingSearchController.removeListener(_onOngoingSearchChanged); upcomingSearchController.removeListener(_onUpcomingSearchChanged); createdSearchController.removeListener(_onCreatedSearchChanged); joinedSearchController.removeListener(_onJoinedSearchChanged); ongoingSearchController.dispose(); upcomingSearchController.dispose(); createdSearchController.dispose(); joinedSearchController.dispose(); super.onClose(); } //---------------------------------------------------------------------------------------------------- //......Ongoing Challenges............................................................................ //---------------------------------------------------------------------------------------------------- var ongoingChallenges = [].obs; var isOngoingLoading = false.obs; var ongoingCurrentPage = 1; var ongoingHasMoreData = true.obs; TextEditingController ongoingSearchController = TextEditingController(); final ongoingSearchText = "".obs; Timer? _ongoingSearchTimer; bool _ongoingInitialized = false; int _ongoingRequestId = 0; void _onOngoingSearchChanged() { if (!_ongoingInitialized) return; _ongoingSearchTimer?.cancel(); _ongoingSearchTimer = Timer(const Duration(milliseconds: 800), () { ongoingSearchText.value = ongoingSearchController.text; fetchOngoingChallenges(isRefresh: true); }); } Future fetchOngoingChallenges({bool isRefresh = false}) async { if (isRefresh) { ongoingCurrentPage = 1; ongoingChallenges.clear(); ongoingHasMoreData.value = true; } else { if (isOngoingLoading.value || !ongoingHasMoreData.value) return; } final requestId = ++_ongoingRequestId; // invalidates any in-flight request isOngoingLoading.value = true; try { final baseUrl = isTribeChallenge(ApiUrl.fetchOngoingChallenges); var url = "$baseUrl?PageNumber=$ongoingCurrentPage&pageSize=20"; if (ongoingSearchController.text.isNotEmpty || ongoingSearchText.value.isNotEmpty) { url += "&SearchType=Name&SearchValue=${ongoingSearchText.value}"; } log("url----->: $url"); final response = await ApiBase.getRequest(extendedURL: url); log("statusCode----->: ${response.statusCode}"); if (requestId != _ongoingRequestId) return; // stale response, drop it if (response.statusCode == 200) { final challengesResponse = challengesResponseModelFromJson( response.body, ); if (challengesResponse.isSuccess == true && challengesResponse.data != null) { final newChallenges = challengesResponse.data!.items ?? []; if (newChallenges.isNotEmpty) { if (isRefresh) { ongoingChallenges.value = newChallenges; } else { ongoingChallenges.addAll(newChallenges); } ongoingCurrentPage++; } else { ongoingHasMoreData.value = false; } } else { if (isRefresh) ongoingChallenges.clear(); } } else { customSnackbar( title: "Error", message: "Failed to fetch ongoing challenges", ); } } catch (e) { if (requestId != _ongoingRequestId) return; logger.error('Error fetching ongoing challenges: $e'); customSnackbar( title: "Error", message: "Failed to fetch ongoing challenges", ); } finally { if (requestId == _ongoingRequestId) { isOngoingLoading.value = false; } } } Future refreshOngoingChallenges() async { await fetchOngoingChallenges(isRefresh: true); } void loadMoreOngoingChallenges() { if (!isOngoingLoading.value && ongoingHasMoreData.value) { fetchOngoingChallenges(); } } void clearOngoingSearch() { ongoingSearchController.clear(); ongoingSearchText.value = ""; } //---------------------------------------------------------------------------------------------------- //......Upcoming Challenges........................................................................... //---------------------------------------------------------------------------------------------------- var upcomingChallenges = [].obs; var isUpcomingLoading = false.obs; var upcomingCurrentPage = 1; var upcomingHasMoreData = true.obs; TextEditingController upcomingSearchController = TextEditingController(); final upcomingSearchText = "".obs; Timer? _upcomingSearchTimer; bool _upcomingInitialized = false; int _upcomingRequestId = 0; void _onUpcomingSearchChanged() { if (!_upcomingInitialized) return; _upcomingSearchTimer?.cancel(); _upcomingSearchTimer = Timer(const Duration(milliseconds: 800), () { upcomingSearchText.value = upcomingSearchController.text; fetchUpcomingChallenges(isRefresh: true); }); } Future fetchUpcomingChallenges({bool isRefresh = false}) async { if (isRefresh) { upcomingCurrentPage = 1; upcomingChallenges.clear(); upcomingHasMoreData.value = true; } else { if (isUpcomingLoading.value || !upcomingHasMoreData.value) return; } final requestId = ++_upcomingRequestId; isUpcomingLoading.value = true; try { final baseUrl = isTribeChallenge(ApiUrl.fetchUpcomingChallenges); var url = "$baseUrl?PageNumber=$upcomingCurrentPage&pageSize=20"; if (upcomingSearchController.text.isNotEmpty || upcomingSearchText.value.isNotEmpty) { url += "&SearchType=Name&SearchValue=${upcomingSearchText.value}"; } log("url----->: $url"); final response = await ApiBase.getRequest(extendedURL: url); if (requestId != _upcomingRequestId) return; if (response.statusCode == 200) { final challengesResponse = challengesResponseModelFromJson( response.body, ); if (challengesResponse.isSuccess == true && challengesResponse.data != null) { final newChallenges = challengesResponse.data!.items ?? []; if (newChallenges.isNotEmpty) { if (isRefresh) { upcomingChallenges.value = newChallenges; } else { upcomingChallenges.addAll(newChallenges); } upcomingCurrentPage++; } else { upcomingHasMoreData.value = false; } } else { if (isRefresh) upcomingChallenges.clear(); } } else { customSnackbar( title: "Error", message: "Failed to fetch upcoming challenges", ); } } catch (e) { if (requestId != _upcomingRequestId) return; logger.error('Error fetching upcoming challenges: $e'); customSnackbar( title: "Error", message: "Failed to fetch upcoming challenges", ); } finally { if (requestId == _upcomingRequestId) { isUpcomingLoading.value = false; } } } void refreshUpcomingChallenges() async { await fetchUpcomingChallenges(isRefresh: true); } void loadMoreUpcomingChallenges() { if (!isUpcomingLoading.value && upcomingHasMoreData.value) { fetchUpcomingChallenges(); } } void clearUpcomingSearch() { upcomingSearchController.clear(); upcomingSearchText.value = ""; } //---------------------------------------------------------------------------------------------------- //......Created Challenges............................................................................ //---------------------------------------------------------------------------------------------------- var createdChallenges = [].obs; var isCreatedLoading = false.obs; var createdCurrentPage = 1; var createdHasMoreData = true.obs; TextEditingController createdSearchController = TextEditingController(); final createdSearchText = "".obs; Timer? _createdSearchTimer; bool _createdInitialized = false; int _createdRequestId = 0; void _onCreatedSearchChanged() { if (!_createdInitialized) return; _createdSearchTimer?.cancel(); _createdSearchTimer = Timer(const Duration(milliseconds: 800), () { createdSearchText.value = createdSearchController.text; fetchCreatedChallenges(isRefresh: true); }); } Future fetchCreatedChallenges({bool isRefresh = false}) async { if (isRefresh) { createdCurrentPage = 1; createdChallenges.clear(); createdHasMoreData.value = true; } else { if (isCreatedLoading.value || !createdHasMoreData.value) return; } final requestId = ++_createdRequestId; isCreatedLoading.value = true; try { final baseUrl = isTribeChallenge(ApiUrl.fetchCreatedbyMeChallenges); var url = "$baseUrl?PageNumber=$createdCurrentPage&PageSize=10"; if (createdSearchController.text.isNotEmpty || createdSearchText.value.isNotEmpty) { url += "&SearchType=Name&SearchValue=${createdSearchText.value}"; } log("url----->: $url"); final response = await ApiBase.getRequest(extendedURL: url); if (requestId != _createdRequestId) return; if (response.statusCode == 200) { final challengesResponse = challengesResponseModelFromJson( response.body, ); if (challengesResponse.isSuccess == true && challengesResponse.data != null) { final newChallenges = challengesResponse.data!.items ?? []; if (newChallenges.isNotEmpty) { if (isRefresh) { createdChallenges.value = newChallenges; } else { createdChallenges.addAll(newChallenges); } createdCurrentPage++; } else { createdHasMoreData.value = false; } } else { if (isRefresh) createdChallenges.clear(); } } else { customSnackbar(title: "Error", message: "Failed to fetch challenges"); } } catch (e) { if (requestId != _createdRequestId) return; logger.error('Error fetching created challenges: $e'); customSnackbar(title: "Error", message: "Failed to fetch challenges"); } finally { if (requestId == _createdRequestId) { isCreatedLoading.value = false; } } } void refreshCreatedChallenges() async { await fetchCreatedChallenges(isRefresh: true); } void loadMoreCreatedChallenges() { if (!isCreatedLoading.value && createdHasMoreData.value) { fetchCreatedChallenges(); } } void clearCreatedSearch() { createdSearchController.clear(); createdSearchText.value = ""; } //---------------------------------------------------------------------------------------------------- //......Joined Challenges............................................................................. //---------------------------------------------------------------------------------------------------- var joinedChallenges = [].obs; var isJoinedLoading = false.obs; var joinedCurrentPage = 1; var joinedHasMoreData = true.obs; TextEditingController joinedSearchController = TextEditingController(); final joinedSearchText = "".obs; Timer? _joinedSearchTimer; bool _joinedInitialized = false; int _joinedRequestId = 0; void _onJoinedSearchChanged() { if (!_joinedInitialized) return; _joinedSearchTimer?.cancel(); _joinedSearchTimer = Timer(const Duration(milliseconds: 800), () { joinedSearchText.value = joinedSearchController.text; fetchJoinedChallenges(isRefresh: true); }); } Future fetchJoinedChallenges({bool isRefresh = false}) async { if (isRefresh) { joinedCurrentPage = 1; joinedChallenges.clear(); joinedHasMoreData.value = true; } else { if (isJoinedLoading.value || !joinedHasMoreData.value) return; } final requestId = ++_joinedRequestId; isJoinedLoading.value = true; try { final baseUrl = isTribeChallenge(ApiUrl.fetchJoinedChallenges); var url = "$baseUrl?PageNumber=$joinedCurrentPage&PageSize=10"; if (joinedSearchController.text.isNotEmpty || joinedSearchText.value.isNotEmpty) { url += "&SearchType=Name&SearchValue=${joinedSearchText.value}"; } log("url----->: $url"); final response = await ApiBase.getRequest(extendedURL: url); if (requestId != _joinedRequestId) return; if (response.statusCode == 200) { final challengesResponse = challengesResponseModelFromJson( response.body, ); if (challengesResponse.isSuccess == true && challengesResponse.data != null) { final newChallenges = challengesResponse.data!.items ?? []; if (newChallenges.isNotEmpty) { if (isRefresh) { joinedChallenges.value = newChallenges; } else { joinedChallenges.addAll(newChallenges); } joinedCurrentPage++; } else { joinedHasMoreData.value = false; } } else { if (isRefresh) joinedChallenges.clear(); } } } catch (e) { if (requestId != _joinedRequestId) return; logger.error('Error fetching joined challenges: $e'); } finally { if (requestId == _joinedRequestId) { isJoinedLoading.value = false; } } } void refreshJoinedChallenges() async { await fetchJoinedChallenges(isRefresh: true); } void loadMoreJoinedChallenges() { if (!isJoinedLoading.value && joinedHasMoreData.value) { fetchJoinedChallenges(); } } void clearJoinedSearch() { joinedSearchController.clear(); joinedSearchText.value = ""; } // Join a Challenge...................................................................... RxList isJoiningChallengeLoading = [].obs; var joinChallengeError = ''.obs; Future joinChallenge({required int challengeID}) async { bool ret = false; try { isJoiningChallengeLoading.add(challengeID); joinChallengeError.value = ''; final response = await ApiBase.postRequest( extendedURL: ApiUrl.joinChallenge, body: {"challengeID": challengeID}, ); if (response.statusCode == 200 || response.statusCode == 201) { ret = true; customSnackbar( title: 'Success', message: 'Successfully joined the challenge!', duration: 1, ); } else { ret = false; Map responseData = json.decode(response.body); String errorMessage = responseData['message'] ?? 'An error occurred while joining the challenge'; joinChallengeError.value = errorMessage; customSnackbar(title: 'Error', message: errorMessage, duration: 1); } } catch (e) { logger.error('Error joining challenge: $e'); ret = false; const errorMessage = 'An error occurred while joining the challenge'; joinChallengeError.value = errorMessage; customSnackbar(title: 'Error', message: errorMessage, duration: 1); } finally { isJoiningChallengeLoading.remove(challengeID); } return ret; } //......Input Performance............................................................................ TextEditingController inputValueController = TextEditingController(); RxBool isChallengeInputLoading = false.obs; Future inputChallengeTaskTracking({ required int challengeID, required int challengeTaskID, required String metricValue, }) async { bool ret = false; isChallengeInputLoading(true); try { final requestBody = { "challengeID": challengeID, "challengeTaskID": challengeTaskID, "metricValue": double.tryParse(metricValue) ?? 0, "trackingDate": selectedDate.value?.toUtc().toIso8601String(), }; final response = await ApiBase.postRequest( extendedURL: ApiUrl.inputChallengeTask, body: requestBody, ); final responseData = jsonDecode(response.body); if (response.statusCode == 200 || response.statusCode == 201) { ret = true; inputValueController.clear(); customSnackbar( title: "Successful!", message: responseData['message'] ?? "Performance submitted successfully!", duration: 1, ); } else { ret = false; customSnackbar( title: "Failed!", message: responseData['message'] ?? "Performance submission failed.", ); } } catch (e) { logger.error('Error submitting challenge task: $e'); ret = false; customSnackbar( title: "Failed!", message: "An error occurred. Please try again.", ); } finally { isChallengeInputLoading(false); } return ret; } //---------------------------------------------------------------------------------------------------- //......Challenge Participants........................................................................ //---------------------------------------------------------------------------------------------------- var challengeParticipants = [].obs; var isParticipantsLoading = false.obs; var participantsCurrentPage = 1; var participantsHasMoreData = true.obs; var currentChallengeId = 0; Future fetchChallengeParticipants({ required int challengeId, bool isRefresh = false, }) async { if (isRefresh) { participantsCurrentPage = 1; challengeParticipants.clear(); participantsHasMoreData.value = true; currentChallengeId = challengeId; } if (currentChallengeId != challengeId) { participantsCurrentPage = 1; challengeParticipants.clear(); participantsHasMoreData.value = true; currentChallengeId = challengeId; } if (isParticipantsLoading.value || !participantsHasMoreData.value) return; isParticipantsLoading.value = true; try { var url = "${ApiUrl.fetchLeaderBoard}/$challengeId?PageNumber=$participantsCurrentPage&PageSize=10"; final response = await ApiBase.getRequest(extendedURL: url); if (response.statusCode == 200) { final participantsResponse = leaderboardParticipentListResponseModelFromJson(response.body); if (participantsResponse.isSuccess == true && participantsResponse.data != null) { final newParticipants = participantsResponse.data!.items ?? []; if (newParticipants.isNotEmpty) { if (isRefresh) { challengeParticipants.value = newParticipants; } else { challengeParticipants.addAll(newParticipants); } participantsCurrentPage++; } else { participantsHasMoreData.value = false; } } else { if (isRefresh) challengeParticipants.clear(); } } else { customSnackbar( title: 'Error', message: 'Failed to fetch challenge participants', duration: 2, ); } } catch (e) { logger.error('Error fetching challenge participants: $e'); customSnackbar( title: 'Error', message: 'Failed to fetch challenge participants', duration: 2, ); } finally { isParticipantsLoading.value = false; } } void refreshChallengeParticipants(int challengeId) async { await fetchChallengeParticipants(challengeId: challengeId, isRefresh: true); } void loadMoreChallengeParticipants() { if (!isParticipantsLoading.value && participantsHasMoreData.value && currentChallengeId != 0) { fetchChallengeParticipants(challengeId: currentChallengeId); } } //---------------------------------------------------------------------------------------------------- //......Common Logic.................................................................................. //---------------------------------------------------------------------------------------------------- RxString showSelectedDateToUI = "".obs; final Rx selectedDate = Rx(null); Future selectDate(BuildContext context) async { final DateTime? pickedDate = await showDatePicker( context: context, initialDate: selectedDate.value ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime.now(), ); if (!context.mounted) return; if (pickedDate != null) { final now = DateTime.now(); final DateTime selectedDateTime = DateTime( pickedDate.year, pickedDate.month, pickedDate.day, now.hour, now.minute, ); if (selectedDateTime != selectedDate.value) { updateSelectedDate(selectedDateTime); } } } String formatDate(DateTime date) { return "${date.month.toString().padLeft(2, '0')}/${date.day.toString().padLeft(2, '0')}/${date.year}"; } void updateSelectedDate(DateTime date) { selectedDate.value = date; showSelectedDateToUI.value = formatDate(date); } }