844 lines
26 KiB
Dart
844 lines
26 KiB
Dart
import 'dart:convert';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:onufitness/services/logger_service.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:onufitness/constants/api_endpoints.dart';
|
|
import 'package:onufitness/models/master_dropdowns/fitness_goals_dropdown_response_model.dart';
|
|
import 'package:onufitness/models/master_dropdowns/metric_list_response_model.dart';
|
|
import 'package:onufitness/screens/echoboard/models/get_exclusive_connections_response_model.dart';
|
|
import 'package:onufitness/services/api_services/base_api_services.dart';
|
|
import 'package:onufitness/utils/custom_sneakbar.dart';
|
|
|
|
class CreateChallengeController extends GetxController {
|
|
final logger = LoggerService();
|
|
|
|
@override
|
|
void onInit() {
|
|
titleController = TextEditingController();
|
|
descriptionController = TextEditingController();
|
|
taskTitleController = TextEditingController();
|
|
metricsController = TextEditingController();
|
|
targetValueController = TextEditingController();
|
|
goalController = TextEditingController();
|
|
unitController = TextEditingController();
|
|
connectionSearchController = TextEditingController();
|
|
super.onInit();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
titleController.dispose();
|
|
descriptionController.dispose();
|
|
taskTitleController.dispose();
|
|
metricsController.dispose();
|
|
targetValueController.dispose();
|
|
goalController.dispose();
|
|
unitController.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
void clearAll() {
|
|
titleController.clear();
|
|
descriptionController.clear();
|
|
taskTitleController.clear();
|
|
targetValueController.clear();
|
|
challengeVisibilityId.value = 1; // Reset to Public
|
|
selectedConnections.clear();
|
|
selectedIds.clear();
|
|
selectedConnections.clear();
|
|
selectedConnections.refresh();
|
|
selectedIds.clear();
|
|
|
|
selectedFitnessGoal.value = "";
|
|
selectedFitnessGoalId.value = 0;
|
|
|
|
startDate.value = null;
|
|
endDate.value = null;
|
|
|
|
demoVideoName.value = '';
|
|
demoVideoFile.value = null;
|
|
|
|
bannerImageName.value = '';
|
|
bannerImageFile.value = null;
|
|
|
|
selectedMetric.value = '';
|
|
selectedMetricId.value = 0;
|
|
selectedUnit.value = '';
|
|
localUnitsList.clear();
|
|
isAutoJoin.value = true;
|
|
}
|
|
|
|
// All TextEditingControllers
|
|
TextEditingController titleController = TextEditingController();
|
|
TextEditingController descriptionController = TextEditingController();
|
|
TextEditingController taskTitleController = TextEditingController();
|
|
TextEditingController metricsController = TextEditingController();
|
|
TextEditingController targetValueController = TextEditingController();
|
|
TextEditingController goalController = TextEditingController();
|
|
TextEditingController unitController = TextEditingController();
|
|
|
|
// Date controllers
|
|
Rx<DateTime?> startDate = Rx<DateTime?>(null);
|
|
Rx<DateTime?> endDate = Rx<DateTime?>(null);
|
|
|
|
// File picker observables
|
|
RxString demoVideoName = ''.obs;
|
|
RxString bannerImageName = ''.obs;
|
|
|
|
// File objects to store the actual files
|
|
Rx<PlatformFile?> demoVideoFile = Rx<PlatformFile?>(null);
|
|
Rx<PlatformFile?> bannerImageFile = Rx<PlatformFile?>(null);
|
|
|
|
// Loading states
|
|
RxBool isVideoUploading = false.obs;
|
|
RxBool isImageUploading = false.obs;
|
|
RxBool isAutoJoin = true.obs;
|
|
|
|
// Clear demo video
|
|
void clearDemoVideo() {
|
|
demoVideoFile.value = null;
|
|
demoVideoName.value = '';
|
|
}
|
|
|
|
// Clear banner image
|
|
void clearBannerImage() {
|
|
bannerImageFile.value = null;
|
|
bannerImageName.value = '';
|
|
}
|
|
|
|
// Date picker methods
|
|
Future<void> pickStartDate(BuildContext context) async {
|
|
final now = DateTime.now();
|
|
final currentStartDate = startDate.value;
|
|
final localStartDate = currentStartDate?.toLocal();
|
|
|
|
// Ensure initialDate is not before firstDate
|
|
final initialDate =
|
|
localStartDate != null && localStartDate.isAfter(now)
|
|
? localStartDate
|
|
: now;
|
|
|
|
final DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: initialDate,
|
|
firstDate: now,
|
|
lastDate: DateTime(2030),
|
|
);
|
|
|
|
if (picked != null) {
|
|
final combined = DateTime(
|
|
picked.year,
|
|
picked.month,
|
|
picked.day,
|
|
now.hour,
|
|
now.minute,
|
|
now.second,
|
|
now.millisecond,
|
|
now.microsecond,
|
|
);
|
|
|
|
startDate.value = combined.toUtc();
|
|
endDate.value = null;
|
|
}
|
|
}
|
|
|
|
Future<void> pickEndDate(BuildContext context) async {
|
|
final now = DateTime.now();
|
|
final currentEndDate = endDate.value;
|
|
final currentStartDate = startDate.value;
|
|
final firstDate =
|
|
currentStartDate != null
|
|
? DateTime(
|
|
currentStartDate.toLocal().year,
|
|
currentStartDate.toLocal().month,
|
|
currentStartDate.toLocal().day,
|
|
)
|
|
: now;
|
|
|
|
final localEndDate = currentEndDate?.toLocal();
|
|
final initialDate =
|
|
localEndDate != null && localEndDate.isAfter(firstDate)
|
|
? localEndDate
|
|
: firstDate;
|
|
|
|
final DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: initialDate,
|
|
firstDate: firstDate,
|
|
lastDate: DateTime(2030),
|
|
);
|
|
|
|
if (picked != null) {
|
|
final combined = DateTime(
|
|
picked.year,
|
|
picked.month,
|
|
picked.day,
|
|
now.hour,
|
|
now.minute,
|
|
now.second,
|
|
now.millisecond,
|
|
now.microsecond,
|
|
);
|
|
|
|
endDate.value = combined.toUtc();
|
|
}
|
|
}
|
|
|
|
// Validation methods
|
|
bool get isDemoVideoSelected => demoVideoName.value.isNotEmpty;
|
|
bool get isBannerImageSelected => bannerImageName.value.isNotEmpty;
|
|
|
|
// Create Challenge Form validation...................................................................
|
|
bool hasValidWordLength(String text) {
|
|
final wordCount = text.trim().split(RegExp(r'\s+')).length;
|
|
return wordCount <= 100;
|
|
}
|
|
|
|
bool hasValidWordLengthDescription(String text) {
|
|
final wordCount = text.trim().split(RegExp(r'\s+')).length;
|
|
return wordCount <= 255;
|
|
}
|
|
|
|
bool validateForm() {
|
|
final title = titleController.text.trim();
|
|
final taskTitle = taskTitleController.text.trim();
|
|
final description = descriptionController.text.trim();
|
|
|
|
if (!hasValidWordLength(title)) {
|
|
customSnackbar(
|
|
title: "Challenge Title Length Exceeded",
|
|
message: "Challenge Title should not exceed 100 words",
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (!hasValidWordLength(taskTitle)) {
|
|
customSnackbar(
|
|
title: "Task Title Length Exceeded",
|
|
message: "Task Title should not exceed 100 words",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
if (!hasValidWordLengthDescription(description)) {
|
|
customSnackbar(
|
|
title: "Description Length Exceeded",
|
|
message: "Description should not exceed 100 words",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (titleController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please enter challenge title",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (selectedFitnessGoalId.value == 0) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please select a fitness goal",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (startDate.value == null) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please select start date",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (endDate.value == null) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please select end date",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (descriptionController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please enter description",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (taskTitleController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please enter task title",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (selectedMetricId.value == 0) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please select a metric",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (targetValueController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please enter target value",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (selectedUnit.value.isEmpty) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please select a unit",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (challengeVisibilityId.value == 3) {
|
|
if (selectedConnections.isEmpty) {
|
|
customSnackbar(
|
|
title: "Validation Error",
|
|
message: "Please select at least one member or group",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//..................................................................................................
|
|
// Calculate duration in days......................................................................
|
|
int calculateDuration() {
|
|
if (startDate.value != null && endDate.value != null) {
|
|
return endDate.value!.difference(startDate.value!).inDays + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//......................................................................................................
|
|
//......................................................................................................
|
|
// Challenge Privacy Update API Call.....................................................................
|
|
//......................................................................................................
|
|
//......................................................................................................
|
|
|
|
RxBool isUpdatePrivacyLoading = false.obs;
|
|
|
|
Future<bool> updateChallengePrivacy({
|
|
required int selectedChallengeId,
|
|
}) async {
|
|
bool ret = false;
|
|
isUpdatePrivacyLoading.value = true;
|
|
|
|
try {
|
|
// Prepare form data
|
|
Map<String, dynamic> formData = {};
|
|
formData['ChallengeID'] = selectedChallengeId.toString();
|
|
formData['ChallengeVisibilityID'] =
|
|
challengeVisibilityId.value.toString();
|
|
|
|
// Prepare new connections JSON (only for exclusive)
|
|
if (challengeVisibilityId.value == 3) {
|
|
formData['NewChallengeConnectionJson'] = jsonEncode(
|
|
selectedConnections,
|
|
);
|
|
} else {
|
|
// If switching to public, we need to remove all connections
|
|
formData['NewChallengeConnectionJson'] = jsonEncode([]);
|
|
}
|
|
//Always keep deleteJson empty
|
|
formData['DeleteChallengeConnectionJson'] = jsonEncode([]);
|
|
|
|
final response = await ApiBase.patchAnyFileWithMimeType(
|
|
extendedURL: ApiUrl.updateChallengePrivacy,
|
|
body: formData,
|
|
sendHeaders: true,
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
ret = true;
|
|
customSnackbar(
|
|
title: "Success",
|
|
message: "Challenge privacy updated successfully!",
|
|
);
|
|
|
|
// Clear selections after successful update
|
|
selectedConnections.clear();
|
|
selectedIds.clear();
|
|
} else {
|
|
ret = false;
|
|
try {
|
|
final errorData = jsonDecode(response.body);
|
|
final errorMessage =
|
|
errorData['message'] ??
|
|
"Failed to update challenge privacy. Please try again.";
|
|
customSnackbar(title: "Error", message: errorMessage);
|
|
} catch (e) {
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Failed to update challenge privacy. Please try again.",
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
logger.error('Error updating challenge privacy: $e');
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Something went wrong. Please try again.",
|
|
);
|
|
} finally {
|
|
isUpdatePrivacyLoading.value = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//................................................................................................
|
|
// Create Challenge API Call -......................................................................
|
|
//................................................................................................
|
|
RxInt challengeVisibilityId = 1.obs; // Default to Public (1)
|
|
|
|
RxBool isCreatingChallengeLoading = false.obs;
|
|
Future<bool> createChallenge() async {
|
|
bool ret = false;
|
|
if (!validateForm()) return false;
|
|
isCreatingChallengeLoading.value = true;
|
|
try {
|
|
// Prepare form data (body fields)
|
|
Map<String, dynamic> formData = {};
|
|
formData['ChallengeTitle'] = titleController.text.trim().toString();
|
|
formData['ChallengeDescription'] =
|
|
descriptionController.text.trim().toString();
|
|
formData['FitnessGoalID'] =
|
|
selectedFitnessGoalId.value.toString().toString();
|
|
formData['StartDate'] = startDate.value!.toIso8601String().toString();
|
|
formData['EndDate'] = endDate.value!.toIso8601String().toString();
|
|
formData['Duration'] = calculateDuration().toString().toString();
|
|
formData['IsAutoJoin'] = isAutoJoin.value.toString();
|
|
formData['ChallengeVisibilityID'] =
|
|
challengeVisibilityId.value.toString();
|
|
|
|
// Challenge tasks JSON
|
|
List<Map<String, dynamic>> challengeTasks = [
|
|
{
|
|
"taskTitle": taskTitleController.text.trim(),
|
|
"taskMetricID": selectedMetricId.value,
|
|
"taskUnit": selectedUnit.value,
|
|
"targetValue": int.tryParse(targetValueController.text.trim()) ?? 0,
|
|
},
|
|
];
|
|
formData['ChallengeTasks'] = jsonEncode(challengeTasks);
|
|
|
|
// Challenge connections JSON
|
|
if (challengeVisibilityId.value == 3) {
|
|
formData['ChallengeConnectionsJson'] = jsonEncode(selectedConnections);
|
|
}
|
|
// Prepare files map with different field names
|
|
Map<String, dynamic> filesMap = {};
|
|
|
|
// Add video file if exists
|
|
if (demoVideoFile.value != null) {
|
|
filesMap['ChallengeVideo'] = demoVideoFile.value!;
|
|
}
|
|
|
|
// Add image file if exists
|
|
if (bannerImageFile.value != null) {
|
|
filesMap['ChallengeImage'] = bannerImageFile.value!;
|
|
}
|
|
|
|
final response = await ApiBase.postMultipleIndivisualFiles(
|
|
extendedURL: ApiUrl.createChallenge,
|
|
body: formData,
|
|
files: filesMap,
|
|
sendHeaders: true,
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
ret = true;
|
|
customSnackbar(
|
|
title: "Success",
|
|
message: "Challenge created successfully!",
|
|
);
|
|
await Future.delayed(Duration(milliseconds: 700));
|
|
clearForm();
|
|
Get.back();
|
|
} else {
|
|
ret = false;
|
|
try {
|
|
ret = false;
|
|
final errorData = jsonDecode(response.body);
|
|
final errorMessage =
|
|
errorData['message'] ??
|
|
"Failed to create challenge. Please try again.";
|
|
customSnackbar(title: "Error", message: errorMessage);
|
|
} catch (e) {
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Failed to create challenge. Please try again.",
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
logger.error('Error creating challenge: $e');
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Something went wrong. Please try again.",
|
|
);
|
|
} finally {
|
|
isCreatingChallengeLoading.value = false;
|
|
}
|
|
isCreatingChallengeLoading.value = false;
|
|
return ret;
|
|
}
|
|
|
|
// Clear form......................................................................................
|
|
void clearForm() {
|
|
challengeVisibilityId.value = 1; // Reset to Public
|
|
selectedConnections.clear();
|
|
selectedIds.clear();
|
|
titleController.clear();
|
|
descriptionController.clear();
|
|
taskTitleController.clear();
|
|
targetValueController.clear();
|
|
|
|
startDate.value = null;
|
|
endDate.value = null;
|
|
|
|
demoVideoFile.value = null;
|
|
demoVideoName.value = '';
|
|
bannerImageFile.value = null;
|
|
bannerImageName.value = '';
|
|
|
|
selectedFitnessGoal.value = '';
|
|
selectedFitnessGoalId.value = 0;
|
|
selectedMetric.value = '';
|
|
selectedMetricId.value = 0;
|
|
selectedUnit.value = '';
|
|
|
|
selectedConnections.clear();
|
|
selectedIds.clear();
|
|
}
|
|
|
|
// Add Participent API Call ..............................................................................
|
|
RxBool isAddPrticipentLoading = false.obs;
|
|
|
|
Future<bool> addParticipentToChallenge({
|
|
required int selectedChallengeId,
|
|
}) async {
|
|
bool ret = false;
|
|
isAddPrticipentLoading(true);
|
|
|
|
try {
|
|
final response = await ApiBase.postRequest(
|
|
extendedURL: ApiUrl.addParticipentToChallenge,
|
|
body: {
|
|
"challengeID": selectedChallengeId,
|
|
"challengeConnectionsJson": jsonEncode(selectedConnections),
|
|
},
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
customSnackbar(
|
|
title: "Success",
|
|
message: "Participants added successfully",
|
|
duration: 1,
|
|
);
|
|
ret = true;
|
|
} else {
|
|
final data = jsonDecode(response.body);
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Error",
|
|
message:
|
|
data['message'] ??
|
|
"Failed to add participants. Please try again.",
|
|
);
|
|
}
|
|
} catch (e) {
|
|
logger.error('Error adding participants to challenge: $e');
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Something went wrong. Please try again later.",
|
|
);
|
|
} finally {
|
|
isAddPrticipentLoading(false);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//.......................................................................................................
|
|
//............... Fetch Fitness Goals List................................................................
|
|
//.......................................................................................................
|
|
|
|
var isFitnessGoalsLoading = false.obs;
|
|
var apiFitnessGoalsList = <SingleFitnessGoal>[].obs;
|
|
var selectedFitnessGoal = "".obs;
|
|
RxList<String> localFitnessGoalsList = <String>[].obs;
|
|
RxInt selectedFitnessGoalId = 0.obs;
|
|
Future<void> fetchFitnessGoals() async {
|
|
isFitnessGoalsLoading(true);
|
|
try {
|
|
var response = await ApiBase.getRequest(
|
|
extendedURL: ApiUrl.fetchFitnessGoals,
|
|
sendHeaders: false,
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
var responseData = fitnessGoalsResponseModelFromJson(response.body);
|
|
if (responseData.isSuccess == true) {
|
|
apiFitnessGoalsList.assignAll(responseData.data ?? []);
|
|
|
|
localFitnessGoalsList.clear();
|
|
|
|
for (var goal in responseData.data!) {
|
|
localFitnessGoalsList.add(goal.fitnessGoalTitle.toString());
|
|
}
|
|
} else {
|
|
var responseData = fitnessGoalsResponseModelFromJson(response.body);
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: responseData.message ?? "Failed to load Fitness Goals",
|
|
);
|
|
}
|
|
} else {
|
|
var responseData = fitnessGoalsResponseModelFromJson(response.body);
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: responseData.message ?? "Failed to load Fitness Goals",
|
|
);
|
|
}
|
|
} catch (e) {
|
|
logger.error('Error fetching fitness goals: $e');
|
|
customSnackbar(title: "Error", message: "Failed to load Fitness Goals");
|
|
} finally {
|
|
isFitnessGoalsLoading(false);
|
|
}
|
|
}
|
|
|
|
//.......................................................................................................
|
|
//... Fetch Metrics and Units............................................................................
|
|
//.......................................................................................................
|
|
|
|
var isMetricsLoading = false.obs;
|
|
var apiMetricsList = <SingelMetricModel>[].obs;
|
|
var localMetricsList = <String>[].obs;
|
|
var selectedMetric = ''.obs;
|
|
var selectedMetricId = 0.obs;
|
|
var localUnitsList = <String>[].obs;
|
|
var selectedUnit = ''.obs;
|
|
|
|
Future<void> fetchMetricsList() async {
|
|
try {
|
|
isMetricsLoading(true);
|
|
|
|
final response = await ApiBase.getRequest(
|
|
extendedURL: ApiUrl.fetchMasterTableUniList,
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final MetricListSubmitModel metricsResponse =
|
|
metricListSubmitModelFromJson(response.body);
|
|
|
|
if (metricsResponse.isSuccess == true && metricsResponse.data != null) {
|
|
apiMetricsList.assignAll(metricsResponse.data!);
|
|
localMetricsList.assignAll(
|
|
metricsResponse.data!
|
|
.map((metric) => metric.metricName ?? '')
|
|
.toList(),
|
|
);
|
|
} else {
|
|
customSnackbar(
|
|
title: 'Error',
|
|
message: metricsResponse.message ?? 'Failed to fetch metrics',
|
|
duration: 2,
|
|
);
|
|
}
|
|
} else {
|
|
logger.warning('Failed to fetch metrics: HTTP ${response.statusCode}');
|
|
customSnackbar(
|
|
title: 'Error',
|
|
message: 'Failed to fetch metrics. Please try again.',
|
|
duration: 2,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
logger.error('Error fetching metrics: $e');
|
|
customSnackbar(
|
|
title: 'Error',
|
|
message: 'Something went wrong. Please try again.',
|
|
duration: 2,
|
|
);
|
|
} finally {
|
|
isMetricsLoading(false);
|
|
}
|
|
}
|
|
|
|
void updateUnitsForSelectedMetric(List<String> units) {
|
|
selectedUnit.value = '';
|
|
localUnitsList.clear();
|
|
if (units.isNotEmpty) {
|
|
final uniqueUnits =
|
|
units.where((unit) => unit.isNotEmpty).toSet().toList();
|
|
localUnitsList.assignAll(uniqueUnits);
|
|
}
|
|
|
|
localUnitsList.refresh();
|
|
update();
|
|
}
|
|
|
|
//..........................................................................................
|
|
//..........................................................................................
|
|
//.............Connection Fetch (Exclusive).................................................
|
|
//..........................................................................................
|
|
//..........................................................................................
|
|
|
|
final RxList<Map<String, dynamic>> selectedConnections =
|
|
<Map<String, dynamic>>[].obs;
|
|
final RxList<String> selectedIds = <String>[].obs;
|
|
RxString connectionSearchValue = ''.obs;
|
|
RxInt connectionCurrentPage = 1.obs;
|
|
final int connectionPageSize = 10;
|
|
final hasMoreDataConnection = true.obs;
|
|
final isConnectionPaginationLoading = false.obs;
|
|
TextEditingController connectionSearchController = TextEditingController();
|
|
|
|
ExclusiveConnectionResponseModel exclusiveConnections =
|
|
ExclusiveConnectionResponseModel();
|
|
RxBool isConnectionsFetchLoading = false.obs;
|
|
|
|
Future<bool> fetchExclusiveConnections({
|
|
bool refresh = false,
|
|
int challengeId = 0,
|
|
}) async {
|
|
if (refresh) {
|
|
connectionCurrentPage.value = 1;
|
|
hasMoreDataConnection.value = true;
|
|
exclusiveConnections = ExclusiveConnectionResponseModel();
|
|
} else {
|
|
if (isConnectionPaginationLoading.value ||
|
|
hasMoreDataConnection.value == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
isConnectionPaginationLoading(true);
|
|
if (refresh) isConnectionsFetchLoading(true);
|
|
|
|
try {
|
|
final response = await ApiBase.getRequest(
|
|
extendedURL:
|
|
"${ApiUrl.fetchExclusiveConnectionsOfChallenge}/$challengeId?pageNumber=${connectionCurrentPage.value}&pagesize=$connectionPageSize&searchType=name&searchValue=${connectionSearchValue.value}",
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
final newData = exclusiveConnectionResponseModelFromJson(response.body);
|
|
|
|
if (newData.isSuccess == true && newData.data != null) {
|
|
if (refresh) {
|
|
exclusiveConnections = newData;
|
|
} else {
|
|
// Append to existing items
|
|
final currentItems = exclusiveConnections.data?.items ?? [];
|
|
final newItems = newData.data?.items ?? [];
|
|
|
|
exclusiveConnections.data?.items = [...currentItems, ...newItems];
|
|
exclusiveConnections.data?.totalCount = newData.data?.totalCount;
|
|
}
|
|
|
|
final fetchedItemsCount =
|
|
exclusiveConnections.data?.items?.length ?? 0;
|
|
final totalItemsCount = newData.data?.totalCount ?? 0;
|
|
|
|
hasMoreDataConnection.value = fetchedItemsCount < totalItemsCount;
|
|
|
|
if (newData.data?.items?.isNotEmpty == true) {
|
|
connectionCurrentPage.value++;
|
|
} else {
|
|
// No more items to fetch
|
|
hasMoreDataConnection.value = false;
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Failed to fetch connection details",
|
|
);
|
|
return false;
|
|
}
|
|
} else {
|
|
customSnackbar(
|
|
title: "Error",
|
|
message: "Failed to fetch connection details",
|
|
);
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
logger.error('Error fetching connections: $e');
|
|
return false;
|
|
} finally {
|
|
isConnectionsFetchLoading(false);
|
|
isConnectionPaginationLoading(false);
|
|
}
|
|
}
|
|
|
|
//..........................................................................................
|
|
|
|
void clearSearch() {
|
|
connectionSearchValue.value = '';
|
|
connectionCurrentPage.value = 1;
|
|
connectionSearchController.clear();
|
|
fetchExclusiveConnections(refresh: true);
|
|
}
|
|
|
|
//..........................................................................................
|
|
void exclusiveConnectionToggleSelection({
|
|
required int? tribeId,
|
|
required String? connectedUserId,
|
|
}) {
|
|
final String id =
|
|
tribeId != null ? 'tribe_$tribeId' : 'user_$connectedUserId';
|
|
|
|
if (selectedIds.contains(id)) {
|
|
// Unselect.....................
|
|
selectedIds.remove(id);
|
|
selectedConnections.removeWhere((element) {
|
|
return (tribeId != null && element['TribeID'] == tribeId) ||
|
|
(connectedUserId != null &&
|
|
element['ConnectedUserID'] == connectedUserId);
|
|
});
|
|
} else {
|
|
// Select........................
|
|
selectedIds.add(id);
|
|
selectedConnections.add({
|
|
"TribeID": tribeId,
|
|
"ConnectedUserID": connectedUserId,
|
|
});
|
|
}
|
|
}
|
|
}
|