1399 lines
47 KiB
Dart
1399 lines
47 KiB
Dart
// 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 = <ChallengeItem>[].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<void> 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<ChallengeItem> 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<void> refreshOngoingChallenges() async {
|
|
// await fetchOngoingChallenges(isRefresh: true);
|
|
// }
|
|
|
|
// void loadMoreOngoingChallenges() {
|
|
// if (!isOngoingLoading.value && ongoingHasMoreData.value) {
|
|
// fetchOngoingChallenges();
|
|
// }
|
|
// }
|
|
|
|
// void clearOngoingSearch() {
|
|
// ongoingSearchController.clear();
|
|
// ongoingSearchText.value = "";
|
|
// }
|
|
|
|
// //----------------------------------------------------------------------------------------------------
|
|
// //......Upcoming Challenges...........................................................................
|
|
// //----------------------------------------------------------------------------------------------------
|
|
|
|
// var upcomingChallenges = <ChallengeItem>[].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<void> 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<ChallengeItem> 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 = <ChallengeItem>[].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<void> 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<ChallengeItem> 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 = <ChallengeItem>[].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<void> 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<ChallengeItem> 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<int> isJoiningChallengeLoading = <int>[].obs;
|
|
// var joinChallengeError = ''.obs;
|
|
|
|
// Future<bool> 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<String, dynamic> 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<bool> 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 = <SingleUserLeaderboard>[].obs;
|
|
// var isParticipantsLoading = false.obs;
|
|
// var participantsCurrentPage = 1;
|
|
// var participantsHasMoreData = true.obs;
|
|
// var currentChallengeId = 0;
|
|
|
|
// Future<void> 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<SingleUserLeaderboard> 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<DateTime?> selectedDate = Rx<DateTime?>(null);
|
|
|
|
// Future<void> 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 = <ChallengeItem>[].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<void> 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<void> refreshOngoingChallenges() async {
|
|
await fetchOngoingChallenges(isRefresh: true);
|
|
}
|
|
|
|
void loadMoreOngoingChallenges() {
|
|
if (!isOngoingLoading.value && ongoingHasMoreData.value) {
|
|
fetchOngoingChallenges();
|
|
}
|
|
}
|
|
|
|
void clearOngoingSearch() {
|
|
ongoingSearchController.clear();
|
|
ongoingSearchText.value = "";
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------------
|
|
//......Upcoming Challenges...........................................................................
|
|
//----------------------------------------------------------------------------------------------------
|
|
|
|
var upcomingChallenges = <ChallengeItem>[].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<void> 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 = <ChallengeItem>[].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<void> 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 = <ChallengeItem>[].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<void> 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<int> isJoiningChallengeLoading = <int>[].obs;
|
|
var joinChallengeError = ''.obs;
|
|
|
|
Future<bool> 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<String, dynamic> 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<bool> 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 = <SingleUserLeaderboard>[].obs;
|
|
var isParticipantsLoading = false.obs;
|
|
var participantsCurrentPage = 1;
|
|
var participantsHasMoreData = true.obs;
|
|
var currentChallengeId = 0;
|
|
|
|
Future<void> 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<DateTime?> selectedDate = Rx<DateTime?>(null);
|
|
|
|
Future<void> 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);
|
|
}
|
|
}
|