From 8380bacaa26db95f382af0dab74ae811fe8c67b9 Mon Sep 17 00:00:00 2001 From: OkaykOrhmn Date: Wed, 13 Nov 2024 16:50:39 +0330 Subject: [PATCH] "Updated various files in the lib directory, including main.dart, ai_chat_args.dart, route_generator.dart, routes.dart, request_helper.dart, action_sheet.dart, ai.dart, ai_chat_page.dart, tool_screen.dart, didvan/button.dart, --- lib/main.dart | 8 + lib/models/ai/ai_chat_args.dart | 4 +- lib/models/ai/bot_assistants_model.dart | 88 +++ lib/routes/route_generator.dart | 10 + lib/routes/routes.dart | 3 + lib/services/network/request_helper.dart | 1 + lib/utils/action_sheet.dart | 17 +- lib/views/ai/ai.dart | 13 +- lib/views/ai/ai_chat_page.dart | 121 ++--- lib/views/ai/bot_assistants_page.dart | 410 ++++++++++++++ lib/views/ai/bot_assistants_state.dart | 43 ++ lib/views/ai/create_bot_assistants_page.dart | 502 ++++++++++++++++++ lib/views/ai/create_bot_assistants_state.dart | 34 ++ lib/views/ai/info_page.dart | 221 ++++++++ lib/views/ai/tool_screen.dart | 4 +- lib/views/widgets/didvan/button.dart | 8 +- lib/views/widgets/didvan/divider.dart | 6 +- lib/views/widgets/didvan/text_field.dart | 23 +- lib/views/widgets/hoshan_app_bar.dart | 29 +- .../widgets/video/chat_video_player.dart | 33 +- 20 files changed, 1463 insertions(+), 115 deletions(-) create mode 100644 lib/models/ai/bot_assistants_model.dart create mode 100644 lib/views/ai/bot_assistants_page.dart create mode 100644 lib/views/ai/bot_assistants_state.dart create mode 100644 lib/views/ai/create_bot_assistants_page.dart create mode 100644 lib/views/ai/create_bot_assistants_state.dart create mode 100644 lib/views/ai/info_page.dart diff --git a/lib/main.dart b/lib/main.dart index 5dbcbcc..6173056 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,6 +19,8 @@ import 'package:didvan/services/notification/firebase_api.dart'; 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'; @@ -178,6 +180,12 @@ class _DidvanState extends State with WidgetsBindingObserver { ChangeNotifierProvider( create: (context) => ToolsState(), ), + ChangeNotifierProvider( + create: (context) => CreateBotAssistantsState(), + ), + ChangeNotifierProvider( + create: (context) => BotAssistantsState(), + ), ], child: Consumer( builder: (context, themeProvider, child) => Container( diff --git a/lib/models/ai/ai_chat_args.dart b/lib/models/ai/ai_chat_args.dart index 194681d..1990856 100644 --- a/lib/models/ai/ai_chat_args.dart +++ b/lib/models/ai/ai_chat_args.dart @@ -6,6 +6,8 @@ class AiChatArgs { final ChatsModel? chat; final Prompts? prompts; final bool? attach; + final List? isTool; - AiChatArgs({required this.bot, this.chat, this.prompts, this.attach}); + AiChatArgs( + {required this.bot, this.chat, this.prompts, this.attach, this.isTool}); } diff --git a/lib/models/ai/bot_assistants_model.dart b/lib/models/ai/bot_assistants_model.dart new file mode 100644 index 0000000..a9864d7 --- /dev/null +++ b/lib/models/ai/bot_assistants_model.dart @@ -0,0 +1,88 @@ +import 'package:didvan/models/ai/bots_model.dart'; + +class BotAssistantsModel { + List? botAssistants; + + BotAssistantsModel({this.botAssistants}); + + BotAssistantsModel.fromJson(Map json) { + if (json['bots'] != null) { + botAssistants = []; + json['bots'].forEach((v) { + botAssistants!.add(BotAssistants.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + if (botAssistants != null) { + data['bots'] = botAssistants!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class BotAssistants { + int? id; + String? name; + String? description; + String? createdAt; + String? image; + BotsModel? bot; + User? user; + + BotAssistants( + {this.id, + this.name, + this.description, + this.createdAt, + this.image, + this.bot, + this.user}); + + BotAssistants.fromJson(Map json) { + id = json['id']; + name = json['name']; + description = json['description']; + createdAt = json['createdAt']; + image = json['image']; + bot = json['bot'] != null ? BotsModel.fromJson(json['bot']) : null; + user = json['user'] != null ? User.fromJson(json['user']) : null; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['description'] = description; + data['createdAt'] = createdAt; + data['image'] = image; + if (bot != null) { + data['bot'] = bot!.toJson(); + } + if (user != null) { + data['user'] = user!.toJson(); + } + return data; + } +} + +class User { + String? fullName; + String? photo; + + User({this.fullName, this.photo}); + + User.fromJson(Map json) { + fullName = json['fullName']; + photo = json['photo']; + } + + Map toJson() { + final Map data = {}; + data['fullName'] = fullName; + data['photo'] = photo; + return data; + } +} diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index df5c9ae..f38c2ba 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -3,8 +3,11 @@ import 'package:didvan/models/ai/ai_chat_args.dart'; 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/history_ai_chat_page.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; +import 'package:didvan/views/ai/info_page.dart'; import 'package:didvan/views/authentication/authentication.dart'; import 'package:didvan/views/authentication/authentication_state.dart'; import 'package:didvan/views/comments/comments.dart'; @@ -316,6 +319,13 @@ class RouteGenerator { archived: settings.arguments as bool?, )); + case Routes.botAssistants: + return _createRoute(const BotAssistantsPage()); + case Routes.createBotAssistants: + return _createRoute(const CreateBotAssistantsPage()); + case Routes.info: + return _createRoute(const InfoPage()); + case Routes.web: return _createRoute(WebView( src: settings.arguments as String, diff --git a/lib/routes/routes.dart b/lib/routes/routes.dart index b64043f..12bb84b 100644 --- a/lib/routes/routes.dart +++ b/lib/routes/routes.dart @@ -2,6 +2,9 @@ class Routes { static const String splash = '/'; static const String aiChat = '/ai-chat'; static const String aiHistory = '/ai-history'; + static const String botAssistants = '/bot-assistants-page'; + static const String createBotAssistants = '/create-bot-assistants-page'; + static const String info = '/info-page'; static const String home = '/home'; static const String radars = '/radars'; diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index 5c8c9fc..bdb6bb4 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -230,6 +230,7 @@ class RequestHelper { static String archivedChat(int id) => '$baseUrl/ai/chat/$id/archive'; static String placeholder(int id) => '$baseUrl/ai/chat/$id/placeholder'; static String tools() => '$baseUrl/ai/tool'; + static String usersAssistants() => '$baseUrl/ai/bot/user'; static String _urlConcatGenerator(List> additions) { String result = ''; diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart index 401c964..5fda755 100644 --- a/lib/utils/action_sheet.dart +++ b/lib/utils/action_sheet.dart @@ -220,20 +220,23 @@ class ActionSheetUtils { onTap: () => Navigator.of(context).pop(), child: Icon( data.titleIcon, - size: 20, + size: 24, color: data.titleColor, ), ), if (data.titleIcon != null) const SizedBox( - width: 8, + width: 4, ), Expanded( - child: DidvanText( - data.title!, - style: Theme.of(context).textTheme.displaySmall, - color: data.titleColor, - fontWeight: FontWeight.bold, + child: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: DidvanText( + data.title!, + style: Theme.of(context).textTheme.displaySmall, + color: data.titleColor, + fontWeight: FontWeight.bold, + ), ), ), ], diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart index b13c671..c47556f 100644 --- a/lib/views/ai/ai.dart +++ b/lib/views/ai/ai.dart @@ -52,7 +52,12 @@ class _AiState extends State { Consumer( builder: (BuildContext context, state, Widget? child) { switch (state.page) { + case 1: + return const ToolsScreen(); + case 2: + return const ToolScreen(); case 0: + default: return Consumer( builder: (context, state, child) { if (state.bots.isEmpty) { @@ -290,14 +295,6 @@ class _AiState extends State { ); }, ); - - case 1: - return const ToolsScreen(); - case 2: - return const ToolScreen(); - - default: - return const SizedBox(); } }, ), diff --git a/lib/views/ai/ai_chat_page.dart b/lib/views/ai/ai_chat_page.dart index 63e2d89..4b751db 100644 --- a/lib/views/ai/ai_chat_page.dart +++ b/lib/views/ai/ai_chat_page.dart @@ -30,6 +30,7 @@ import 'package:didvan/views/widgets/hoshan_app_bar.dart'; import 'package:didvan/views/widgets/marquee_text.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/video/chat_video_player.dart'; +import 'package:didvan/views/widgets/video/custome_controls.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -119,52 +120,52 @@ class _AiChatPageState extends State { ) : Stack( children: [ - Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 24, - ), - ClipOval( - child: CachedNetworkImage( - width: 75, - height: 75, - imageUrl: widget.args.bot.image.toString(), + SingleChildScrollView( + reverse: true, + controller: state.scrollController, + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 24, ), - ), - const SizedBox( - height: 12, - ), - DidvanText( - widget.args.bot.name.toString(), - fontSize: 17, - fontWeight: FontWeight.bold, - ), - if (state.messages.isEmpty) - Column( - children: [ - const SizedBox( - height: 16, - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20.0), - child: Center( - child: DidvanText( - widget.args.bot.description ?? - '')), - ) - ], - ) - ], - ), - if (state.messages.isNotEmpty) - SingleChildScrollView( - reverse: true, - controller: state.scrollController, - child: ListView.builder( + ClipOval( + child: CachedNetworkImage( + width: 75, + height: 75, + imageUrl: widget.args.bot.image.toString(), + ), + ), + const SizedBox( + height: 12, + ), + DidvanText( + widget.args.bot.name.toString(), + fontSize: 17, + fontWeight: FontWeight.bold, + ), + if (state.messages.isEmpty) + Column( + children: [ + const SizedBox( + height: 16, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20.0), + child: Center( + child: DidvanText( + widget.args.bot.description ?? + '')), + ) + ], + ) + ], + ), + if (state.messages.isNotEmpty) + ListView.builder( itemCount: state.messages.length, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -196,8 +197,8 @@ class _AiChatPageState extends State { ], ); }), - ), - ], + ], + ), ), Positioned( top: 32, @@ -391,7 +392,9 @@ class _AiChatPageState extends State { borderRadius: DesignConfig.lowBorderRadius, child: ChatVideoPlayer( - src: file.path, + src: RequestHelper.baseUrl + + file.path, + custome: const CustomControls(), )), ) : file.isImage() @@ -444,14 +447,15 @@ class _AiChatPageState extends State { prompts: message)); }, itemBuilder: (BuildContext context) { - final historyAiChatState = context - .read(); + final bots = widget.args.isTool ?? + context + .read() + .bots; return [ ...List.generate( - historyAiChatState.bots.length, + bots.length, (index) => PopupMenuItem( - value: historyAiChatState - .bots[index], + value: bots[index], height: 72, child: Container( constraints: @@ -462,11 +466,9 @@ class _AiChatPageState extends State { ClipOval( child: CachedNetworkImage( - imageUrl: - historyAiChatState - .bots[index] - .image - .toString(), + imageUrl: bots[index] + .image + .toString(), width: 42, height: 42, ), @@ -477,8 +479,7 @@ class _AiChatPageState extends State { textDirection: TextDirection.ltr, child: DidvanText( - historyAiChatState - .bots[index] + bots[index] .name .toString(), maxLines: 1, diff --git a/lib/views/ai/bot_assistants_page.dart b/lib/views/ai/bot_assistants_page.dart new file mode 100644 index 0000000..9b1e005 --- /dev/null +++ b/lib/views/ai/bot_assistants_page.dart @@ -0,0 +1,410 @@ +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/routes/routes.dart'; +import 'package:didvan/views/ai/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'; +import 'package:didvan/views/widgets/shimmer_placeholder.dart'; +import 'package:didvan/views/widgets/skeleton_image.dart'; +import 'package:didvan/views/widgets/state_handlers/empty_list.dart'; +import 'package:flutter/material.dart'; +import 'package:persian_number_utility/persian_number_utility.dart'; +import 'package:provider/provider.dart'; + +class BotAssistantsPage extends StatefulWidget { + const BotAssistantsPage({Key? key}) : super(key: key); + + @override + State createState() => _BotAssistantsPageState(); +} + +class _BotAssistantsPageState extends State { + bool isMyAssistants = true; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: HoshanAppBar( + onBack: () => Navigator.pop(context), + withActions: false, + ), + floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, + floatingActionButton: isMyAssistants + ? FloatingActionButton.extended( + label: const DidvanText( + 'ایجاد دستیار جدید', + color: Colors.white, + ), + icon: const Icon( + Icons.add, + color: Colors.white, + ), + backgroundColor: Theme.of(context).colorScheme.primary, + onPressed: () { + Navigator.pushNamed(context, Routes.createBotAssistants); + }, + ) + : null, + body: Consumer( + builder: + (BuildContext context, BotAssistantsState state, Widget? child) => + SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Center( + child: Padding( + padding: EdgeInsets.only(top: 32, bottom: 24), + child: DidvanText( + 'انتخاب بات‌ها', + fontSize: 20, + fontWeight: FontWeight.bold, + color: Color(0xff1B3C59), + ), + ), + ), + switchAssistants(context), + FutureBuilder?>( + future: state.getGlobalAssissmant(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return listOfAssistantsPlaceHolder(); + } + if ((snapshot.hasData && snapshot.data == null) || + (snapshot.hasData && + snapshot.data != null && + snapshot.data!.isEmpty)) { + return const EmptyList(); + } + return listOfAssistants(list: snapshot.data!); + }), + if (isMyAssistants) const SizedBox(height: 72) + ], + ), + ), + ), + ); + } + + ListView listOfAssistants({required final List list}) { + return ListView.builder( + itemCount: list.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 8), + itemBuilder: (context, index) { + final assistants = list[index]; + return Container( + padding: const EdgeInsets.all(12), + margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 32), + decoration: const BoxDecoration( + color: Colors.white, borderRadius: DesignConfig.lowBorderRadius), + child: Column( + children: [ + if (isMyAssistants) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.disabledBackground, + borderRadius: DesignConfig.lowBorderRadius), + child: const DidvanText('عمومی'), + ), + ], + ), + Row( + children: [ + SkeletonImage( + imageUrl: assistants.image ?? assistants.bot!.image ?? '', + width: 80, + height: 80, + ), + const SizedBox( + width: 8, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DidvanText( + assistants.name ?? '', + fontSize: 16, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox( + height: 8, + ), + DidvanText( + assistants.description ?? '', + fontSize: 12, + color: Theme.of(context).colorScheme.disabledText, + ), + const SizedBox( + height: 18, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + children: [ + const Icon( + DidvanIcons.calendar_day_light, + size: 18, + ), + const SizedBox( + width: 4, + ), + DidvanText( + DateTime.parse(assistants.createdAt!) + .toPersianDateStr(), + fontSize: 12, + ), + ], + ), + ), + Expanded( + child: isMyAssistants + ? const Row( + children: [ + Icon( + DidvanIcons.user_edit_light, + size: 18, + ), + SizedBox( + width: 4, + ), + DidvanText( + 'ویرایش', + fontSize: 12, + ), + ], + ) + : Row( + children: [ + SkeletonImage( + imageUrl: + assistants.user!.photo ?? '', + width: 24, + height: 24, + borderRadius: + BorderRadius.circular(360), + ), + const SizedBox( + width: 4, + ), + Expanded( + child: DidvanText( + assistants.user!.fullName ?? '', + fontSize: 12, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ) + ], + ), + ], + ), + ) + ], + ), + const SizedBox( + height: 12, + ), + const DidvanButton( + title: 'استفاده از دستیار', + ) + ], + ), + ); + }, + ); + } + + ListView listOfAssistantsPlaceHolder() { + return ListView.builder( + itemCount: 10, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 8), + itemBuilder: (context, index) { + return Container( + padding: const EdgeInsets.all(12), + margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 32), + decoration: const BoxDecoration( + color: Colors.white, borderRadius: DesignConfig.lowBorderRadius), + child: Column( + children: [ + if (isMyAssistants) + const Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ShimmerPlaceholder( + width: 60, + height: 24, + borderRadius: DesignConfig.lowBorderRadius, + ), + ], + ), + const Row( + children: [ + ShimmerPlaceholder( + width: 80, + height: 80, + borderRadius: DesignConfig.lowBorderRadius, + ), + SizedBox( + width: 8, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ShimmerPlaceholder( + width: 120, + height: 24, + borderRadius: DesignConfig.lowBorderRadius, + ), + SizedBox( + height: 8, + ), + ShimmerPlaceholder( + width: 240, + height: 46, + borderRadius: DesignConfig.lowBorderRadius, + ), + SizedBox( + height: 18, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ShimmerPlaceholder( + height: 18, + borderRadius: DesignConfig.lowBorderRadius, + ), + ), + Expanded(child: SizedBox()), + Expanded( + child: ShimmerPlaceholder( + height: 18, + borderRadius: DesignConfig.lowBorderRadius, + ), + ), + ], + ), + ], + ), + ) + ], + ), + const SizedBox( + height: 12, + ), + ShimmerPlaceholder( + width: MediaQuery.sizeOf(context).width, + height: 46, + borderRadius: DesignConfig.lowBorderRadius, + ), + ], + ), + ); + }, + ); + } + + Container switchAssistants(BuildContext context) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 32), + padding: const EdgeInsets.all(12), + decoration: const BoxDecoration( + color: Colors.white, borderRadius: DesignConfig.lowBorderRadius), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: InkWell( + onTap: () => setState(() => isMyAssistants = true), + child: Column( + children: [ + Icon( + DidvanIcons.profile_solid, + color: isMyAssistants + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.disabledText, + ), + Container( + width: 100, + height: 1, + margin: const EdgeInsets.symmetric(vertical: 4), + decoration: BoxDecoration( + color: isMyAssistants + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.disabledText, + ), + ), + DidvanText( + 'دستیارهای من', + color: isMyAssistants + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.disabledText, + ) + ], + ), + ), + ), + Container( + height: 34, + width: 1, + margin: const EdgeInsets.symmetric(horizontal: 12), + decoration: + BoxDecoration(color: Theme.of(context).colorScheme.primary), + ), + Expanded( + child: InkWell( + onTap: () => setState(() => isMyAssistants = false), + child: Column( + children: [ + Icon( + DidvanIcons.profile_solid, + color: !isMyAssistants + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.disabledText, + ), + Container( + width: 100, + height: 1, + margin: const EdgeInsets.symmetric(vertical: 4), + decoration: BoxDecoration( + color: !isMyAssistants + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.disabledText, + ), + ), + DidvanText( + 'دستیارهای دیگران', + color: !isMyAssistants + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.disabledText, + ) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/ai/bot_assistants_state.dart b/lib/views/ai/bot_assistants_state.dart new file mode 100644 index 0000000..8f46cba --- /dev/null +++ b/lib/views/ai/bot_assistants_state.dart @@ -0,0 +1,43 @@ +import 'package:didvan/models/ai/bot_assistants_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'; + +class BotAssistantsState extends CoreProvier { + Future?> getGlobalAssissmant() async { + List? globalAssissmant; + + final service = RequestService( + RequestHelper.usersAssistants(), + ); + await service.httpGet(); + if (service.isSuccess) { + final BotAssistantsModel toolsModel = + BotAssistantsModel.fromJson(service.result); + globalAssissmant = toolsModel.botAssistants!; + appState = AppState.idle; + return globalAssissmant; + } + appState = AppState.failed; + return globalAssissmant; + } + + Future?> getMyAssissmant() async { + List? globalAssissmant; + + final service = RequestService( + RequestHelper.usersAssistants(), + ); + await service.httpGet(); + if (service.isSuccess) { + final BotAssistantsModel toolsModel = + BotAssistantsModel.fromJson(service.result); + globalAssissmant = toolsModel.botAssistants!; + appState = AppState.idle; + return globalAssissmant; + } + appState = AppState.failed; + return globalAssissmant; + } +} diff --git a/lib/views/ai/create_bot_assistants_page.dart b/lib/views/ai/create_bot_assistants_page.dart new file mode 100644 index 0000000..2c8ffd0 --- /dev/null +++ b/lib/views/ai/create_bot_assistants_page.dart @@ -0,0 +1,502 @@ +import 'package:animated_custom_dropdown/custom_dropdown.dart'; +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/bots_model.dart'; +import 'package:didvan/models/enums.dart'; +import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/utils/action_sheet.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'; +import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:didvan/views/widgets/didvan/text_field.dart'; +import 'package:didvan/views/widgets/hoshan_app_bar.dart'; +import 'package:didvan/views/widgets/shimmer_placeholder.dart'; +import 'package:didvan/views/widgets/skeleton_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CreateBotAssistantsPage extends StatefulWidget { + const CreateBotAssistantsPage({Key? key}) : super(key: key); + + @override + State createState() => + _CreateBotAssistantsPageState(); +} + +class _CreateBotAssistantsPageState extends State { + final List botModels = ['مدل زبانی', 'مدل تصویری']; + late List allBots = context.read().bots; + final _formYouTubeKey = GlobalKey(); + final _formNameKey = GlobalKey(); + final _formDescKey = GlobalKey(); + + List countOfLink = ['']; + int selectedItem = 0; + bool inEdite = true; + + late InputBorder defaultBorder = OutlineInputBorder( + borderRadius: DesignConfig.mediumBorderRadius, + borderSide: BorderSide( + width: 1, color: Theme.of(context).colorScheme.disabledText)); + + @override + void initState() { + super.initState(); + + context.read().getImageToolsBots(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: HoshanAppBar( + onBack: () => Navigator.pop(context), + withActions: false, + ), + body: Consumer( + builder: (BuildContext context, CreateBotAssistantsState state, + Widget? child) => + SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 32), + child: Column( + children: [ + title(text: 'نوع دستیار'), + SizedBox( + width: MediaQuery.sizeOf(context).width, + child: CustomDropdown( + closedHeaderPadding: const EdgeInsets.all(12), + items: botModels, + initialItem: botModels[selectedItem], + hideSelectedFieldWhenExpanded: false, + decoration: CustomDropdownDecoration( + listItemDecoration: ListItemDecoration( + selectedColor: Theme.of(context) + .colorScheme + .surface + .withOpacity(0.5), + ), + closedBorder: Border.all(color: Colors.grey), + closedFillColor: Colors.grey.shade100.withOpacity(0.1), + expandedFillColor: + Theme.of(context).colorScheme.surface), + // hintText: "انتخاب کنید", + onChanged: (value) { + setState(() { + selectedItem = botModels.indexOf(value!); + }); + }, + ), + ), + const SizedBox( + height: 24, + ), + const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + DidvanButton( + width: 120, + style: ButtonStyleMode.flat, + title: 'انتخاب عکس', + ), + SkeletonImage( + imageUrl: 'https://via.placeholder.com/70x70', + width: 80, + height: 80, + ), + ], + ), + const SizedBox( + height: 24, + ), + title(text: 'انتخاب نام'), + Form( + key: _formNameKey, + child: DidvanTextField( + onChanged: (value) {}, + validator: (value) { + String? result; + if (value.isEmpty) { + result = 'نام نباید خالی باشد'; + } else if (value.length < 4) { + result = 'نام نباید کمتر از 4 حرف باشد'; + } + return result; + }, + hintText: 'ai@2024_B', + maxLength: 20, + ), + ), + const SizedBox( + height: 8, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + DidvanIcons.info_circle_light, + color: Theme.of(context).colorScheme.caption, + ), + const SizedBox(width: 4), + Expanded( + child: DidvanText( + 'نام منحصر به فرد شامل 4 تا 20 کاراکتر (حروف، اعداد، خط تیره، نقطه و زیرخط) ', + textAlign: TextAlign.right, + fontSize: 12, + color: Theme.of(context).colorScheme.caption, + ), + ), + ], + ), + const SizedBox( + height: 24, + ), + title(text: 'نوع دستیار'), + state.loadingImageBots + ? ShimmerPlaceholder( + width: MediaQuery.sizeOf(context).width, + height: 48, + borderRadius: DesignConfig.lowBorderRadius, + ) + : SizedBox( + width: MediaQuery.sizeOf(context).width, + child: CustomDropdown( + closedHeaderPadding: const EdgeInsets.all(12), + items: selectedItem == 0 ? allBots : state.imageBots, + headerBuilder: (context, bot, enabled) => + botRow(bot, context), + listItemBuilder: + (context, bot, isSelected, onItemSelect) => + botRow(bot, context), + initialItem: + (selectedItem == 0 ? allBots : state.imageBots) + .first, + hideSelectedFieldWhenExpanded: false, + decoration: CustomDropdownDecoration( + listItemDecoration: ListItemDecoration( + selectedColor: Theme.of(context) + .colorScheme + .surface + .withOpacity(0.5), + ), + closedBorder: Border.all(color: Colors.grey), + closedFillColor: + Colors.grey.shade100.withOpacity(0.1), + expandedFillColor: + Theme.of(context).colorScheme.surface), + // hintText: "انتخاب کنید", + onChanged: (value) { + // setState(() { + // selectedItem = value; + // }); + }, + ), + ), + const SizedBox( + height: 24, + ), + title(text: 'دستورالعمل'), + Form( + key: _formDescKey, + child: DidvanTextField( + hintText: + 'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.', + textInputType: TextInputType.multiline, + minLine: 6, + maxLine: 6, + maxLength: 400, + hasHeight: false, + showLen: true, + validator: (value) { + String? result; + if (value.isEmpty) { + result = 'دستورالعمل نباید خالی باشد'; + } else if (value.length < 10) { + result = 'نام نباید کمتر از 10 حرف باشد'; + } + return result; + }, + ), + ), + const SizedBox( + height: 24, + ), + if (selectedItem == 0) + Column( + children: [ + title(text: 'پایگاه دانش', isRequired: false), + SizedBox( + height: 48, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context) + .colorScheme + .disabledBackground, + shape: const RoundedRectangleBorder( + borderRadius: + DesignConfig.lowBorderRadius)), + onPressed: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + CupertinoIcons.add, + color: Theme.of(context).colorScheme.caption, + ), + const SizedBox( + width: 4, + ), + DidvanText( + 'آپلود فایل (فایل صوتی، پی دی اف)', + color: Theme.of(context).colorScheme.caption, + fontSize: 16, + ) + ], + )), + ), + const SizedBox( + height: 24, + ), + ], + ), + title(text: 'لینک یوتیوب', isRequired: false), + Form( + key: _formYouTubeKey, + child: DidvanTextField( + onChanged: (value) {}, + validator: (value) => + value.startsWith('https://www.youtube.com') + ? null + : 'باید لینک یوتیوب باشد', + hintText: 'https://www.youtube.com/watch?v', + ), + ), + const SizedBox( + height: 24, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + title(text: 'لینک وب سایت', isRequired: false), + Row( + children: [ + if (countOfLink.length > 1) + DidvanIconButton( + icon: CupertinoIcons.minus_circle_fill, + onPressed: () { + setState(() { + countOfLink.removeLast(); + }); + }, + ), + DidvanIconButton( + icon: CupertinoIcons.plus_circle_fill, + onPressed: () { + setState(() { + countOfLink.add(''); + }); + }, + ), + ], + ) + ], + ), + ListView.builder( + shrinkWrap: true, + itemCount: countOfLink.length, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => Column( + children: [ + DidvanTextField( + onChanged: (value) { + countOfLink.insert(index, value); + }, + // validator: (value) {}, + hintText: 'https://www.weforum.org/agenda/2024/08', + ), + const SizedBox( + height: 8, + ), + ], + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + DidvanIcons.info_circle_light, + color: Theme.of(context).colorScheme.caption, + ), + const SizedBox(width: 4), + Expanded( + child: DidvanText( + 'دستیار شما با استناد بر اطلاعات ارائه شده در پایگاه دانش، پیام کاربران را ارزیابی می‌کند.', + textAlign: TextAlign.right, + fontSize: 12, + color: Theme.of(context).colorScheme.caption, + ), + ), + ], + ), + const SizedBox( + height: 24, + ), + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const DidvanText( + 'نمایش عمومی', + fontSize: 14, + ), + Row( + children: [ + Expanded( + child: DidvanText( + 'در صورت فعال بودن، دستیار شما توسط سایرین قابل مشاهده بوده و مورد استفاده قرار می‌گیرد.', + fontSize: 14, + color: Theme.of(context).colorScheme.caption, + ), + ), + ], + ) + ], + ), + ), + SizedBox( + width: 64, + height: 48, + child: DidvanSwitch( + value: false, + title: '', + onChanged: (value) {}, + ), + ), + ], + ), + const SizedBox( + height: 24, + ), + inEdite + ? Flex( + direction: Axis.horizontal, + children: [ + Flexible( + flex: 2, + child: DidvanButton( + title: 'ذخیره تغییرات', + onPressed: () { + final List valid = []; + valid.add( + !_formYouTubeKey.currentState!.validate()); + valid.add( + !_formNameKey.currentState!.validate()); + valid.add( + !_formDescKey.currentState!.validate()); + if (valid.firstWhere( + (element) => !element, + )) return; + }, + ), + ), + const SizedBox( + width: 20, + ), + Flexible( + flex: 1, + child: DidvanButton( + title: 'حذف دستیار', + style: ButtonStyleMode.flat, + color: Theme.of(context).colorScheme.error, + onPressed: () { + ActionSheetUtils(context).openDialog( + data: ActionSheetData( + title: 'حذف دستیار', + titleIcon: DidvanIcons.trash_solid, + titleColor: + Theme.of(context).colorScheme.error, + content: const Column( + children: [ + DidvanText( + 'با حذف این دستیار، استفاده از آن برای شما و سایر کاربران، امکان‌پذیر نیست.\nآیا مطمئن هستید؟!', + fontSize: 14, + ) + ], + ))); + }, + ), + ), + ], + ) + : const DidvanButton( + title: 'ذخیره', + ), + const SizedBox( + height: 24, + ), + ], + ), + ), + ), + ), + ); + } + + Container botRow(BotsModel bot, BuildContext context) { + return Container( + alignment: Alignment.center, + child: Row( + children: [ + ClipOval( + child: CachedNetworkImage( + imageUrl: bot.image.toString(), + width: 42, + height: 42, + ), + ), + const SizedBox(width: 12), + Expanded( + child: DidvanText( + bot.name.toString(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + )) + ], + ), + ); + } + + Widget title({required final String text, final bool isRequired = true}) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Text.rich( + TextSpan( + children: [ + TextSpan( + text: text, + style: Theme.of(context).textTheme.bodyMedium, + ), + if (isRequired) + TextSpan( + text: '*', + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.error)), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/ai/create_bot_assistants_state.dart b/lib/views/ai/create_bot_assistants_state.dart new file mode 100644 index 0000000..c90f05e --- /dev/null +++ b/lib/views/ai/create_bot_assistants_state.dart @@ -0,0 +1,34 @@ +import 'package:didvan/models/ai/bots_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'; + +class CreateBotAssistantsState extends CoreProvier { + List imageBots = []; + bool loadingImageBots = false; + + void getImageToolsBots() async { + final service = RequestService( + RequestHelper.tools(), + ); + await service.httpGet(); + if (service.isSuccess) { + final ToolsModel toolsModel = ToolsModel.fromJson(service.result); + if (toolsModel.tools != null) { + imageBots = toolsModel.tools!.first.bots ?? []; + } else { + imageBots = []; + } + + appState = AppState.idle; + loadingImageBots = false; + update(); + return; + } + appState = AppState.failed; + loadingImageBots = false; + update(); + } +} diff --git a/lib/views/ai/info_page.dart b/lib/views/ai/info_page.dart new file mode 100644 index 0000000..ba30eb2 --- /dev/null +++ b/lib/views/ai/info_page.dart @@ -0,0 +1,221 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/views/widgets/didvan/divider.dart'; +import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:didvan/views/widgets/hoshan_app_bar.dart'; +import 'package:didvan/views/widgets/video/chat_video_player.dart'; +import 'package:flutter/material.dart'; + +class InfoPage extends StatefulWidget { + const InfoPage({Key? key}) : super(key: key); + + @override + State createState() => _InfoPageState(); +} + +class _InfoPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: HoshanAppBar( + withActions: false, + onBack: () => Navigator.pop(context), + ), + body: SingleChildScrollView( + child: Column( + children: [ + const Center( + child: Padding( + padding: EdgeInsets.only(top: 32, bottom: 24), + child: DidvanText( + 'آموزش پرامپت نویسی اصولی', + fontSize: 20, + fontWeight: FontWeight.bold, + color: Color(0xff1B3C59), + ), + ), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: ClipRRect( + borderRadius: DesignConfig.lowBorderRadius, + child: ChatVideoPlayer( + src: + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + showOptions: true, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + DidvanText( + 'آنچه در این ویدیو خواهید دید:', + fontSize: 16, + ), + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + Container( + margin: const EdgeInsets.only(left: 8), + decoration: const ShapeDecoration( + shape: CircleBorder( + side: BorderSide( + width: 3, color: Colors.black)), + ), + ), + const DidvanText( + 'انتخاب کلمات کلیدی مناسب', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + Container( + margin: const EdgeInsets.only(left: 8), + decoration: const ShapeDecoration( + shape: CircleBorder( + side: BorderSide( + width: 3, color: Colors.black)), + ), + ), + const DidvanText( + 'ساختار و قالب‌بندی پرامپت‌ها', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + Container( + margin: const EdgeInsets.only(left: 8), + decoration: const ShapeDecoration( + shape: CircleBorder( + side: BorderSide( + width: 3, color: Colors.black)), + ), + ), + const DidvanText( + 'تعیین سبک و استایل در پرامپت', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + Container( + margin: const EdgeInsets.only(left: 8), + decoration: const ShapeDecoration( + shape: CircleBorder( + side: BorderSide( + width: 3, color: Colors.black)), + ), + ), + const DidvanText( + 'استفاده از جزییات و صفت‌ها', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + Container( + margin: const EdgeInsets.only(left: 8), + decoration: const ShapeDecoration( + shape: CircleBorder( + side: BorderSide( + width: 3, color: Colors.black)), + ), + ), + const DidvanText( + 'بهینه‌سازی پرامپت‌ها و تکرار', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + Container( + margin: const EdgeInsets.only(left: 8), + decoration: const ShapeDecoration( + shape: CircleBorder( + side: BorderSide( + width: 3, color: Colors.black)), + ), + ), + const DidvanText( + 'اشتباهات رایج در پرامپت‌نویسی', + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ], + ), + ], + ), + ) + ], + ), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: DidvanDivider( + verticalPadding: 12, + height: 4, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + DidvanIcons.support_solid, + size: 32, + ), + const SizedBox( + width: 8, + ), + const DidvanText('هنوز سوالی دارید؟'), + TextButton( + onPressed: () {}, + child: const DidvanText( + ' پیام به پشتیبانی', + color: Color(0xff007EA7), + ), + ) + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/views/ai/tool_screen.dart b/lib/views/ai/tool_screen.dart index e41c897..ba6c030 100644 --- a/lib/views/ai/tool_screen.dart +++ b/lib/views/ai/tool_screen.dart @@ -66,9 +66,7 @@ class _ToolScreenState extends State { final bot = tool.bots![index]; return InkWell( onTap: () => Navigator.of(context).pushNamed(Routes.aiChat, - arguments: AiChatArgs( - bot: bot, - )), + arguments: AiChatArgs(bot: bot, isTool: tool.bots)), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( diff --git a/lib/views/widgets/didvan/button.dart b/lib/views/widgets/didvan/button.dart index c5a18c5..caf4d30 100644 --- a/lib/views/widgets/didvan/button.dart +++ b/lib/views/widgets/didvan/button.dart @@ -11,6 +11,7 @@ class DidvanButton extends StatelessWidget { final ButtonStyleMode style; final bool enabled; final double? height; + final Color? color; const DidvanButton({ Key? key, this.onPressed, @@ -19,6 +20,7 @@ class DidvanButton extends StatelessWidget { this.enabled = true, this.width, this.height = 48, + this.color, }) : super(key: key); @override @@ -27,7 +29,7 @@ class DidvanButton extends StatelessWidget { Color backgroundColor = Colors.black; switch (style) { case ButtonStyleMode.primary: - backgroundColor = Theme.of(context).colorScheme.primary; + backgroundColor = color ?? Theme.of(context).colorScheme.primary; foregroundColor = Theme.of(context).colorScheme.white; break; case ButtonStyleMode.secondary: @@ -37,10 +39,10 @@ class DidvanButton extends StatelessWidget { case ButtonStyleMode.flat: if (DesignConfig.isDark) { backgroundColor = Theme.of(context).colorScheme.surface; - foregroundColor = Theme.of(context).colorScheme.white; + foregroundColor = color ?? Theme.of(context).colorScheme.white; } else { backgroundColor = Theme.of(context).colorScheme.surface; - foregroundColor = Theme.of(context).colorScheme.primary; + foregroundColor = color ?? Theme.of(context).colorScheme.primary; } break; default: diff --git a/lib/views/widgets/didvan/divider.dart b/lib/views/widgets/didvan/divider.dart index 3b6b382..767d3d2 100644 --- a/lib/views/widgets/didvan/divider.dart +++ b/lib/views/widgets/didvan/divider.dart @@ -3,13 +3,15 @@ import 'package:flutter/material.dart'; class DidvanDivider extends StatelessWidget { final double? verticalPadding; + final double? height; - const DidvanDivider({Key? key, this.verticalPadding}) : super(key: key); + const DidvanDivider({Key? key, this.verticalPadding, this.height}) + : super(key: key); @override Widget build(BuildContext context) { return Container( - height: 1, + height: height ?? 1, width: double.infinity, margin: EdgeInsets.symmetric(vertical: verticalPadding ?? 16), color: Theme.of(context).colorScheme.border, diff --git a/lib/views/widgets/didvan/text_field.dart b/lib/views/widgets/didvan/text_field.dart index 57f16af..e10e459 100644 --- a/lib/views/widgets/didvan/text_field.dart +++ b/lib/views/widgets/didvan/text_field.dart @@ -22,6 +22,12 @@ class DidvanTextField extends StatefulWidget { final TextInputType? textInputType; final bool disableBorders; final bool isSmall; + final int? maxLine; + final int? minLine; + final int? maxLength; + final bool hasHeight; + final bool showLen; + const DidvanTextField({ Key? key, this.onChanged, @@ -38,6 +44,11 @@ class DidvanTextField extends StatefulWidget { this.acceptSpace = true, this.disableBorders = false, this.isSmall = false, + this.maxLine, + this.minLine, + this.maxLength, + this.hasHeight = true, + this.showLen = false, }) : super(key: key); @override @@ -76,7 +87,7 @@ class _DidvanTextFieldState extends State { ), if (widget.title != null) const SizedBox(height: 8), Container( - height: 48, + height: widget.hasHeight ? 48 : null, padding: const EdgeInsets.symmetric(horizontal: 12).copyWith( left: widget.obsecureText ? 0 : 12, ), @@ -101,7 +112,17 @@ class _DidvanTextFieldState extends State { onFieldSubmitted: widget.onSubmitted, onChanged: _onChanged, validator: _validator, + maxLines: widget.maxLine, + minLines: widget.minLine, + maxLength: widget.maxLength, obscuringCharacter: '*', + buildCounter: widget.showLen + ? null + : (context, + {required currentLength, + required isFocused, + required maxLength}) => + const SizedBox(), style: (widget.isSmall ? Theme.of(context).textTheme.bodySmall! : Theme.of(context).textTheme.bodyMedium!) diff --git a/lib/views/widgets/hoshan_app_bar.dart b/lib/views/widgets/hoshan_app_bar.dart index e68e017..cc37249 100644 --- a/lib/views/widgets/hoshan_app_bar.dart +++ b/lib/views/widgets/hoshan_app_bar.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/assets.dart'; +import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; @@ -10,7 +11,9 @@ import 'package:provider/provider.dart'; class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { final Function()? onBack; - const HoshanAppBar({Key? key, this.onBack}) : super(key: key); + final bool withActions; + const HoshanAppBar({Key? key, this.onBack, this.withActions = true}) + : super(key: key); @override Widget build(BuildContext context) { @@ -48,16 +51,22 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { ), Row( children: [ - DidvanIconButton( - icon: DidvanIcons.info_circle_light, + if (withActions) + DidvanIconButton( + icon: DidvanIcons.info_circle_light, + size: 32, + onPressed: () { + Navigator.pushNamed(context, Routes.info); + }), + if (withActions) + DidvanIconButton( + icon: DidvanIcons.antenna_light, size: 32, - onPressed: () {}), - DidvanIconButton( - icon: DidvanIcons.antenna_light, - size: 32, - onPressed: () {}, - ), - context.watch().page != 0 + onPressed: () { + Navigator.pushNamed(context, Routes.botAssistants); + }, + ), + context.watch().page != 0 || !withActions ? Transform.rotate( angle: 180 * pi / 180, child: DidvanIconButton( diff --git a/lib/views/widgets/video/chat_video_player.dart b/lib/views/widgets/video/chat_video_player.dart index 46856d9..ae03e38 100644 --- a/lib/views/widgets/video/chat_video_player.dart +++ b/lib/views/widgets/video/chat_video_player.dart @@ -3,16 +3,18 @@ import 'package:chewie/chewie.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/services/network/request.dart'; -import 'package:didvan/services/network/request_helper.dart'; -import 'package:didvan/views/widgets/video/custome_controls.dart'; +import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:video_player/video_player.dart'; class ChatVideoPlayer extends StatefulWidget { final String src; - const ChatVideoPlayer({Key? key, required this.src}) : super(key: key); + final Widget? custome; + final bool showOptions; + const ChatVideoPlayer( + {Key? key, required this.src, this.custome, this.showOptions = false}) + : super(key: key); @override State createState() => _ChatVideoPlayerState(); @@ -29,20 +31,18 @@ class _ChatVideoPlayerState extends State { } Future _handleVideoPlayback() async { - _videoPlayerController = VideoPlayerController.network( - RequestHelper.baseUrl + widget.src, + _videoPlayerController = VideoPlayerController.network(widget.src, httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'}); await _videoPlayerController.initialize().then((_) { setState(() { _chewieController = ChewieController( - customControls: const CustomControls(), + customControls: widget.custome, videoPlayerController: _videoPlayerController, autoPlay: false, looping: true, - showOptions: false, - allowPlaybackSpeedChanging: false, - placeholder: const CircularProgressIndicator(), + showOptions: widget.showOptions, + allowPlaybackSpeedChanging: widget.showOptions, aspectRatio: 16 / 9, materialProgressColors: ChewieProgressColors( playedColor: Theme.of(context).colorScheme.title, @@ -65,16 +65,9 @@ class _ChatVideoPlayerState extends State { @override Widget build(BuildContext context) { return _chewieController == null - ? SizedBox( - child: Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: SpinKitThreeBounce( - color: Theme.of(context).colorScheme.primary, - size: 18, - ), - ), - ), + ? ShimmerPlaceholder( + width: MediaQuery.sizeOf(context).width, + height: 200, ) : AspectRatio( aspectRatio: _videoPlayerController.value.aspectRatio,