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

368 lines
10 KiB
Dart

import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:onufitness/constants/api_endpoints.dart';
import 'package:onufitness/screens/echoboard/models/tribe/get_all_connections_with_tribe_member_checked_response_model.dart';
import 'package:onufitness/services/api_services/base_api_services.dart';
import 'package:onufitness/utils/custom_sneakbar.dart';
class TribeMemberController extends GetxController {
RxBool isDeleteTribeLoading = false.obs;
RxBool isLoadingMembers = false.obs;
RxBool isLoadingMoreMembers = false.obs;
RxBool isAddingMembers = false.obs;
Rx<GetConnectionsWithTribeMemberCheckedResponseModel?> connectionsData =
Rx<GetConnectionsWithTribeMemberCheckedResponseModel?>(null);
// Keep track of selected members
RxSet<String> selectedMemberIds = <String>{}.obs;
// Search controller
TextEditingController searchController = TextEditingController();
RxString searchValue = ''.obs;
// Pagination
RxInt currentPage = 1.obs;
RxInt pageSize = 20.obs;
RxBool hasMoreData = true.obs;
// Scroll controller
ScrollController scrollController = ScrollController();
// Current tribe id
int? currentTribeId;
@override
void onInit() {
super.onInit();
_setupScrollListener();
}
@override
void onClose() {
searchController.dispose();
scrollController.dispose();
super.onClose();
}
void _setupScrollListener() {
scrollController.addListener(() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
if (!isLoadingMoreMembers.value && hasMoreData.value) {
loadMoreMembers();
}
}
});
}
void onSearchSubmitted(String value) {
searchValue.value = value;
currentPage.value = 1;
hasMoreData.value = true;
fetchConnectedUsersWithMemberStatus(
tribeId: currentTribeId,
isRefresh: true,
);
}
void clearSearch() {
searchController.clear();
searchValue.value = '';
currentPage.value = 1;
hasMoreData.value = true;
fetchConnectedUsersWithMemberStatus(
tribeId: currentTribeId,
isRefresh: true,
);
}
void loadMoreMembers() {
if (!hasMoreData.value || isLoadingMoreMembers.value) return;
currentPage.value++;
fetchConnectedUsersWithMemberStatus(
tribeId: currentTribeId,
isRefresh: false,
);
}
// Fetch connected users with member status for a tribe.........................................................................
Future<void> fetchConnectedUsersWithMemberStatus({
required int? tribeId,
bool isRefresh = true,
}) async {
if (tribeId == null) return;
currentTribeId = tribeId;
if (isRefresh) {
isLoadingMembers(true);
currentPage.value = 1;
hasMoreData.value = true;
} else {
isLoadingMoreMembers(true);
}
try {
String url =
"${ApiUrl.getConnectedUsersWithMemberStatus}/$tribeId?PageNumber=${currentPage.value}&PageSize=${pageSize.value}";
// Add search query if exists
if (searchValue.value.isNotEmpty) {
url += "&SearchType=name&SearchValue=${searchValue.value}";
}
final response = await ApiBase.getRequest(extendedURL: url);
log(response.body.toString());
if (response.statusCode == 200) {
final model = getConnectionsWithTribeMemberCheckedResponseModelFromJson(
response.body,
);
if (isRefresh) {
// Replace data on refresh
connectionsData.value = model;
// Pre-select users who are already members
selectedMemberIds.clear();
if (model.data?.items != null) {
for (var user in model.data!.items!) {
if (user.isMember == true && user.userId != null) {
selectedMemberIds.add(user.userId!);
}
}
}
} else {
// Append data for pagination
if (model.data?.items != null && model.data!.items!.isNotEmpty) {
final existingItems = connectionsData.value?.data?.items ?? [];
final newItems = model.data!.items!;
// Merge items
final allItems = [...existingItems, ...newItems];
// Update connectionsData with merged items
connectionsData
.value = GetConnectionsWithTribeMemberCheckedResponseModel(
isSuccess: model.isSuccess,
statusCode: model.statusCode,
message: model.message,
data: Data(totalCount: model.data?.totalCount, items: allItems),
errors: model.errors,
);
// Pre-select new members
for (var user in newItems) {
if (user.isMember == true && user.userId != null) {
selectedMemberIds.add(user.userId!);
}
}
} else {
// No more data
hasMoreData.value = false;
}
}
// Check if there's more data to load
final totalCount = model.data?.totalCount ?? 0;
final loadedCount = connectionsData.value?.data?.items?.length ?? 0;
hasMoreData.value = loadedCount < totalCount;
} else {
customSnackbar(title: "Error", message: "Failed to load users");
}
} catch (e) {
customSnackbar(
title: "Error",
message: "An error occurred: ${e.toString()}",
);
} finally {
if (isRefresh) {
isLoadingMembers(false);
} else {
isLoadingMoreMembers(false);
}
}
}
void toggleMemberSelection(String userId) {
if (selectedMemberIds.contains(userId)) {
selectedMemberIds.remove(userId);
} else {
selectedMemberIds.add(userId);
}
}
bool isMemberSelected(String userId) {
return selectedMemberIds.contains(userId);
}
// Add selected members to tribe..................................................................................
Future<bool> addTribeMembers({required int tribeId}) async {
if (selectedMemberIds.isEmpty) {
customSnackbar(
title: "Warning",
message: "Please select at least one member",
);
return false;
}
isAddingMembers(true);
try {
final body = {
"tribeID": tribeId,
"tribeMemberIDs": selectedMemberIds.toList(),
};
final response = await ApiBase.postRequest(
extendedURL: ApiUrl.addTribeMembers,
body: body,
sendHeaders: true,
);
String message = "Something went wrong";
try {
final decoded = jsonDecode(response.body);
message = decoded["message"]?.toString() ?? message;
} catch (_) {
// if invalid JSON, keep default message
}
if (response.statusCode == 200) {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(title: "Success", message: message, duration: 2);
});
// Refresh the list after adding members
await fetchConnectedUsersWithMemberStatus(
tribeId: tribeId,
isRefresh: true,
);
return true;
} else {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(title: "Error", message: message, duration: 2);
});
return false;
}
} catch (e) {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(
title: "Failed",
message: "Failed to add members",
duration: 2,
);
});
return false;
} finally {
isAddingMembers(false);
}
}
// Delete a member from tribe (Admin only)...............................................................
RxBool deleteTribeMemberLoading = false.obs;
Future<bool> deleteTribeMember({
required int tribeId,
required String memberId,
}) async {
deleteTribeMemberLoading(true);
try {
final body = {"tribeID": tribeId, "tribeMemberID": memberId};
final response = await ApiBase.deleteRequest(
extendedURL: ApiUrl.removeTribeMember,
body: body,
);
String message = "Something went wrong";
try {
final decoded = jsonDecode(response.body);
message = decoded["message"]?.toString() ?? message;
} catch (_) {
// if invalid JSON, keep default message
}
if (response.statusCode == 200) {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(
title: "Success",
message:
message.isNotEmpty ? message : "Member removed successfully",
duration: 2,
);
});
return true;
} else {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(
title: "Error",
message: message.isNotEmpty ? message : "Failed to remove member",
duration: 2,
);
});
return false;
}
} catch (e) {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(
title: "Failed",
message: "Failed to remove member",
duration: 2,
);
});
return false;
} finally {
deleteTribeMemberLoading(false);
}
}
// Delete tribe.......................................................................................
Future<bool> deleteTribe({required int tribeID}) async {
isDeleteTribeLoading(true);
try {
final response = await ApiBase.deleteRequest(
extendedURL: "${ApiUrl.deleteTribe}/$tribeID",
body: {},
);
String message = "Something went wrong";
try {
final decoded = jsonDecode(response.body);
message = decoded["message"]?.toString() ?? message;
} catch (_) {
// if invalid JSON, keep default message
}
if (response.statusCode == 200) {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(title: "Success", message: message, duration: 2);
});
return true;
} else {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(title: "Error", message: message, duration: 2);
});
return false;
}
} catch (e) {
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(
title: "Failed",
message: "Failed to delete tribe",
duration: 2,
);
});
return false;
} finally {
isDeleteTribeLoading(false);
}
}
}