623 lines
20 KiB
Dart
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();
|
|
}
|
|
|
|
//.....................................................................................
|
|
}
|