import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/etqan_ovr_api_client.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart'; import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart'; import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart'; import 'package:speech_to_text/speech_recognition_result.dart'; import 'package:speech_to_text/speech_to_text.dart'; class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // Home screen state List? getEtqanProjectsList; List? getEtqanEmployeeRequestsList; List? getEtqanEmployeeIncidnetRequest; bool isLoading = false; String? _ticketId; // Speech to text state final SpeechToText _speechToText = SpeechToText(); bool _speechEnabled = false; bool _isListening = false; String _currentLocaleId = ''; TextEditingController? _currentController; // Getters for speech state bool get speechEnabled => _speechEnabled; bool get isListening => _isListening; // Getter for ticketId String get getTicketId => _ticketId ?? ''; // Setter for ticketId void setTicketId(String ticketId) { _ticketId = ticketId; notifyListeners(); } // Create request screen state EtqanGetProjectsResponse? selectedProject; String? selectedEmployeeNumber; File? attachment; String? attachmentBase64; String description = ''; bool isAnonymous = false; /// Initialize speech recognition - call this once when screen loads Future initSpeech() async { _speechEnabled = await _speechToText.initialize( onStatus: (String status) { print('Speech status: $status, isListening: $_isListening'); // If user wants to keep listening but speech stopped, restart it if ((status == 'done' || status == 'notListening') && _isListening && _currentController != null) { // Automatically restart listening after a brief pause Future.delayed(const Duration(milliseconds: 200), () { if (_isListening && _currentController != null) { _startListeningInternal(_currentLocaleId, _currentController!); } }); } }, ); notifyListeners(); } /// Internal method to start listening Future _startListeningInternal(String localeId, TextEditingController controller) async { if (!_speechEnabled) return; await _speechToText.listen( onResult: (SpeechRecognitionResult result) { description = result.recognizedWords; controller.text = result.recognizedWords; controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length)); notifyListeners(); }, localeId: localeId, listenFor: const Duration(seconds: 60), pauseFor: const Duration(seconds: 10), listenOptions: SpeechListenOptions( partialResults: true, listenMode: ListenMode.confirmation, cancelOnError: false, ), ); } /// Start listening for speech input Future startListening(String localeId, TextEditingController controller) async { _isListening = true; _currentLocaleId = localeId; _currentController = controller; notifyListeners(); await _startListeningInternal(localeId, controller); } /// Stop listening for speech input Future stopListening() async { _isListening = false; _currentLocaleId = ''; _currentController = null; await _speechToText.stop(); notifyListeners(); } /// Toggle listening state Future toggleListening(String localeId, TextEditingController controller) async { if (_isListening) { await stopListening(); } else { await startListening(localeId, controller); } } // Fetch Etqan Projects Future fetchEtqanProjects(BuildContext context) async { isLoading = true; notifyListeners(); Utils.showLoading(context); getEtqanProjectsList?.clear(); getEtqanProjectsList = await EtqanApiClient().getEtqanProjects(); Utils.hideLoading(context); isLoading = false; notifyListeners(); } // Fetch Etqan Employee Requests Future fetchEtqanEmployeeRequests(BuildContext context) async { isLoading = true; notifyListeners(); Utils.showLoading(context); getEtqanEmployeeRequestsList?.clear(); getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); Utils.hideLoading(context); isLoading = false; notifyListeners(); } // Fetch Etqan Employee Incident Report Future fetchEtqanEmployeeIncidentRequests(BuildContext context, String ticketNo) async { isLoading = true; notifyListeners(); Utils.showLoading(context); getEtqanEmployeeIncidnetRequest?.clear(); getEtqanEmployeeIncidnetRequest = await EtqanApiClient().getEtqanIncidentRequests(ticketNo); Utils.hideLoading(context); isLoading = false; notifyListeners(); } // Set selected project void setSelectedProject(EtqanGetProjectsResponse? project) { selectedProject = project; notifyListeners(); } // Set selected employee number void setSelectedEmployeeNumber(String? employeeNumber) { selectedEmployeeNumber = employeeNumber; notifyListeners(); } // Set attachment with base64 void setAttachment(File file, String base64) { attachment = file; attachmentBase64 = base64; notifyListeners(); } // Remove attachment void removeAttachment() { attachment = null; attachmentBase64 = null; notifyListeners(); } // Set description void setDescription(String value) { description = value; notifyListeners(); } // Validate description bool isDescriptionValid() { return description.trim().isNotEmpty; } // Set isAnonymous void setIsAnonymous(bool value) { isAnonymous = value; notifyListeners(); } // Clear create request form void clearCreateRequestForm() { selectedProject = null; selectedEmployeeNumber = null; attachment = null; attachmentBase64 = null; description = ''; isAnonymous = false; notifyListeners(); } // Submit request Future?> submitRequest(BuildContext context) async { try { Utils.showLoading(context); Map? response = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous); Utils.hideLoading(context); if (response != null) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); notifyListeners(); return response; } return null; } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); return null; } } @override void dispose() { if (_isListening) { _speechToText.stop(); } super.dispose(); } }