diff --git a/lib/models/ai/info_model.dart b/lib/models/ai/info_model.dart index a54496c..6eddf0f 100644 --- a/lib/models/ai/info_model.dart +++ b/lib/models/ai/info_model.dart @@ -1,5 +1,6 @@ class InfoModel { String? url; + String? pdf; String? title; List? description; @@ -7,6 +8,7 @@ class InfoModel { InfoModel.fromJson(Map json) { url = json['url']; + pdf = json['pdf']; title = json['title']; description = json['description'].cast(); } @@ -14,6 +16,7 @@ class InfoModel { Map toJson() { final Map data = {}; data['url'] = url; + data['pdf'] = pdf; data['title'] = title; data['description'] = description; return data; diff --git a/lib/views/ai/ai_chat_page.dart b/lib/views/ai/ai_chat_page.dart index 7165cd5..727d2c1 100644 --- a/lib/views/ai/ai_chat_page.dart +++ b/lib/views/ai/ai_chat_page.dart @@ -25,6 +25,7 @@ import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/widgets/ai_message_bar.dart'; import 'package:didvan/views/ai/widgets/audio_wave.dart'; import 'package:didvan/views/ai/widgets/hoshan_drawer.dart'; +import 'package:didvan/views/widgets/didvan/didvan_markdown.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/hoshan_app_bar.dart'; import 'package:didvan/views/widgets/marquee_text.dart'; @@ -34,11 +35,9 @@ import 'package:didvan/views/widgets/video/custome_controls.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:provider/provider.dart'; -import 'package:url_launcher/url_launcher_string.dart'; class AiChatPage extends StatefulWidget { final AiChatArgs args; @@ -111,6 +110,7 @@ class _AiChatPageState extends State { withActions: false, ), key: scaffKey, + resizeToAvoidBottomInset: true, drawer: const HoshanDrawer(), body: state.loading ? Center( @@ -278,27 +278,6 @@ class _AiChatPageState extends State { ? Duration(seconds: message.duration!) : null)); - MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet( - pPadding: const EdgeInsets.all(0.8), - h1Padding: const EdgeInsets.all(0.8), - h2Padding: const EdgeInsets.all(0.8), - h3Padding: const EdgeInsets.all(0.8), - h4Padding: const EdgeInsets.all(0.8), - h5Padding: const EdgeInsets.all(0.8), - h6Padding: const EdgeInsets.all(0.8), - tablePadding: const EdgeInsets.all(0.8), - blockquotePadding: const EdgeInsets.all(0.8), - listBulletPadding: const EdgeInsets.all(0.8), - tableCellsPadding: const EdgeInsets.all(0.8), - codeblockPadding: const EdgeInsets.all(8), - code: TextStyle( - backgroundColor: Theme.of(context).colorScheme.black, - color: Theme.of(context).colorScheme.white, - ), - codeblockDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: Theme.of(context).colorScheme.black)); - return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4), child: Column( @@ -348,29 +327,19 @@ class _AiChatPageState extends State { if (!snapshot.hasData) { return const SizedBox(); } - 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), + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, horizontal: 16), + child: Directionality( + textDirection: snapshot.data + .toString() + .startsWithEnglish() + ? TextDirection.ltr + : TextDirection.rtl, + child: DidvanMarkdownText( + text: "${snapshot.data}...", + ), + ), ); }, ); @@ -430,25 +399,18 @@ class _AiChatPageState extends State { ((message.audio == null || (message.audio != null && !message.audio!)))) - 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: const EdgeInsets.symmetric( + vertical: 8.0, horizontal: 16), + child: Directionality( + textDirection: message.text + .toString() + .startsWithEnglish() + ? TextDirection.ltr + : TextDirection.rtl, + child: DidvanMarkdownText( + text: message.text.toString(), + ), ), ), Padding( diff --git a/lib/views/ai/info_page.dart b/lib/views/ai/info_page.dart index 1f4257a..254e55c 100644 --- a/lib/views/ai/info_page.dart +++ b/lib/views/ai/info_page.dart @@ -4,6 +4,7 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/ai/info_state.dart'; +import 'package:didvan/views/widgets/didvan/button.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'; @@ -13,6 +14,7 @@ import 'package:didvan/views/widgets/video/primary_controls.dart'; import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class InfoPage extends StatefulWidget { const InfoPage({Key? key}) : super(key: key); @@ -62,17 +64,29 @@ class _InfoPageState extends State { ), ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: ClipRRect( - borderRadius: DesignConfig.lowBorderRadius, - child: ChatVideoPlayer( - src: state.infoModel.url ?? '', - showOptions: true, - custome: const PrimaryControls(), + if (state.infoModel.url != null) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: ClipRRect( + borderRadius: DesignConfig.lowBorderRadius, + child: ChatVideoPlayer( + src: state.infoModel.url!, + showOptions: true, + custome: const PrimaryControls(), + ), + ), + ), + if (state.infoModel.pdf != null) + Padding( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 0), + child: DidvanButton( + title: 'دانلود فایل راهنما', + onPressed: () { + launchUrlString(state.infoModel.pdf!, + mode: LaunchMode.inAppBrowserView); + }, ), ), - ), Padding( padding: const EdgeInsets.all(20.0), child: Column( diff --git a/lib/views/ai/widgets/ai_message_bar.dart b/lib/views/ai/widgets/ai_message_bar.dart index 85c35e3..8891eef 100644 --- a/lib/views/ai/widgets/ai_message_bar.dart +++ b/lib/views/ai/widgets/ai_message_bar.dart @@ -488,7 +488,7 @@ class _AiMessageBarState extends State { .toPersianDateStr())) { state.messages.last.prompts.add(Prompts( error: false, - text: message.text, + text: message.text.replaceAll(r"\n", r" \n \n "), // file: state.file?.path, // fileName: state.file?.basename, fileLocal: state.file, @@ -506,7 +506,7 @@ class _AiMessageBarState extends State { prompts: [ Prompts( error: false, - text: message.text, + text: message.text.replaceAll(r"\n", r" \n \n "), finished: true, // file: state.file?.path, // fileName: state.file?.basename, diff --git a/lib/views/ai/widgets/ai_message_bar_ios.dart b/lib/views/ai/widgets/ai_message_bar_ios.dart index 7525887..797bb1f 100644 --- a/lib/views/ai/widgets/ai_message_bar_ios.dart +++ b/lib/views/ai/widgets/ai_message_bar_ios.dart @@ -254,7 +254,10 @@ class _AiMessageBarIOSState extends State { .add(Prompts( error: false, text: state - .message.text, + .message.text + .replaceAll( + r"\n", + r" \n \n "), file: state .file?.path, fileName: state @@ -285,7 +288,10 @@ class _AiMessageBarIOSState extends State { false, text: state .message - .text, + .text + .replaceAll( + r"\n", + r" \n \n "), finished: true, file: state diff --git a/lib/views/widgets/didvan/didvan_markdown.dart b/lib/views/widgets/didvan/didvan_markdown.dart new file mode 100644 index 0000000..91e3582 --- /dev/null +++ b/lib/views/widgets/didvan/didvan_markdown.dart @@ -0,0 +1,107 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/utils/extension.dart'; +import 'package:flutter/material.dart'; +import 'package:markdown_widget/config/configs.dart'; +import 'package:markdown_widget/widget/blocks/leaf/heading.dart'; +import 'package:markdown_widget/widget/blocks/leaf/paragraph.dart'; +import 'package:markdown_widget/widget/markdown.dart'; + +class DidvanMarkdownText extends StatelessWidget { + final String text; + final Color? color; + final double? width; + + const DidvanMarkdownText( + {super.key, required this.text, this.color, this.width}); + +// onTapLink: (text, href, title) { +// if (href != null) { +// launchUrlString( +// href, +// mode: LaunchMode +// .inAppBrowserView, +// ); +// } +// }, +// MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet( +// pPadding: const EdgeInsets.all(0.8), +// h1Padding: const EdgeInsets.all(0.8), +// h2Padding: const EdgeInsets.all(0.8), +// h3Padding: const EdgeInsets.all(0.8), +// h4Padding: const EdgeInsets.all(0.8), +// h5Padding: const EdgeInsets.all(0.8), +// h6Padding: const EdgeInsets.all(0.8), +// tablePadding: const EdgeInsets.all(0.8), +// blockquotePadding: const EdgeInsets.all(0.8), +// listBulletPadding: const EdgeInsets.all(0.8), +// tableCellsPadding: const EdgeInsets.all(0.8), +// codeblockPadding: const EdgeInsets.all(8), +// code: TextStyle( +// backgroundColor: Theme.of(context).colorScheme.black, +// color: Theme.of(context).colorScheme.white, +// ), +// codeblockDecoration: BoxDecoration( +// borderRadius: BorderRadius.circular(4), +// color: Theme.of(context).colorScheme.black)); + + @override + Widget build(BuildContext context) { + return Directionality( + textDirection: + text.startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, + child: SizedBox( + width: width, + child: MarkdownWidget( + data: text, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + selectable: true, + config: MarkdownConfig(configs: [ + H1Config( + style: MarkdownConfig.defaultConfig.h1.style.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)), + H2Config( + style: MarkdownConfig.defaultConfig.h2.style.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)), + H3Config( + style: MarkdownConfig.defaultConfig.h3.style.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)), + H4Config( + style: MarkdownConfig.defaultConfig.h4.style.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)), + H5Config( + style: MarkdownConfig.defaultConfig.h5.style.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)), + H6Config( + style: MarkdownConfig.defaultConfig.h6.style.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)), + PConfig( + textStyle: MarkdownConfig.defaultConfig.p.textStyle.copyWith( + fontFamily: text.startsWithEnglish() + ? DesignConfig.fontFamily.replaceAll('-FA', '') + : null, + color: color)) + ]), + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 9250998..6fc9e51 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -414,6 +414,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.8" + flutter_highlight: + dependency: transitive + description: + name: flutter_highlight + sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" + url: "https://pub.dev" + source: hosted + version: "0.7.0" flutter_html: dependency: "direct main" description: @@ -459,14 +467,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_markdown: - dependency: "direct main" - description: - name: flutter_markdown - sha256: a23c41ee57573e62fc2190a1f36a0480c4d90bde3a8a8d7126e5d5992fb53fb7 - url: "https://pub.dev" - source: hosted - version: "0.7.3+1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -597,6 +597,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + highlight: + dependency: transitive + description: + name: highlight + sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" + url: "https://pub.dev" + source: hosted + version: "0.7.0" home_widget: dependency: "direct main" description: @@ -797,6 +805,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.2.2" + markdown_widget: + dependency: "direct main" + description: + name: markdown_widget + sha256: "216dced98962d7699a265344624bc280489d739654585ee881c95563a3252fac" + url: "https://pub.dev" + source: hosted + version: "2.3.2+6" marquee: dependency: "direct main" description: @@ -1125,6 +1141,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + scroll_to_index: + dependency: transitive + description: + name: scroll_to_index + sha256: b707546e7500d9f070d63e5acf74fd437ec7eeeb68d3412ef7b0afada0b4f176 + url: "https://pub.dev" + source: hosted + version: "3.0.1" skeleton_text: dependency: "direct main" description: @@ -1410,6 +1434,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 981d023..08aa5eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,7 +87,7 @@ dependencies: video_player: ^2.9.2 chewie: ^1.8.3 typewritertext: ^3.0.8 - flutter_markdown: ^0.7.3+1 + markdown_widget: ^2.3.2+6 file_picker: ^8.0.5 marquee: ^2.2.3 mime: ^1.0.2