onufitness_mobile/lib/screens/echoboard/controllers/connection_and_tribe_controller.dart
2026-01-13 11:36:24 +05:30

623 lines
20 KiB
Dart

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<GetAllUsersForSendConnectionModel?> invitationsData =
Rx<GetAllUsersForSendConnectionModel?>(null);
Rx<GetAllUsersForSendConnectionModel?> friendsData =
Rx<GetAllUsersForSendConnectionModel?>(null);
Rx<GetAllUsersForSendConnectionModel?> suggestedUsersData =
Rx<GetAllUsersForSendConnectionModel?>(null);
Rx<GetAllUsersForSendConnectionModel?> allUsersData =
Rx<GetAllUsersForSendConnectionModel?>(null);
Rx<GetAllUsersForSendConnectionModel?> tribeMemberData =
Rx<GetAllUsersForSendConnectionModel?>(null);
// Tribe data
Rx<TribeListWithMembersResponseModel?> tribesData =
Rx<TribeListWithMembersResponseModel?>(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<GetAllUsersForSendConnectionModel?> 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<GetAllUsersForSendConnectionModel?>(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<bool> 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<SingleTribeDetailsResponseModel?> singleTribeDetails =
Rx<SingleTribeDetailsResponseModel?>(null);
Future<bool> 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<bool> 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<bool> 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<String> sendConnectionRequestLoading = <String>[].obs;
RxBool singleSendConnectionRequestLoading = false.obs;
Future<bool> 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<bool> 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<String> responseToInvitationLoading = <String>[].obs;
RxBool isRespondingToConnection = false.obs;
Future<bool> 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<String> selectedUserIds = <String>[].obs;
Rx<File?> selectedTribeImage = Rx<File?>(null);
TextEditingController tribeNameController = TextEditingController();
Future<void> pickTribeImage() async {
final picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image != null) {
selectedTribeImage.value = File(image.path);
}
}
Future<bool> 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<String, dynamic> 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<String, SocialUserInfo> _selectedUserMap =
<String, SocialUserInfo>{}.obs;
List<SocialUserInfo> 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();
}
//.....................................................................................
}