// ignore_for_file: body_might_complete_normally_catch_error import 'dart:async'; import 'dart:convert'; import 'package:didvan/main.dart'; import 'package:didvan/models/ai/files_model.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:provider/provider.dart'; import 'package:didvan/models/ai/bots_model.dart'; import 'package:didvan/models/ai/chats_model.dart'; import 'package:didvan/models/ai/messages_model.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/providers/core.dart'; import 'package:didvan/services/ai/ai_api_service.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; class AiChatState extends CoreProvier { ValueNotifier> messageOnstream = ValueNotifier(const Stream.empty().asBroadcastStream()); List messages = []; bool onResponsing = false; bool loading = false; bool isEdite = false; ValueNotifier changingPlaceHolder = ValueNotifier(false); final ScrollController scrollController = ScrollController(); int? chatId; ChatsModel? chat; FilesModel? file; bool isRecorded = false; TextEditingController message = TextEditingController(); Future _scrolledEnd() async { WidgetsBinding.instance.addPostFrameCallback((_) async { await scrollController.animateTo( scrollController.position.minScrollExtent, duration: const Duration(milliseconds: 600), curve: Curves.easeInOut, ); }); } Future _onError(e) async { onResponsing = false; messages.last.prompts.removeLast(); messages.last.prompts.last = messages.last.prompts.last.copyWith(error: true); messageOnstream.value = const Stream.empty(); await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData( message: 'خطا در برقراری ارتباط', aLertType: ALertType.error)); update(); } Future getChatId() async { final service = RequestService( RequestHelper.aiChatId(), ); await service.httpGet(); if (service.isSuccess) { final id = service.result['id']; chatId = id; chat ??= ChatsModel(id: chatId); } return service; } Future getAllMessages(int chatId) async { loading = true; onResponsing = true; update(); final service = RequestService( RequestHelper.aiAChat(chatId), ); await service.httpGet(); if (service.isSuccess) { messages.clear(); final ChatsModel allMessages = ChatsModel.fromJson(service.result['chat']); for (var i = 0; i < allMessages.prompts!.length; i++) { final prompt = allMessages.prompts![i]; if (messages.isNotEmpty && DateTime.parse(messages.last.prompts.last.createdAt.toString()) .toPersianDateStr() .contains(DateTime.parse(prompt.createdAt.toString()) .toPersianDateStr())) { messages.last.prompts.add(prompt); } else { messages.add(MessageModel( dateTime: prompt.createdAt.toString(), prompts: [prompt])); } } appState = AppState.idle; loading = false; onResponsing = false; update(); return; } appState = AppState.failed; loading = false; onResponsing = false; update(); } Future changePlaceHolder(String placeholder) async { changingPlaceHolder.value = true; update(); await Future.delayed(const Duration(seconds: 3)); final service = RequestService(RequestHelper.placeholder(chatId!), body: {'placeholder': placeholder}); await service.put(); if (service.isSuccess) { appState = AppState.idle; if (chat == null) { chat = ChatsModel(id: chatId, placeholder: placeholder); } else { chat = chat!.copyWith(placeholder: placeholder); } changingPlaceHolder.value = false; update(); return; } appState = AppState.failed; changingPlaceHolder.value = false; update(); } Future postMessage(BotsModel bot) async { onResponsing = true; final uploadedFile = file; file = null; update(); String message = messages.last.prompts.last.text!; messages.last.prompts.add(Prompts( finished: false, error: false, text: '...', role: 'bot', createdAt: DateTime.now() .subtract(const Duration(minutes: 210)) .toIso8601String())); update(); await _scrolledEnd(); // if (file != null) { // html.AnchorElement anchorElement = html.AnchorElement(href: file!.path); // anchorElement.download = '${file!.path}.m4a'; // anchorElement.click(); // } final req = await AiApiService.initial( url: '/${bot.id}/${bot.name}'.toLowerCase(), message: message, chatId: chatId, file: uploadedFile, edite: isEdite); file = null; isRecorded = false; update(); final res = await AiApiService().getResponse(req).catchError((e) { _onError(e); // return e; }); String responseMessgae = ''; String dataMessgae = ''; update(); final r = res.listen((value) async { var str = utf8.decode(value); if (!kIsWeb) { if (bot.id == 12) { responseMessgae += str.split('{{{').first; } if (str.contains('{{{')) { dataMessgae += "{{{${str.split('{{{').last}"; update(); return; } } responseMessgae += str; if (kIsWeb) { try { int startIndex = responseMessgae.indexOf('{{{'); // + 3 to include the }}} characters String slicedText = responseMessgae.substring(startIndex, responseMessgae.length); dataMessgae = slicedText; responseMessgae = responseMessgae.replaceAll(dataMessgae, ''); } catch (e) { e.printError(); } if (bot.id == 12) { responseMessgae = "${responseMessgae.split('.png').first}.png"; return; } } messageOnstream.value = Stream.value(responseMessgae); update(); }); r.onDone(() async { if (chatId == null) { final service = await getChatId(); navigatorKey.currentContext!.read().getChats(); if (!service.isSuccess) { _onError(null); return; } } onResponsing = false; if (responseMessgae.isEmpty) { messages.last.prompts.removeLast(); messages.last.prompts.last = messages.last.prompts.last.copyWith(error: true); messageOnstream.value = const Stream.empty(); update(); _scrolledEnd(); return; } else { int? humanMessageId; int? aiMessageId; try { final data = AppInitializer.messagesData(dataMessgae); humanMessageId = data['HUMAN_MESSAGE_ID']; aiMessageId = data['AI_MESSAGE_ID']; } catch (e) { e.printError(); return; } messages.last.prompts.last = messages.last.prompts.last.copyWith( finished: true, text: bot.id == 12 ? null : responseMessgae, file: bot.id == 12 ? responseMessgae : null, id: aiMessageId); if (messages.last.prompts.length > 2) { messages.last.prompts[messages.last.prompts.length - 2] = messages .last.prompts[messages.last.prompts.length - 2] .copyWith(id: humanMessageId); } else { messages.last.prompts.first = messages.last.prompts.first.copyWith(id: humanMessageId); } messageOnstream.value = const Stream.empty(); } if (isEdite) { messages.last.prompts.removeAt((messages.last.prompts.length - 1) - 2); messages.last.prompts.removeAt((messages.last.prompts.length - 1) - 2); isEdite = false; } update(); _scrolledEnd(); }); r.onError((e) => _onError(e)); } Future deleteMessage(int id, int mIndex, int index) async { final service = RequestService(RequestHelper.deleteMessage(chatId!, id)); await service.delete(); if (service.isSuccess) { if (messages[mIndex].prompts.length <= 1) { messages.removeAt(mIndex); } else { messages[mIndex].prompts.removeAt(index); } appState = AppState.idle; update(); return; } appState = AppState.failed; await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData( message: 'خطا در برقراری ارتباط', aLertType: ALertType.error)); update(); } }