import 'dart:convert'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:onufitness/constants/api_endpoints.dart'; import 'package:onufitness/screens/echoboard/models/get_all_tribr_list_with_members_response_model.dart'; import 'package:onufitness/screens/echoboard/models/get_user_list_for_connection_request_response_model.dart'; import 'package:onufitness/screens/echoboard/models/tribe/single_tribe_details_response_model.dart'; import 'package:onufitness/services/api_services/base_api_services.dart'; import 'package:onufitness/utils/custom_sneakbar.dart'; class SocialConnectionController extends GetxController { @override void onInit() { searchController = TextEditingController(); tribeNameController = TextEditingController(); tribeSearchController = TextEditingController(); super.onInit(); } TextEditingController searchController = TextEditingController(); TextEditingController tribeSearchController = TextEditingController(); RxBool isLoading = false.obs; RxString searchValue = ''.obs; RxString tribeSearchValue = ''.obs; RxInt currentPage = 1.obs; RxInt tribeCurrentPage = 1.obs; final int pageSize = 20; final int tribePageSize = 20; RxString errorMessage = ''.obs; // Separate data models for each tab to prevent data mixing Rx invitationsData = Rx(null); Rx friendsData = Rx(null); Rx suggestedUsersData = Rx(null); Rx allUsersData = Rx(null); Rx tribeMemberData = Rx(null); // Tribe data Rx tribesData = Rx(null); RxBool isTribesLoading = false.obs; RxBool isTribePaginationLoading = false.obs; RxBool hasMoreTribes = true.obs; // This is a computed property to get the correct data based on current tab Rx get usersData { if (currentTab.value == "Invitations") { return invitationsData; } else if (currentTab.value == "Friends") { return friendsData; } else if (currentTab.value == "Suggested") { return suggestedUsersData; } else if (currentTab.value == "Pending") { return allUsersData; } else if (currentTab.value == "TribeMembers") { return tribeMemberData; } // Fallback (should never happen in this case) return Rx(null); } void clearCurrentTabData() { if (currentTab.value == "Invitations") { invitationsData.value = null; } else if (currentTab.value == "Friends") { friendsData.value = null; } else if (currentTab.value == "Suggested") { suggestedUsersData.value = null; } else if (currentTab.value == "Pending") { allUsersData.value = null; } else if (currentTab.value == "TribeMembers") { tribeMemberData.value = null; } } void clearAllTabData() { invitationsData.value = null; friendsData.value = null; suggestedUsersData.value = null; allUsersData.value = null; tribeMemberData.value = null; } // Track the current tab RxString currentTab = "Invitations".obs; // Check if user is member of tribe API call............................................................... RxBool isCheckingTribeMembership = false.obs; Future checkUserInTribe({required int tribeId}) async { isCheckingTribeMembership(true); bool isMember = false; try { String extendedURL = "/api/Socials/check-user-in-tribe/$tribeId"; var response = await ApiBase.getRequest(extendedURL: extendedURL); if (response.statusCode == 200) { // Parse the response final parsedData = jsonDecode(response.body); isMember = parsedData['data'] ?? true; } else if (response.statusCode == 400) { // User is not a member isMember = false; Future.delayed(Duration(milliseconds: 300), () { customSnackbar( title: "Access Limited", message: "You're not a member of the tribe this post belongs to.", duration: 3, ); }); } else { Future.delayed(Duration(milliseconds: 300), () { customSnackbar( title: "Access Limited", message: "You're not a member of the tribe this post belongs to.", duration: 3, ); }); isMember = false; } } catch (e) { Future.delayed(Duration(milliseconds: 300), () { customSnackbar( title: "Error", message: "Failed to verify tribe membership. Please try again.", duration: 2, ); }); isMember = false; } isCheckingTribeMembership(false); return isMember; } // Get Single tribes with tribe ID.................................................................................................. RxBool isSingleTribeLoading = false.obs; Rx singleTribeDetails = Rx(null); Future getSingleTribeDetails({required int tribeId}) async { isSingleTribeLoading.value = true; bool ret = false; try { String extendedURL = "/api/Socials/get-tribe-details-with-member-count/$tribeId"; var response = await ApiBase.getRequest(extendedURL: extendedURL); if (response.statusCode == 200) { final parsedData = singleTribeDetailsResponseModelFromJson( response.body, ); singleTribeDetails.value = parsedData; ret = true; } else { ret = false; } } catch (e) { ret = false; } isSingleTribeLoading.value = false; return ret; } // Get all tribes with members.................................................................................................. Future getAllTribesWithMembers({bool refresh = false}) async { if (refresh) { tribeCurrentPage.value = 1; hasMoreTribes.value = true; isTribesLoading.value = true; } else { if (!hasMoreTribes.value || isTribePaginationLoading.value) { return false; } isTribePaginationLoading.value = true; } bool ret = false; try { String extendedURL = "/api/Socials/get-tribes-by-memberID?PageNumber=${tribeCurrentPage.value}&PageSize=$tribePageSize"; if (tribeSearchValue.value.isNotEmpty) { extendedURL += "&SearchType=name&SearchValue=${tribeSearchValue.value}"; } var response = await ApiBase.getRequest(extendedURL: extendedURL); if (response.statusCode == 200) { final parsedData = tribeListWithMembersResponseModelFromJson( response.body, ); if (refresh || tribeCurrentPage.value == 1) { tribesData.value = parsedData; } else { // Append to existing data for pagination if (tribesData.value?.data?.items != null && parsedData.data?.items != null) { tribesData.value!.data!.items!.addAll(parsedData.data!.items!); tribesData.value!.data!.totalCount = parsedData.data!.totalCount; tribesData.refresh(); } else { tribesData.value = parsedData; } } // Check if there are more pages final totalCount = parsedData.data?.totalCount ?? 0; final currentItemCount = tribesData.value?.data?.items?.length ?? 0; hasMoreTribes.value = currentItemCount < totalCount; if (hasMoreTribes.value) { tribeCurrentPage.value++; } ret = true; } else { errorMessage('Failed to load tribes: ${response.statusCode}'); ret = false; } } catch (e) { errorMessage('Error: ${e.toString()}'); ret = false; } isTribesLoading.value = false; isTribePaginationLoading.value = false; return ret; } // Clear tribe search void clearTribeSearch() { tribeSearchValue.value = ''; tribeSearchController.clear(); getAllTribesWithMembers(refresh: true); } // Get all user and Search User by name Future searchUsers({int? tribeId, bool clearOldData = true}) async { if (clearOldData) { clearCurrentTabData(); } isLoading(true); errorMessage(''); bool ret = false; try { String extendedURL = ""; if (currentTab.value == "Invitations") { extendedURL = "${ApiUrl.getInvitationUserlist}?PageNumber=${currentPage.value}&PageSize=$pageSize&SearchType=name&SearchValue=${searchValue.value}"; } if (currentTab.value == "Friends") { extendedURL = "${ApiUrl.getFriendsList}?PageNumber=${currentPage.value}&PageSize=$pageSize&SearchType=name&SearchValue=${searchValue.value}"; } if (currentTab.value == "Suggested") { extendedURL = "${ApiUrl.getSuggestedUserConnection}?PageNumber=${currentPage.value}&PageSize=$pageSize"; } if (currentTab.value == "Pending") { if (searchValue.value != "" || searchController.text.isNotEmpty) { //For showing data from all users extendedURL = "${ApiUrl.getSocialUsers}?PageNumber=${currentPage.value}&PageSize=$pageSize&SearchType=name&SearchValue=${searchValue.value}"; } else { extendedURL = "${ApiUrl.getSocialUsers}?connectionStatus=Pending&PageNumber=${currentPage.value}&PageSize=$pageSize"; } } if (currentTab.value == "TribeMembers") { extendedURL = "${ApiUrl.getTribeMemberDetailsByID}/$tribeId?PageNumber=${currentPage.value}&PageSize=$pageSize&SearchType=name&SearchValue=${searchValue.value}"; } var response = await ApiBase.getRequest(extendedURL: extendedURL); if (response.statusCode == 200) { final parsedData = getAllUsersForSendConnectionModelFromJson( response.body, ); // Update the appropriate data model if (currentTab.value == "Invitations") { invitationsData.value = parsedData; } else if (currentTab.value == "Friends") { friendsData.value = parsedData; } else if (currentTab.value == "Suggested") { suggestedUsersData.value = parsedData; } else if (currentTab.value == "Pending") { allUsersData.value = parsedData; } else if (currentTab.value == "TribeMembers") { tribeMemberData.value = parsedData; } // Ensure currentPage is valid based on the new total count final totalCount = parsedData.data?.totalCount ?? 0; final totalPages = totalCount > 0 ? (totalCount / pageSize).ceil() : 1; // If current page is greater than total pages, adjust it if (currentPage.value > totalPages && totalPages > 0) { currentPage.value = totalPages; } ret = true; } else { errorMessage('Failed to load users: ${response.statusCode}'); ret = false; } } catch (e) { errorMessage('Error: ${e.toString()}'); ret = false; } isLoading(false); return ret; } // Send Connection Request API call............................................................... RxList sendConnectionRequestLoading = [].obs; RxBool singleSendConnectionRequestLoading = false.obs; Future sendConnectionRequest(String userId) async { sendConnectionRequestLoading.add(userId); singleSendConnectionRequestLoading(true); bool ret = false; try { var response = await ApiBase.postRequest( extendedURL: ApiUrl.createConnection, body: {'receiverID': userId}, ); if (response.statusCode == 200 || response.statusCode == 201) { // Update connection status in appropriate data model if (currentTab.value == "Pending") { final userList = allUsersData.value?.data?.items; if (userList != null) { final user = userList.firstWhereOrNull((u) => u.userId == userId); if (user != null) { user.connectionStatus = "Pending"; allUsersData.refresh(); // to trigger UI update } } } else if (currentTab.value == "Suggested") { final userList = suggestedUsersData.value?.data?.items; if (userList != null) { final user = userList.firstWhereOrNull((u) => u.userId == userId); if (user != null) { user.connectionStatus = "Pending"; suggestedUsersData.refresh(); // to trigger UI update } } } ret = true; } else { customSnackbar( title: "Failure", message: "Failed to send connection request.", ); ret = false; } } catch (e) { customSnackbar( title: "Error", message: "Failed to send connection request. Please try again later.", ); ret = false; } sendConnectionRequestLoading.remove(userId); singleSendConnectionRequestLoading(false); return ret; } //Remobe frienf API call................................................................................ RxBool isRemoveConnectionLoading = false.obs; Future removeFriend({required String connectionId}) async { isRemoveConnectionLoading(true); try { final response = await ApiBase.deleteRequest( extendedURL: "/api/Socials/user-delete-connection", body: {"connectedUserID": connectionId}, ); if (response.statusCode == 200 || response.statusCode == 201) { return true; } else { return false; } } catch (e) { return false; } finally { isRemoveConnectionLoading(false); } } // Response to Connection Request API call.............................................................. RxList responseToInvitationLoading = [].obs; RxBool isRespondingToConnection = false.obs; Future respondToConnectionRequest(String userId, String status) async { responseToInvitationLoading.add(userId); isRespondingToConnection(true); bool ret = false; try { var response = await ApiBase.putRequest( extendedURL: ApiUrl.responseToConnectRequest, body: {'connectedUserID': userId, 'connectionStatus': status}, ); if (response.statusCode == 200 || response.statusCode == 204) { // Remove the user from the list after responding if (currentTab.value == "Invitations") { final userList = invitationsData.value?.data?.items; if (userList != null) { userList.removeWhere((u) => u.userId == userId); // Update total count when removing items if (invitationsData.value?.data?.totalCount != null) { invitationsData.value!.data!.totalCount = invitationsData.value!.data!.totalCount! - 1; } invitationsData.refresh(); final totalCount = invitationsData.value?.data?.totalCount ?? 0; final totalPages = totalCount > 0 ? (totalCount / pageSize).ceil() : 1; if (currentPage.value > totalPages && totalPages > 0) { currentPage.value = totalPages; (); } } } String message = status == "Accepted" ? "Connection request accepted successfully!" : "Connection request rejected"; customSnackbar(title: "Success", message: message, duration: 1); ret = true; } else { customSnackbar( title: "Failure", message: "Failed to respond to connection request.", duration: 1, ); ret = false; } } catch (e) { customSnackbar( title: "Error", message: "Failed to respond to connection request. Please try again later.", duration: 1, ); ret = false; } responseToInvitationLoading.remove(userId); isRespondingToConnection(false); return ret; } //..................................................................................... // Pagination........................................................................ //..................................................................................... void onPageChanged(int page) { currentPage.value = page; searchUsers(); } void onSearchChanged(String value) { searchValue.value = value; currentPage.value = 1; } void clearSearch() { searchValue.value = ''; searchController.clear(); currentPage.value = 1; clearCurrentTabData(); // Clear old data searchUsers(); } void changeTab(String tab) { if (currentTab.value == tab) return; // Don't reload if same tab // Clear search when changing tabs searchValue.value = ''; searchController.clear(); currentPage.value = 1; // Update tab currentTab.value = tab; // Clear old data and fetch new clearCurrentTabData(); searchUsers(); } //..................................................................................... // Tribe functions........................................................................ //..................................................................................... RxList selectedUserIds = [].obs; Rx selectedTribeImage = Rx(null); TextEditingController tribeNameController = TextEditingController(); Future pickTribeImage() async { final picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image != null) { selectedTribeImage.value = File(image.path); } } Future createTribe() async { final imageFile = selectedTribeImage.value; final tribeName = tribeNameController.text.trim(); bool ret = false; if (tribeName.isEmpty) { customSnackbar( title: "Error", message: "Please enter a tribe name", duration: 1, ); return false; } isLoading.value = true; try { final Map body = {'TribeName': tribeName}; for (var i = 0; i < selectedUserIds.length; i++) { body['TribeMemberIDs[$i]'] = selectedUserIds[i]; } final response = await ApiBase.postAnyFileWithMimeType( extendedURL: ApiUrl.createTribe, file: imageFile != null ? XFile(imageFile.path) : null, fileNameKey: 'TribeImage', body: body, ); if (response.statusCode == 200 || response.statusCode == 201) { ret = true; selectedTribeImage.value = null; tribeNameController.clear(); selectedUserIds.clear(); clearAllUserSelections(); } else { ret = false; customSnackbar( title: "Failed", message: "Tribe creation failed. Please try again later.", duration: 1, ); } } catch (e, stack) { ret = false; customSnackbar( title: "Error", message: "An unexpected error occurred. Please check your connection.", duration: 1, ); } finally { isLoading.value = false; } return ret; } //......... Tribe User Selection..................................................................... final Map _selectedUserMap = {}.obs; List get allSelectedUsers => _selectedUserMap.values.toList(); void tribeUserSelection(String userId) { final data = usersData.value?.data; final items = data?.items ?? []; if (selectedUserIds.contains(userId)) { selectedUserIds.remove(userId); _selectedUserMap.remove(userId); } else { selectedUserIds.add(userId); final userIndex = items.indexWhere((user) => user.userId == userId); if (userIndex >= 0) { _selectedUserMap[userId] = items[userIndex]; } } } void clearAllUserSelections() { selectedUserIds.clear(); _selectedUserMap.clear(); } //..................................................................................... }