import 'dart:developer'; import 'package:get/get.dart'; import 'package:onufitness/constants/api_endpoints.dart'; import 'package:onufitness/screens/echoboard/models/goal_badges_response_model.dart'; import 'package:onufitness/screens/echoboard/models/post_model.dart'; import 'package:onufitness/screens/echoboard/models/user_social_profile_response_model.dart'; import 'package:onufitness/services/api_services/base_api_services.dart'; import 'package:onufitness/utils/custom_sneakbar.dart'; class ProfileController extends GetxController { RxBool isRecentAcrivityLoading = false.obs; RxBool isLoadingMoreRecentActivity = false.obs; RxBool isProfileLoading = false.obs; RxString selectedUserId = "".obs; // Pagination variables for recent activities RxInt currentRecentActivityPage = 1.obs; RxInt recentActivityPageSize = 5.obs; RxBool hasMoreRecentActivities = true.obs; // Response models PostModel postModel = PostModel(); Rx userSocialProfile = UserSocialProfileResponseModel().obs; // Cache map for storing posts with their IDs as keys final RxMap postsCache = {}.obs; final posts = [].obs; Future fetchRecentActivity({bool loadMore = false}) async { if (loadMore) { // If already loading more or no more posts to load, return if (isLoadingMoreRecentActivity.value || !hasMoreRecentActivities.value) { return false; } isLoadingMoreRecentActivity(true); } else { // First load isRecentAcrivityLoading(true); currentRecentActivityPage.value = 1; postsCache.value = {}; postModel = PostModel(); posts.value = []; } bool ret = false; try { final url = "${ApiUrl.getRecentActivity}-$selectedUserId?PageNumber=${currentRecentActivityPage.value}&PageSize=${recentActivityPageSize.value}"; final response = await ApiBase.getRequest(extendedURL: url); log("Posts API Statuscode: ${response.statusCode}"); final fetchedModel = postModelFromJson(response.body); if (response.statusCode == 200 && fetchedModel.isSuccess == true) { final newItems = fetchedModel.data?.items ?? []; // Check if there are more items to load if (newItems.length < recentActivityPageSize.value) { hasMoreRecentActivities(false); } if (newItems.isNotEmpty) { for (var post in newItems) { if (post.postId != null) { postsCache[post.postId.toString()] = post; } } posts.addAll(newItems); // Update page number for next request currentRecentActivityPage.value++; } // Save the post model for pagination metadata if (!loadMore) { postModel = fetchedModel; } ret = true; } else { ret = false; } } catch (e) { log("Exception in fetchPosts: $e"); customSnackbar(title: "Error", message: "Failed to fetch posts."); ret = false; } finally { if (loadMore) { isLoadingMoreRecentActivity(false); } else { isRecentAcrivityLoading(false); } } return ret; } // Method to reset pagination void resetRecentActivityPagination() { currentRecentActivityPage.value = 1; hasMoreRecentActivities.value = true; } // Get Social Profile info Future getUserSocialProfileInfo() async { isProfileLoading(true); bool result = false; try { final response = await ApiBase.getRequest( extendedURL: "${ApiUrl.getSocialProfileInfo}/$selectedUserId", ); if (response.statusCode >= 200 && response.statusCode < 300) { userSocialProfile.value = userSocialProfileResponseModelFromJson( response.body, ); if (userSocialProfile.value.isSuccess == true) { log("User Social Profile fetched successfully"); result = true; } else { log( "User Social Profile API returned success=false: ${userSocialProfile.value.message}", ); result = false; } } else { log( "User Social Profile API failed with status: ${response.statusCode}", ); result = false; } } catch (e) { log("Exception in getUserSocialProfileInfo: $e"); customSnackbar(title: "Error", message: "Failed to fetch user profile."); result = false; } finally { isProfileLoading(false); } return result; } // Update post reaction in ProfileController's posts void updatePostReaction(String postId, int reactionTypeId, bool isRemove) { log("🔄 ProfileController: Updating post reaction for postId: $postId"); final postIndex = posts.indexWhere( (post) => post.postId.toString() == postId, ); if (postIndex == -1) return; final oldPost = posts[postIndex]; final currentReactions = oldPost.totalPostReactions ?? 0; final isAlreadyLiked = oldPost.postReactionTypeId != null; int updatedCount = currentReactions; if (isRemove && isAlreadyLiked) { updatedCount = (currentReactions - 1).clamp(0, double.infinity).toInt(); } else if (!isRemove && !isAlreadyLiked) { updatedCount = currentReactions + 1; } final updatedPost = SocialPostItem( postId: oldPost.postId, userId: oldPost.userId, content: oldPost.content, mediaFiles: oldPost.mediaFiles, profilePicture: oldPost.profilePicture, fullName: oldPost.fullName, userTypeName: oldPost.userTypeName, createdAt: oldPost.createdAt, totalPostComments: oldPost.totalPostComments, postReactionTypeId: isRemove ? null : reactionTypeId, totalPostReactions: updatedCount, poll: oldPost.poll, ); posts[postIndex] = updatedPost; update(); posts.refresh(); } // Update post comment count in ProfileController's posts void updatePostCommentCount(String postId, int increment) { log( "🔄 ProfileController: Updating comment count for postId: $postId by $increment", ); final postIndex = posts.indexWhere( (post) => post.postId.toString() == postId, ); if (postIndex != -1) { final currentPost = posts[postIndex]; final currentComments = currentPost.totalPostComments ?? 0; final updatedCount = (currentComments + increment).clamp(0, double.infinity).toInt(); final updatedPost = currentPost.copyWith(totalPostComments: updatedCount); posts[postIndex] = updatedPost; // Also update in cache postsCache[postId] = updatedPost; posts.refresh(); update(); log("✅ ProfileController: Updated comment count to: $updatedCount"); } } void updatePostPollResults(dynamic voteResponse) { log("🔄 ProfileController: Updating poll results"); final pollPostId = voteResponse['pollPostID']; if (pollPostId == null) return; final postIndex = posts.indexWhere( (post) => post.poll?.pollPostId == pollPostId, ); if (postIndex != -1) { final currentPost = posts[postIndex]; if (currentPost.poll == null) return; final totalPollVotes = voteResponse['totalPollVotes'] as int? ?? 0; final options = voteResponse['options'] as List? ?? []; // Create updated post with new poll data final updatedPost = SocialPostItem( postId: currentPost.postId, userId: currentPost.userId, firstName: currentPost.firstName, lastName: currentPost.lastName, fullName: currentPost.fullName, profilePicture: currentPost.profilePicture, userTypeId: currentPost.userTypeId, userTypeName: currentPost.userTypeName, coachTypeName: currentPost.coachTypeName, coachRequestStatus: currentPost.coachRequestStatus, content: currentPost.content, postTypeId: currentPost.postTypeId, postVisibilityId: currentPost.postVisibilityId, deviceTypeId: currentPost.deviceTypeId, isTribe: currentPost.isTribe, tribeId: currentPost.tribeId, createdAt: currentPost.createdAt, updateAt: currentPost.updateAt, mediaFiles: currentPost.mediaFiles, comments: currentPost.comments, totalPostComments: currentPost.totalPostComments, totalPostReactions: currentPost.totalPostReactions, postReactionTypeId: currentPost.postReactionTypeId, ); // Update poll data updatedPost.poll = Poll( pollPostId: currentPost.poll!.pollPostId, question: voteResponse['question'] ?? currentPost.poll!.question, expiryDate: voteResponse['expiryDate'] != null ? DateTime.parse(voteResponse['expiryDate']) : currentPost.poll!.expiryDate, totalPollVotes: totalPollVotes, options: [], ); // Update poll options for (var socketOption in options) { final optionMap = socketOption as Map; final originalOption = currentPost.poll!.options?.firstWhere( (opt) => opt.optionId == optionMap['optionID'], orElse: () => Option(), ); updatedPost.poll!.options!.add( Option( optionId: optionMap['optionID'], pollId: optionMap['pollID'], userId: optionMap['userID'], optionLabel: optionMap['optionLabel'] ?? originalOption?.optionLabel, totalOptionPollVotes: optionMap['totalOptionPollVotes'] ?? 0, percentageOptionPollVotes: optionMap['percentageOptionPollVotes'] ?? 0, hasUserVoted: originalOption?.hasUserVoted ?? false, ), ); } // Update in ProfileController posts[postIndex] = updatedPost; postsCache[updatedPost.postId.toString()] = updatedPost; posts.refresh(); update(); log( "✅ ProfileController: Updated poll results with total votes: $totalPollVotes", ); } } void syncPostFromEchoBoard(SocialPostItem updatedPost) { if (updatedPost.postId == null) return; final postId = updatedPost.postId.toString(); // Find and update in main posts list final postIndex = posts.indexWhere( (post) => post.postId.toString() == postId, ); if (postIndex != -1) { posts[postIndex] = updatedPost; log( "✅ ProfileController: Synced post at index $postIndex from EchoBoardController", ); } // Update in cache postsCache[postId] = updatedPost; // Force UI update posts.refresh(); update(); log("✅ ProfileController: Post $postId synced from EchoBoardController"); } //........................................................................................ //......Completed Coal Badge api call.................................................................................. //........................................................................................ Future> fetchCompletedGoalsForBadges({ required String userId, }) async { try { final response = await ApiBase.getRequest( extendedURL: "${ApiUrl.fetchCompletedGoalsForBadges}/$userId", ); if (response.statusCode == 200) { final result = completedGoalForBadgesResponseModelFromJson( response.body, ); return result.data ?? []; } else { return []; } } catch (e) { log("Error fetching completed goals for badges: $e"); return []; } } }