import 'dart:convert'; import 'package:auto_gpt_flutter_client/models/task.dart'; import 'package:auto_gpt_flutter_client/models/test_suite.dart'; import 'package:auto_gpt_flutter_client/services/shared_preferences_service.dart'; import 'package:flutter/foundation.dart'; import 'package:collection/collection.dart'; import 'package:auto_gpt_flutter_client/services/task_service.dart'; import 'package:auto_gpt_flutter_client/models/task_request_body.dart'; // TODO: How will all these functions work with test suites? class TaskViewModel with ChangeNotifier { final TaskService _taskService; final SharedPreferencesService _prefsService; List _tasks = []; List _testSuites = []; List combinedDataSource = []; List tasksDataSource = []; Task? _selectedTask; TestSuite? _selectedTestSuite; bool _isWaitingForAgentResponse = false; bool get isWaitingForAgentResponse => _isWaitingForAgentResponse; TaskViewModel(this._taskService, this._prefsService); /// Returns the currently selected task. Task? get selectedTask => _selectedTask; TestSuite? get selectedTestSuite => _selectedTestSuite; /// Adds a task and returns its ID. Future createTask(String title) async { _isWaitingForAgentResponse = true; notifyListeners(); try { final newTask = TaskRequestBody(input: title); // Add to data source final createdTask = await _taskService.createTask(newTask); // Create a Task object from the created task response final newTaskObject = Task(id: createdTask['task_id'], title: createdTask['input']); fetchAndCombineData(); final taskId = newTaskObject.id; print("Task $taskId created successfully!"); return newTaskObject.id; } catch (e) { // TODO: We are bubbling up the full response. Revisit this. rethrow; } finally { _isWaitingForAgentResponse = false; notifyListeners(); } } /// Deletes a task. void deleteTask(String taskId) { _taskService.saveDeletedTask(taskId); _tasks.removeWhere((task) => task.id == taskId); notifyListeners(); print("Task $taskId deleted successfully!"); } /// Fetches tasks from the data source. Future fetchTasks() async { try { final tasksFromApi = await _taskService.fetchAllTasks(); _tasks = tasksFromApi .where((task) => !_taskService.isTaskDeleted(task.id)) .toList(); _tasks = _tasks.reversed.toList(); notifyListeners(); print("Tasks fetched successfully!"); } catch (error) { print("Error fetching tasks: $error"); } } /// Handles the selection of a task by its ID. void selectTask(String id) { final task = _tasks.firstWhereOrNull((t) => t.id == id); if (task != null) { _selectedTask = task; print("Selected task with ID: ${task.id} and Title: ${task.title}"); notifyListeners(); // Notify listeners to rebuild UI } else { final errorMessage = "Error: Attempted to select a task with ID: $id that does not exist in the data source."; print(errorMessage); throw ArgumentError(errorMessage); } } /// Deselects the currently selected task. void deselectTask() { _selectedTask = null; print("Deselected the current task."); notifyListeners(); // Notify listeners to rebuild UI } void selectTestSuite(TestSuite testSuite) { _selectedTestSuite = testSuite; notifyListeners(); } void deselectTestSuite() { _selectedTestSuite = null; notifyListeners(); } // Helper method to save test suites to SharedPreferences Future _saveTestSuitesToPrefs() async { final testSuitesToStore = _testSuites.map((testSuite) => jsonEncode(testSuite.toJson())).toList(); await _prefsService.setStringList('testSuites', testSuitesToStore); } // Adds a new test suite and saves it to SharedPreferences void addTestSuite(TestSuite testSuite) async { _testSuites.add(testSuite); await _saveTestSuitesToPrefs(); notifyListeners(); print("Test suite successfully added!"); } // Fetch test suites from SharedPreferences Future fetchTestSuites() async { final storedTestSuites = await _prefsService.getStringList('testSuites') ?? []; _testSuites = storedTestSuites .map((testSuiteMap) => TestSuite.fromJson(jsonDecode(testSuiteMap))) .toList(); notifyListeners(); } // The fetchAndCombineData method performs several tasks: // 1. It fetches the tasks and filters out deleted ones. // 2. It fetches the test suites from SharedPreferences. // 3. It combines both the tasks and test suites into a single data source according to specified logic. Future fetchAndCombineData() async { // Step 1: Fetch tasks from the data source // This will populate the _tasks list with tasks fetched from the backend. await fetchTasks(); // Step 2: Fetch test suites from SharedPreferences // This will populate the _testSuites list with test suites fetched from SharedPreferences. await fetchTestSuites(); // Step 3: Combine into a shared data source // Create a map to hold test suites by their timestamp. Map testSuiteMap = {}; // Clear the existing combined data source to start fresh. combinedDataSource.clear(); tasksDataSource.clear(); // Iterate through each task to check if it's contained in any of the test suites. for (var task in _tasks) { bool found = false; // Iterate through each test suite. for (var testSuite in _testSuites) { // Check if the current task is contained in the current test suite. if (testSuite.tests.contains(task)) { found = true; // If this test suite is already in the map, add this task to its list of tasks. if (testSuiteMap.containsKey(testSuite.timestamp)) { testSuiteMap[testSuite.timestamp]!.tests.add(task); // Find and replace the test suite in the combined data source. final index = combinedDataSource.indexWhere((item) => item is TestSuite && item.timestamp == testSuite.timestamp); if (index != -1) { combinedDataSource[index] = testSuiteMap[testSuite.timestamp]!; } } // If this test suite is not in the map, add it to the map and to the combined data source. else { final newTestSuite = TestSuite( timestamp: testSuite.timestamp, tests: [task], ); testSuiteMap[testSuite.timestamp] = newTestSuite; combinedDataSource.add( newTestSuite); // Add the new test suite to the combined data source. } break; // Exit the loop as the task is found in a test suite. } } // If the task was not found in any test suite, add it to the combined data source. if (!found) { combinedDataSource.add(task); tasksDataSource.add(task); } } // After processing all tasks, call notifyListeners to rebuild the widgets that depend on this data. notifyListeners(); print("Combined tasks and test suites successfully!"); } }