diff --git a/android/app/build.gradle b/android/app/build.gradle index a8078dc..49ffd2f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -57,7 +57,7 @@ android { applicationId "com.didvan.didvanapp" minSdkVersion 24 //noinspection ExpiredTargetSdkVersion - targetSdkVersion 31 + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/lib/constants/assets.dart b/lib/constants/assets.dart index dd6ffb1..bf28018 100644 --- a/lib/constants/assets.dart +++ b/lib/constants/assets.dart @@ -43,8 +43,10 @@ class Assets { static String loadingAnimation = '$_baseAnimationsPath/loading.gif'; static String bookmarkAnimation = '$_baseAnimationsPath/bookmark.gif'; - static String boxAnimation = - '$_baseAnimationsPath/onlinegiftools-$_themeSuffix.gif'; + static String boxAnimationLight = + '$_baseAnimationsPath/onlinegiftools-light.gif'; + static String boxAnimationDark = + '$_baseAnimationsPath/onlinegiftools-dark.gif'; static String get businessCategoryIcon => '$_baseCategoriesPath/business-$_themeSuffix.svg'; diff --git a/lib/main.dart b/lib/main.dart index 6173056..4a69785 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,7 +20,6 @@ import 'package:didvan/services/notification/notification_service.dart'; import 'package:didvan/utils/my_custom_scroll_behavior.dart'; import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/ai/bot_assistants_state.dart'; -import 'package:didvan/views/ai/create_bot_assistants_state.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/tools_state.dart'; import 'package:didvan/views/podcasts/podcasts_state.dart'; @@ -180,9 +179,6 @@ class _DidvanState extends State with WidgetsBindingObserver { ChangeNotifierProvider( create: (context) => ToolsState(), ), - ChangeNotifierProvider( - create: (context) => CreateBotAssistantsState(), - ), ChangeNotifierProvider( create: (context) => BotAssistantsState(), ), diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 9108744..520068c 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -5,6 +5,7 @@ import 'package:didvan/views/ai/ai_chat_page.dart'; import 'package:didvan/views/ai/ai_chat_state.dart'; import 'package:didvan/views/ai/bot_assistants_page.dart'; import 'package:didvan/views/ai/create_bot_assistants_page.dart'; +import 'package:didvan/views/ai/create_bot_assistants_state.dart'; import 'package:didvan/views/ai/history_ai_chat_page.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/info_page.dart'; @@ -322,8 +323,18 @@ class RouteGenerator { case Routes.botAssistants: return _createRoute(const BotAssistantsPage()); case Routes.createBotAssistants: - return _createRoute(CreateBotAssistantsPage( - id: settings.arguments as int?, + return _createRoute(ChangeNotifierProvider( + create: (context) { + final CreateBotAssistantsState pr = CreateBotAssistantsState(); + final id = settings.arguments as int?; + if (id != null) { + pr.getAnAssistant(id: id); + } + return pr; + }, + child: CreateBotAssistantsPage( + id: settings.arguments as int?, + ), )); case Routes.info: return _createRoute(const InfoPage()); diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart index 532899c..7c68706 100644 --- a/lib/views/ai/ai.dart +++ b/lib/views/ai/ai.dart @@ -76,21 +76,10 @@ class _AiState extends State { Expanded( child: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.only(bottom: 24), + padding: + const EdgeInsets.only(bottom: 24, top: 32), child: Column( children: [ - const SizedBox( - height: 24, - ), - Icon( - DidvanIcons.ai_solid, - size: MediaQuery.sizeOf(context).width / 5, - color: Theme.of(context).colorScheme.title, - ), - DidvanText( - 'هوشان', - color: Theme.of(context).colorScheme.title, - ), const SizedBox( height: 24, ), diff --git a/lib/views/ai/ai_chat_page.dart b/lib/views/ai/ai_chat_page.dart index 440ac7f..ab3bb45 100644 --- a/lib/views/ai/ai_chat_page.dart +++ b/lib/views/ai/ai_chat_page.dart @@ -19,6 +19,7 @@ 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/utils/date_time.dart'; +import 'package:didvan/utils/extension.dart'; import 'package:didvan/views/ai/ai_chat_state.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/widgets/ai_message_bar.dart'; @@ -342,23 +343,30 @@ class _AiChatPageState extends State { if (!snapshot.hasData) { return const SizedBox(); } - return Markdown( - data: "${snapshot.data}...", - onTapLink: (text, href, title) { - if (href != null) { - launchUrlString( - href, - mode: - LaunchMode.inAppBrowserView, - ); - } - }, - selectable: false, - shrinkWrap: true, - physics: - const NeverScrollableScrollPhysics(), - styleSheet: - defaultMarkdownStyleSheet); + return Directionality( + textDirection: snapshot.data + .toString() + .startsWithEnglish() + ? TextDirection.ltr + : TextDirection.rtl, + child: Markdown( + data: "${snapshot.data}...", + onTapLink: (text, href, title) { + if (href != null) { + launchUrlString( + href, + mode: LaunchMode + .inAppBrowserView, + ); + } + }, + selectable: false, + shrinkWrap: true, + physics: + const NeverScrollableScrollPhysics(), + styleSheet: + defaultMarkdownStyleSheet), + ); }, ); }), @@ -417,20 +425,26 @@ class _AiChatPageState extends State { ((message.audio == null || (message.audio != null && !message.audio!)))) - Markdown( - data: message.text.toString(), - onTapLink: (text, href, title) { - if (href != null) { - launchUrlString( - href, - mode: LaunchMode.inAppBrowserView, - ); - } - }, - selectable: true, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - styleSheet: defaultMarkdownStyleSheet, + Directionality( + textDirection: + message.text.toString().startsWithEnglish() + ? TextDirection.ltr + : TextDirection.rtl, + child: Markdown( + data: message.text.toString(), + onTapLink: (text, href, title) { + if (href != null) { + launchUrlString( + href, + mode: LaunchMode.inAppBrowserView, + ); + } + }, + selectable: true, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + styleSheet: defaultMarkdownStyleSheet, + ), ), Padding( padding: @@ -438,7 +452,10 @@ class _AiChatPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - if (message.role.toString().contains('user')) + if (message.role + .toString() + .contains('user') && + widget.args.assistantsName == null) PopupMenuButton( offset: const Offset(0, 46), onSelected: (value) async { @@ -446,7 +463,12 @@ class _AiChatPageState extends State { Routes.aiChat, arguments: AiChatArgs( bot: value, - prompts: message)); + prompts: message, + isTool: widget.args.isTool ?? + context + .read< + HistoryAiChatState>() + .bots)); }, itemBuilder: (BuildContext context) { final bots = widget.args.isTool ?? diff --git a/lib/views/ai/bot_assistants_page.dart b/lib/views/ai/bot_assistants_page.dart index f4c3375..2608d3a 100644 --- a/lib/views/ai/bot_assistants_page.dart +++ b/lib/views/ai/bot_assistants_page.dart @@ -6,7 +6,6 @@ import 'package:didvan/models/ai/bot_assistants_model.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/ai/bot_assistants_state.dart'; -import 'package:didvan/views/ai/create_bot_assistants_state.dart'; import 'package:didvan/views/widgets/didvan/button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/hoshan_app_bar.dart'; @@ -52,22 +51,25 @@ class _BotAssistantsPageState extends State { (BuildContext context, BotAssistantsState state, Widget? child) => CustomScrollView( slivers: [ - const SliverToBoxAdapter( + SliverToBoxAdapter( child: Center( child: Padding( - padding: EdgeInsets.only(top: 32, bottom: 24), + padding: const EdgeInsets.only(top: 32, bottom: 24), child: DidvanText( 'انتخاب بات‌ها', fontSize: 20, fontWeight: FontWeight.bold, - color: Color(0xff1B3C59), + color: Theme.of(context).colorScheme.checkFav, ), ), ), ), if (state.appState != AppState.failed) SliverToBoxAdapter( - child: switchAssistants(context, state), + child: Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: switchAssistants(context, state), + ), ), SliverStateHandler( childCount: state.isMyAssistants @@ -101,8 +103,9 @@ class _BotAssistantsPageState extends State { return Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 32), - decoration: const BoxDecoration( - color: Colors.white, borderRadius: DesignConfig.lowBorderRadius), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: DesignConfig.lowBorderRadius), child: Column( children: [ if (state.isMyAssistants) @@ -188,8 +191,8 @@ class _BotAssistantsPageState extends State { padding: const EdgeInsets.all(12), margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 32), decoration: BoxDecoration( - border: Border.all(color: const Color(0xffe0e0e0)), - color: Colors.white, + border: Border.all(color: Theme.of(context).colorScheme.border), + color: Theme.of(context).colorScheme.surface, borderRadius: DesignConfig.lowBorderRadius), child: Column( children: [ @@ -265,10 +268,6 @@ class _BotAssistantsPageState extends State { child: state.isMyAssistants ? InkWell( onTap: () async { - context - .read() - .getAnAssistant(id: assistants.id!); - Navigator.pushNamed( context, Routes.createBotAssistants, arguments: assistants.id); @@ -325,8 +324,10 @@ class _BotAssistantsPageState extends State { title: 'استفاده از دستیار', onPressed: () => Navigator.pushNamed(context, Routes.aiChat, arguments: AiChatArgs( - bot: assistants.bot! - .copyWith(id: assistants.id, image: assistants.image), + bot: assistants.bot!.copyWith( + id: assistants.id, + image: assistants.image, + description: assistants.description), assistantsName: assistants.name)), ) ], @@ -338,8 +339,9 @@ class _BotAssistantsPageState extends State { return Container( margin: const EdgeInsets.symmetric(horizontal: 32), padding: const EdgeInsets.all(12), - decoration: const BoxDecoration( - color: Colors.white, borderRadius: DesignConfig.lowBorderRadius), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: DesignConfig.lowBorderRadius), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/views/ai/create_bot_assistants_page.dart b/lib/views/ai/create_bot_assistants_page.dart index fe6421d..ed23803 100644 --- a/lib/views/ai/create_bot_assistants_page.dart +++ b/lib/views/ai/create_bot_assistants_page.dart @@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/models/ai/bot_assistants_model.dart'; import 'package:didvan/models/ai/bot_assistants_req_model.dart'; import 'package:didvan/models/ai/bots_model.dart'; import 'package:didvan/models/ai/file_create_assistants_model.dart'; @@ -20,7 +19,6 @@ import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/extension.dart'; import 'package:didvan/views/ai/bot_assistants_state.dart'; import 'package:didvan/views/ai/create_bot_assistants_state.dart'; -import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/widgets/didvan/button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/switch.dart'; @@ -50,27 +48,12 @@ class _CreateBotAssistantsPageState extends State { borderRadius: DesignConfig.mediumBorderRadius, borderSide: BorderSide( width: 1, color: Theme.of(context).colorScheme.disabledText)); - late List allBots = context.read().bots; - - final List botModels = ['مدل زبانی', 'مدل تصویری']; // final _formYouTubeKey = GlobalKey(); final _formNameKey = GlobalKey(); final _formPromptKey = GlobalKey(); final _formDescKey = GlobalKey(); - String name = ''; - String prompt = ''; - String desc = ''; - // String? youtubeLink; - - List countOfLink = ['']; - List files = []; - ValueNotifier image = ValueNotifier(null); - String selectedBotType = 'text'; - bool isPrivate = true; - BotAssistants? assistant; - BotsModel? initialBot; Timer? _timer; @override @@ -100,7 +83,7 @@ class _CreateBotAssistantsPageState extends State { return; } final List resultFiles = []; - for (var file in files) { + for (var file in state.files) { if (!file.fromNetwork) { resultFiles.add(file.file!); } @@ -109,19 +92,20 @@ class _CreateBotAssistantsPageState extends State { final success = await state.createAssistants( id: widget.id, data: BotAssistantsReqModel( - type: selectedBotType, - name: name, - description: desc, + type: state.selectedBotType, + name: state.name, + description: state.desc, botId: selectedBotId, - prompt: prompt, - webLinks: countOfLink.first.isEmpty ? null : countOfLink, + prompt: state.prompt, + webLinks: + state.countOfLink.first.isEmpty ? null : state.countOfLink, // youtubeLink: youtubeLink, - isPrivate: isPrivate, + isPrivate: state.isPrivate, files: resultFiles, - deleteImage: assistant != null - ? assistant!.image == null - : image.value == null, - image: image.value)); + deleteImage: state.assistant != null + ? state.assistant!.image == null + : state.image.value == null, + image: state.image.value)); if (success) { context.read().getMyAssissmant(); context.read().assistant = null; @@ -135,10 +119,6 @@ class _CreateBotAssistantsPageState extends State { @override Widget build(BuildContext context) { - int selectedBotId = selectedBotType == 'text' - ? allBots.first.id! - : context.read().imageBots.first.id!; - return WillPopScope( onWillPop: () async { context.read().assistant = null; @@ -155,34 +135,9 @@ class _CreateBotAssistantsPageState extends State { ), body: Consumer(builder: (BuildContext context, CreateBotAssistantsState state, Widget? child) { - if (assistant == null && state.assistant != null) { - assistant = state.assistant; - - name = assistant!.name ?? ''; - prompt = assistant!.prompt ?? ''; - desc = assistant!.description ?? ''; - // youtubeLink = assistant!.; - isPrivate = assistant!.private ?? true; - if (files.isEmpty && - assistant!.files != null && - assistant!.files!.isNotEmpty) { - for (var file in assistant!.files!) { - files.add(FileCreateAssistantsModel( - fromNetwork: true, file: null, url: file)); - } - } - countOfLink = assistant!.websites ?? ['']; - selectedBotType = assistant!.type ?? 'text'; - - final list = selectedBotType == 'text' ? allBots : state.imageBots; - for (var bot in list) { - if (bot.id == assistant!.botId) { - initialBot = bot; - break; - } - } - } - + int selectedBotId = state.selectedBotType == 'text' + ? state.allBots.first.id! + : context.read().imageBots.first.id!; return state.loading ? Center( child: SpinKitThreeBounce( @@ -202,9 +157,9 @@ class _CreateBotAssistantsPageState extends State { width: MediaQuery.sizeOf(context).width, child: CustomDropdown( closedHeaderPadding: const EdgeInsets.all(12), - items: botModels, - enabled: assistant == null, - initialItem: botModels[0], + items: state.botModels, + enabled: state.assistant == null, + initialItem: state.botModels[0], hideSelectedFieldWhenExpanded: false, decoration: CustomDropdownDecoration( listItemDecoration: ListItemDecoration( @@ -220,10 +175,9 @@ class _CreateBotAssistantsPageState extends State { Theme.of(context).colorScheme.surface), // hintText: "انتخاب کنید", onChanged: (value) { - setState(() { - final index = botModels.indexOf(value!); - selectedBotType = index == 0 ? 'text' : 'image'; - }); + final index = state.botModels.indexOf(value!); + state.selectedBotType = + index == 0 ? 'text' : 'image'; }, ), ), @@ -231,7 +185,7 @@ class _CreateBotAssistantsPageState extends State { height: 24, ), ValueListenableBuilder( - valueListenable: image, + valueListenable: state.image, builder: (context, img, _) { return Row( mainAxisAlignment: @@ -240,24 +194,24 @@ class _CreateBotAssistantsPageState extends State { DidvanButton( width: 120, style: ButtonStyleMode.flat, - color: image.value != null || - assistant?.image != null + color: state.image.value != null || + state.assistant?.image != null ? Theme.of(context).colorScheme.error : null, - title: image.value != null || - assistant?.image != null + title: state.image.value != null || + state.assistant?.image != null ? 'حذف عکس' : 'انتخاب عکس', onPressed: () async { - if (image.value != null || - assistant?.image != null) { - if (assistant != null) { - assistant!.image = null; + if (state.image.value != null || + state.assistant?.image != null) { + if (state.assistant != null) { + state.assistant!.image = null; } - image.value = null; + state.image.value = null; return; } - image.value = + state.image.value = await MediaService.pickImage( source: ImageSource.gallery); }, @@ -273,9 +227,9 @@ class _CreateBotAssistantsPageState extends State { Image.file(File(img.path))), ) : SkeletonImage( - imageUrl: assistant != null && - assistant!.image != null - ? assistant!.image! + imageUrl: state.assistant != null && + state.assistant!.image != null + ? state.assistant!.image! : 'https://via.placeholder.com/70x70', width: 80, height: 80, @@ -290,14 +244,14 @@ class _CreateBotAssistantsPageState extends State { Form( key: _formNameKey, child: DidvanTextField( - initialValue: name, + initialValue: state.name, onChanged: (value) { - name = value; + state.name = value; if (value.isEmpty) { return; } - if (assistant == null) { + if (state.assistant == null) { _timer?.cancel(); _timer = Timer(const Duration(seconds: 1), () async { @@ -312,7 +266,7 @@ class _CreateBotAssistantsPageState extends State { result = 'نام نباید خالی باشد'; } else if (value.length < 4) { result = 'نام نباید کمتر از 4 حرف باشد'; - } else if (assistant == null && + } else if (state.assistant == null && !state.successName) { result = 'اسم دیگری انتخاب کنید این اسم موجود است'; @@ -359,7 +313,7 @@ class _CreateBotAssistantsPageState extends State { Form( key: _formDescKey, child: DidvanTextField( - initialValue: desc, + initialValue: state.desc, // hintText: // 'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.', textInputType: TextInputType.multiline, @@ -369,7 +323,7 @@ class _CreateBotAssistantsPageState extends State { hasHeight: false, showLen: true, onChanged: (value) { - desc = value; + state.desc = value; }, validator: (value) { String? result; @@ -396,18 +350,18 @@ class _CreateBotAssistantsPageState extends State { width: MediaQuery.sizeOf(context).width, child: CustomDropdown( closedHeaderPadding: const EdgeInsets.all(12), - items: selectedBotType == 'text' - ? allBots + items: state.selectedBotType == 'text' + ? state.allBots : state.imageBots, headerBuilder: (context, bot, enabled) => botRow(bot, context), listItemBuilder: (context, bot, isSelected, onItemSelect) => botRow(bot, context), - initialItem: assistant != null - ? initialBot - : (selectedBotType == 'text' - ? allBots + initialItem: state.assistant != null + ? state.initialBot + : (state.selectedBotType == 'text' + ? state.allBots : state.imageBots) .first, hideSelectedFieldWhenExpanded: false, @@ -427,9 +381,7 @@ class _CreateBotAssistantsPageState extends State { .surface), // hintText: "انتخاب کنید", onChanged: (value) { - setState(() { - selectedBotId = value!.id!; - }); + selectedBotId = value!.id!; }, ), ), @@ -440,7 +392,7 @@ class _CreateBotAssistantsPageState extends State { Form( key: _formPromptKey, child: DidvanTextField( - initialValue: prompt, + initialValue: state.prompt, hintText: 'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.', textInputType: TextInputType.multiline, @@ -450,7 +402,7 @@ class _CreateBotAssistantsPageState extends State { hasHeight: false, showLen: true, onChanged: (value) { - prompt = value; + state.prompt = value; }, validator: (value) { String? result; @@ -466,15 +418,15 @@ class _CreateBotAssistantsPageState extends State { const SizedBox( height: 24, ), - if (assistant == null) + if (state.assistant == null) Column( children: [ - if (selectedBotType == 'text') + if (state.selectedBotType == 'text') Column( children: [ title( text: 'پایگاه دانش', isRequired: false), - if (files.length != 3) + if (state.files.length != 3) SizedBox( height: 48, child: ElevatedButton( @@ -492,11 +444,21 @@ class _CreateBotAssistantsPageState extends State { .pickMultiFile(); if (picks != null) { for (var file in picks.xFiles) { - files.add( - FileCreateAssistantsModel( - fromNetwork: false, - file: file, - url: null)); + if (file.path.isDocument() || + file.path.isAudio()) { + state.files.add( + FileCreateAssistantsModel( + fromNetwork: false, + file: file, + url: null)); + } else { + ActionSheetUtils(context) + .showAlert(AlertData( + message: + 'باید فایل انتخاب شده صوتی یا Pdf باشد', + aLertType: ALertType + .error)); + } } } state.update(); @@ -529,107 +491,125 @@ class _CreateBotAssistantsPageState extends State { MainAxisAlignment.spaceEvenly, children: [ ...List.generate( - files.length, + state.files.length, (index) { - return Stack( - children: [ - Container( - width: MediaQuery.sizeOf( - context) - .width / - 5, - height: MediaQuery.sizeOf( - context) - .width / - 5, - margin: const EdgeInsets - .symmetric( - horizontal: 8, - vertical: 12), - padding: const EdgeInsets - .all(8), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .disabledBackground, - borderRadius: DesignConfig - .lowBorderRadius), - child: Column( - children: [ - Expanded( - child: files[index] - .fromNetwork - ? files[index] - .url! - .isImage() - ? CachedNetworkImage( - imageUrl: - files[index] - .url!) - : const Icon( - CupertinoIcons - .doc) - : files[index] - .file! - .path - .isImage() - ? Image.file( - File(files[ - index] - .file! - .path)) - : const Icon( - CupertinoIcons - .doc), - ), - MarqueeText( - text: files[index] + return SizedBox( + child: Stack( + children: [ + Container( + width: MediaQuery + .sizeOf( + context) + .width / + 5, + height: MediaQuery.sizeOf( + context) + .width / + 5, + margin: const EdgeInsets + .symmetric( + horizontal: 8, + vertical: 12), + padding: + const EdgeInsets.all( + 8), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .disabledBackground, + borderRadius: DesignConfig + .lowBorderRadius), + child: Column( + children: [ + Expanded( + child: state + .files[ + index] .fromNetwork - ? files[index] - .url! - .split('/') - .last - : files[index] - .file! - .name, - textDirection: - TextDirection - .rtl, - style: Theme.of( - context) - .textTheme - .labelSmall!) - ], - )), - Positioned( - top: 8, - left: 4, - child: InkWell( - onTap: () { - files.removeAt(index); - state.update(); - }, - child: Container( - padding: - const EdgeInsets - .all(6), - decoration: - BoxDecoration( - shape: BoxShape - .circle, - color: Theme.of( - context) - .colorScheme - .error), - child: const Icon( - DidvanIcons - .trash_solid, - color: Colors.white, - size: 18, + ? state + .files[ + index] + .url! + .isImage() + ? CachedNetworkImage( + imageUrl: state + .files[ + index] + .url!) + : const Icon( + CupertinoIcons + .doc) + : state + .files[ + index] + .file! + .path + .isImage() + ? Image.file(File(state + .files[ + index] + .file! + .path)) + : const Icon( + CupertinoIcons + .doc), + ), + MarqueeText( + text: state + .files[ + index] + .fromNetwork + ? state + .files[ + index] + .url! + .split( + '/') + .last + : state + .files[ + index] + .file! + .name, + textDirection: + TextDirection + .rtl, + style: Theme.of( + context) + .textTheme + .labelSmall!) + ], + )), + Positioned( + top: 8, + left: 4, + child: InkWell( + onTap: () { + state.files + .removeAt(index); + state.update(); + }, + child: Container( + padding: + const EdgeInsets + .all(6), + decoration: BoxDecoration( + shape: BoxShape + .circle, + color: Theme.of( + context) + .colorScheme + .error), + child: const Icon( + DidvanIcons + .trash_solid, + color: Colors.white, + size: 18, + ), ), - ), - )) - ], + )) + ], + ), ); }, ) @@ -666,25 +646,24 @@ class _CreateBotAssistantsPageState extends State { text: 'لینک وب سایت', isRequired: false), Row( children: [ - if (countOfLink.length > 1) + if (state.countOfLink.length > 1) DidvanIconButton( icon: CupertinoIcons.minus_circle_fill, onPressed: () { - setState(() { - countOfLink.removeLast(); - }); + state.countOfLink.removeLast(); + state.update(); }, ), - if (countOfLink.length != 3) + if (state.countOfLink.length != 3) DidvanIconButton( icon: CupertinoIcons.plus_circle_fill, onPressed: () { - setState(() { - if (countOfLink.last.isNotEmpty) { - countOfLink.add(''); - } - }); + if (state + .countOfLink.last.isNotEmpty) { + state.countOfLink.add(''); + state.update(); + } }, ), ], @@ -693,13 +672,18 @@ class _CreateBotAssistantsPageState extends State { ), ListView.builder( shrinkWrap: true, - itemCount: countOfLink.length, + itemCount: state.countOfLink.length, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => Column( children: [ DidvanTextField( onChanged: (value) { - countOfLink.insert(index, value); + state.countOfLink[index] = value; + if (state.countOfLink[index].isEmpty && + state.countOfLink.length != 1) { + state.countOfLink.removeAt(index); + } + state.update(); }, // validator: (value) {}, hintText: @@ -766,10 +750,10 @@ class _CreateBotAssistantsPageState extends State { width: 64, height: 48, child: DidvanSwitch( - value: !isPrivate, + value: !state.isPrivate, title: '', onChanged: (value) { - isPrivate = !value; + state.isPrivate = !value; }, ), ), @@ -853,7 +837,7 @@ class _CreateBotAssistantsPageState extends State { ), ], ) - : Column( + : Stack( children: [ DidvanButton( title: diff --git a/lib/views/ai/create_bot_assistants_state.dart b/lib/views/ai/create_bot_assistants_state.dart index 5ea65ee..c9d1328 100644 --- a/lib/views/ai/create_bot_assistants_state.dart +++ b/lib/views/ai/create_bot_assistants_state.dart @@ -1,11 +1,17 @@ +import 'package:didvan/main.dart'; import 'package:didvan/models/ai/bot_assistants_model.dart'; import 'package:didvan/models/ai/bot_assistants_req_model.dart'; import 'package:didvan/models/ai/bots_model.dart'; +import 'package:didvan/models/ai/file_create_assistants_model.dart'; import 'package:didvan/models/ai/tools_model.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; +import 'package:didvan/views/ai/history_ai_chat_state.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:provider/provider.dart'; class CreateBotAssistantsState extends CoreProvier { List imageBots = []; @@ -15,6 +21,21 @@ class CreateBotAssistantsState extends CoreProvier { bool successName = false; bool loading = false; BotAssistants? assistant; + String name = ''; + String prompt = ''; + String desc = ''; + // String? youtubeLink; + late List allBots = + navigatorKey.currentContext!.read().bots; + + final List botModels = ['مدل زبانی', 'مدل تصویری']; + BotsModel? initialBot; + + List countOfLink = ['']; + List files = []; + ValueNotifier image = ValueNotifier(null); + String selectedBotType = 'text'; + bool isPrivate = true; void getImageToolsBots() async { loadingImageBots = true; @@ -77,6 +98,31 @@ class CreateBotAssistantsState extends CoreProvier { await service.httpGet(); if (service.isSuccess) { assistant = BotAssistants.fromJson(service.result['bot']); + if (assistant != null) { + name = assistant!.name ?? ''; + prompt = assistant!.prompt ?? ''; + desc = assistant!.description ?? ''; + // youtubeLink = assistant!.; + isPrivate = assistant!.private ?? true; + if (files.isEmpty && + assistant!.files != null && + assistant!.files!.isNotEmpty) { + for (var file in assistant!.files!) { + files.add(FileCreateAssistantsModel( + fromNetwork: true, file: null, url: file)); + } + } + countOfLink = assistant!.websites ?? ['']; + selectedBotType = assistant!.type ?? 'text'; + + final list = selectedBotType == 'text' ? allBots : imageBots; + for (var bot in list) { + if (bot.id == assistant!.botId) { + initialBot = bot; + break; + } + } + } appState = AppState.idle; loading = false; update(); diff --git a/lib/views/ai/info_page.dart b/lib/views/ai/info_page.dart index 078ba3d..057388a 100644 --- a/lib/views/ai/info_page.dart +++ b/lib/views/ai/info_page.dart @@ -1,4 +1,5 @@ import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; @@ -25,14 +26,14 @@ class _InfoPageState extends State { body: SingleChildScrollView( child: Column( children: [ - const Center( + Center( child: Padding( - padding: EdgeInsets.only(top: 32, bottom: 24), + padding: const EdgeInsets.only(top: 32, bottom: 24), child: DidvanText( 'آموزش پرامپت نویسی اصولی', fontSize: 20, fontWeight: FontWeight.bold, - color: Color(0xff1B3C59), + color: Theme.of(context).colorScheme.checkFav, ), ), ), diff --git a/lib/views/ai/tool_screen.dart b/lib/views/ai/tool_screen.dart index ba6c030..be8cc1f 100644 --- a/lib/views/ai/tool_screen.dart +++ b/lib/views/ai/tool_screen.dart @@ -1,4 +1,5 @@ import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/tools_model.dart'; import 'package:didvan/routes/routes.dart'; @@ -40,7 +41,7 @@ class _ToolScreenState extends State { tool.name!, fontSize: 20, fontWeight: FontWeight.bold, - color: const Color(0xff1B3C59), + color: Theme.of(context).colorScheme.checkFav, ), const SizedBox(height: 8), Padding( @@ -48,7 +49,7 @@ class _ToolScreenState extends State { child: DidvanText( tool.guide!, fontSize: 12, - color: const Color(0xff666666), + color: Theme.of(context).colorScheme.caption, ), ), const SizedBox(height: 24), @@ -88,7 +89,7 @@ class _ToolScreenState extends State { bot.name!, fontSize: 16, fontWeight: FontWeight.bold, - color: const Color(0xff2A282F), + color: Theme.of(context).colorScheme.text, ), if (bot.description != null) Column( diff --git a/lib/views/ai/tools_screen.dart b/lib/views/ai/tools_screen.dart index d5d9338..56f43bc 100644 --- a/lib/views/ai/tools_screen.dart +++ b/lib/views/ai/tools_screen.dart @@ -1,4 +1,5 @@ import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/ai/tools_state.dart'; @@ -32,13 +33,13 @@ class _ToolsScreenState extends State { : const BouncingScrollPhysics(), child: Column( children: [ - const Padding( - padding: EdgeInsets.only(top: 46, bottom: 24), + Padding( + padding: const EdgeInsets.only(top: 46, bottom: 24), child: DidvanText( 'انتخاب بات‌ها', fontSize: 20, fontWeight: FontWeight.bold, - color: Color(0xff1B3C59), + color: Theme.of(context).colorScheme.checkFav, ), ), Consumer( @@ -68,8 +69,9 @@ class _ToolsScreenState extends State { child: Container( decoration: BoxDecoration( borderRadius: DesignConfig.lowBorderRadius, + color: Theme.of(context).colorScheme.surface, border: Border.all( - color: const Color(0xffB8B8B8), + color: Theme.of(context).colorScheme.border, ), ), padding: const EdgeInsets.all(24), @@ -125,7 +127,9 @@ class _ToolsScreenState extends State { color: Theme.of(context).colorScheme.primary, ), DidvanText(tool.description!, - fontSize: 12, color: const Color(0xffA8A6AC)) + fontSize: 12, + color: + Theme.of(context).colorScheme.caption) ], ), ) diff --git a/lib/views/ai/widgets/ai_message_bar.dart b/lib/views/ai/widgets/ai_message_bar.dart index 24fa330..016c208 100644 --- a/lib/views/ai/widgets/ai_message_bar.dart +++ b/lib/views/ai/widgets/ai_message_bar.dart @@ -1,6 +1,7 @@ // ignore_for_file: library_private_types_in_public_api, avoid_web_libraries_in_flutter, deprecated_member_use import 'dart:async'; +import 'package:didvan/utils/extension.dart'; import 'package:record/record.dart'; import 'package:universal_html/html.dart' as html; import 'dart:io'; @@ -378,45 +379,52 @@ class _AiMessageBarState extends State { Expanded( child: state.file != null && state.file!.isRecorded ? audioContainer() - : TextFormField( - textInputAction: TextInputAction.newline, - style: Theme.of(context).textTheme.bodyMedium, - minLines: 1, - maxLines: 6, // Set this - keyboardType: TextInputType.multiline, - controller: state.message, + : Directionality( + textDirection: state.message.text + .toString() + .startsWithEnglish() + ? TextDirection.ltr + : TextDirection.rtl, + child: TextFormField( + textInputAction: TextInputAction.newline, + style: Theme.of(context).textTheme.bodyMedium, + minLines: 1, + maxLines: 6, // Set this + keyboardType: TextInputType.multiline, + controller: state.message, - enabled: !(state.file != null && - widget.bot.attachment == 1), - decoration: InputDecoration( - contentPadding: - const EdgeInsets.fromLTRB(12, 12, 12, 0), - border: InputBorder.none, - hintText: 'بنویسید...', - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - color: Theme.of(context) - .colorScheme - .disabledText), - suffixIcon: state.isEdite - ? InkWell( - onTap: () { - state.isEdite = false; - state.update(); - }, - child: const Icon( - DidvanIcons.close_circle_solid), - ) - : const SizedBox(), + enabled: !(state.file != null && + widget.bot.attachment == 1), + decoration: InputDecoration( + contentPadding: + const EdgeInsets.fromLTRB(12, 12, 12, 12), + border: InputBorder.none, + hintText: 'بنویسید...', + hintStyle: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: Theme.of(context) + .colorScheme + .disabledText), + suffixIcon: state.isEdite + ? InkWell( + onTap: () { + state.isEdite = false; + state.update(); + }, + child: const Icon( + DidvanIcons.close_circle_solid), + ) + : const SizedBox(), + ), + + onChanged: (value) { + if (value.isEmpty || value.length == 1) { + state.update(); + } + }, ), - - onChanged: (value) { - if (value.isEmpty || value.length == 1) { - state.update(); - } - }, ), ), Padding( diff --git a/lib/views/ai/widgets/ai_message_bar_ios.dart b/lib/views/ai/widgets/ai_message_bar_ios.dart index 57ad762..7525887 100644 --- a/lib/views/ai/widgets/ai_message_bar_ios.dart +++ b/lib/views/ai/widgets/ai_message_bar_ios.dart @@ -14,6 +14,7 @@ import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/media/voice.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/date_time.dart'; +import 'package:didvan/utils/extension.dart'; import 'package:didvan/views/ai/ai_chat_state.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/widgets/audio_wave.dart'; @@ -391,68 +392,81 @@ class _AiMessageBarIOSState extends State { state.file!.isRecorded ? audioContainer() : Form( + child: Directionality( + textDirection: state + .message.text + .toString() + .startsWithEnglish() + ? TextDirection + .ltr + : TextDirection + .rtl, child: TextFormField( - textInputAction: - TextInputAction - .newline, - style: - Theme.of(context) - .textTheme - .bodyMedium, - minLines: 1, - maxLines: - 6, // Set this - // expands: true, // - // keyboardType: TextInputType.text, - keyboardType: - TextInputType - .multiline, - controller: - state.message, - - enabled: !(state - .file != - null && - widget.bot - .attachment == - 1), - - decoration: - InputDecoration( - border: InputBorder - .none, - hintText: - 'بنویسید...', - hintStyle: Theme.of( + textInputAction: + TextInputAction + .newline, + style: Theme.of( context) .textTheme - .bodySmall! - .copyWith( - color: Theme.of( - context) - .colorScheme - .disabledText), - suffixIcon: state - .isEdite - ? InkWell( - onTap: () { - state.isEdite = - false; - state - .update(); - }, - child: const Icon( - DidvanIcons - .close_circle_solid), - ) - : const SizedBox(), - ), + .bodyMedium, + minLines: 1, + maxLines: + 6, // Set this + // expands: true, // + // keyboardType: TextInputType.text, + keyboardType: + TextInputType + .multiline, + controller: + state.message, - onChanged: (value) { - messageText.value = - value; - state.update(); - }, + enabled: !(state + .file != + null && + widget.bot + .attachment == + 1), + + decoration: + InputDecoration( + border: + InputBorder + .none, + hintText: + 'بنویسید...', + hintStyle: Theme.of( + context) + .textTheme + .bodySmall! + .copyWith( + color: Theme.of( + context) + .colorScheme + .disabledText), + suffixIcon: state + .isEdite + ? InkWell( + onTap: + () { + state.isEdite = + false; + state + .update(); + }, + child: const Icon( + DidvanIcons + .close_circle_solid), + ) + : const SizedBox(), + ), + + onChanged: (value) { + messageText + .value = + value; + state.update(); + }, + ), )), ), ), diff --git a/lib/views/widgets/hoshan_app_bar.dart b/lib/views/widgets/hoshan_app_bar.dart index f434ae4..121bfe0 100644 --- a/lib/views/widgets/hoshan_app_bar.dart +++ b/lib/views/widgets/hoshan_app_bar.dart @@ -1,5 +1,7 @@ import 'dart:math'; +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/routes/routes.dart'; @@ -7,6 +9,7 @@ import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/ai/bot_assistants_state.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -37,15 +40,17 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Row( + Row( children: [ Icon( DidvanIcons.ai_solid, size: 40, + color: Theme.of(context).colorScheme.title, ), DidvanText( 'هوشان', fontSize: 14, + color: Theme.of(context).colorScheme.title, fontWeight: FontWeight.bold, ), ], @@ -60,14 +65,23 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { Navigator.pushNamed(context, Routes.info); }), if (withActions) - DidvanIconButton( - icon: DidvanIcons.antenna_light, - size: 32, - onPressed: () { - context.read().getMyAssissmant(); + Stack( + children: [ + DidvanIconButton( + icon: DidvanIcons.ai_regular, + size: 32, + onPressed: () { + context.read().getMyAssissmant(); - Navigator.pushNamed(context, Routes.botAssistants); - }, + Navigator.pushNamed(context, Routes.botAssistants); + }, + ), + Icon( + CupertinoIcons.plus, + color: Theme.of(context).colorScheme.primary, + size: 16, + ) + ], ), context.watch().page != 0 || !withActions ? Transform.rotate( @@ -85,7 +99,9 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { child: Padding( padding: const EdgeInsets.only(right: 8.0), child: Image.asset( - Assets.boxAnimation, + DesignConfig.brightness == Brightness.dark + ? Assets.boxAnimationDark + : Assets.boxAnimationLight, width: 38, height: 38, ), diff --git a/pubspec.lock b/pubspec.lock index 5f35698..9250998 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1414,10 +1414,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" wakelock_plus: dependency: transitive description: