diff --git a/lib/assets/icons/ChartBot.svg b/lib/assets/icons/ChartBot.svg new file mode 100644 index 0000000..b7357af --- /dev/null +++ b/lib/assets/icons/ChartBot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/assets/icons/CrateImage.svg b/lib/assets/icons/CrateImage.svg new file mode 100644 index 0000000..a8315ec --- /dev/null +++ b/lib/assets/icons/CrateImage.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/icons/DidvanPlus.svg b/lib/assets/icons/DidvanPlus.svg new file mode 100644 index 0000000..ed0dbe5 --- /dev/null +++ b/lib/assets/icons/DidvanPlus.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/icons/Signal.svg b/lib/assets/icons/Signal.svg new file mode 100644 index 0000000..320f2b0 --- /dev/null +++ b/lib/assets/icons/Signal.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/icons/SummeryBot.svg b/lib/assets/icons/SummeryBot.svg new file mode 100644 index 0000000..e778549 --- /dev/null +++ b/lib/assets/icons/SummeryBot.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/icons/TTV_Bot.svg b/lib/assets/icons/TTV_Bot.svg new file mode 100644 index 0000000..e92c145 --- /dev/null +++ b/lib/assets/icons/TTV_Bot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/assets/icons/TranslateBot.svg b/lib/assets/icons/TranslateBot.svg new file mode 100644 index 0000000..b3fd0f0 --- /dev/null +++ b/lib/assets/icons/TranslateBot.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/lib/assets/icons/VideoBot.svg b/lib/assets/icons/VideoBot.svg new file mode 100644 index 0000000..3ace746 --- /dev/null +++ b/lib/assets/icons/VideoBot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/assets/icons/akar-icons_sound-on.svg b/lib/assets/icons/akar-icons_sound-on.svg new file mode 100644 index 0000000..068d9ca --- /dev/null +++ b/lib/assets/icons/akar-icons_sound-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/icons/copy.svg b/lib/assets/icons/copy.svg new file mode 100644 index 0000000..c4be72a --- /dev/null +++ b/lib/assets/icons/copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/assets/icons/material-symbols_download-rounded.svg b/lib/assets/icons/material-symbols_download-rounded.svg new file mode 100644 index 0000000..629096d --- /dev/null +++ b/lib/assets/icons/material-symbols_download-rounded.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/icons/refresh-2.svg b/lib/assets/icons/refresh-2.svg new file mode 100644 index 0000000..36d4d5a --- /dev/null +++ b/lib/assets/icons/refresh-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/icons/sms-tracking.svg b/lib/assets/icons/sms-tracking.svg new file mode 100644 index 0000000..be6c012 --- /dev/null +++ b/lib/assets/icons/sms-tracking.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/views/ai/ai_chat_page.dart b/lib/views/ai/ai_chat_page.dart index ddcddb6..aea5cb7 100644 --- a/lib/views/ai/ai_chat_page.dart +++ b/lib/views/ai/ai_chat_page.dart @@ -312,6 +312,96 @@ class _AiChatPageState extends State with TickerProviderStateMixin { } } + String _getBotTitle() { + final name = _currentBot.name?.toLowerCase() ?? ''; + if (name.contains('dall-e') || + name.contains('dalle') || + name.contains('image')) { + return 'ساخت عکس'; + } else if (name.contains('veo') || name.contains('video')) { + return 'ساخت ویدیو'; + } else if (name.contains('translate') || + name.contains('translator') || + name.contains('ترجمه')) { + return 'ترجمه'; + } else if (name.contains('summarize') || + name.contains('aisummery') || + name.contains('خلاصه')) { + return 'خلاصه‌ساز'; + } else if (name.contains('speech') || + name.contains('text-to-speech') || + name.contains('aiaudio') || + name.contains('متن به صوت')) { + return 'تبدیل متن به صوت'; + } else if (name.contains('chart') || + name.contains('graph') || + name.contains('نمودار') || + name.contains('تحلیل')) { + return 'تحلیل و ترسیم نمودار'; + } + return _currentBot.short ?? _currentBot.name ?? 'هوش مصنوعی'; + } + + String _getBotSubtitle() { + final name = _currentBot.name?.toLowerCase() ?? ''; + if (name.contains('dall-e') || + name.contains('dalle') || + name.contains('image')) { + return 'با استفاده از این بات، می‌توانید تصاویر با کیفیت ایجاد کنید. پس از توصیف ایده‌ خود در قابل چند عبارت، بات، تصویری نزدیک به درخواست شما تولید می‌کند. در صورت نیاز، می‌توانید توضیح خود را اصلاح کرده و دوباره امتحان کنید تا به نتیجه دلخواه برسید.'; + } else if (name.contains('veo') || name.contains('video')) { + return 'با استفاده از این بات، می‌توانید ویدیوهای کوتاه و ساده را از ایده‌ها، متن‌ها یا سناریوی موردنظر خود بسازید. کافی است توضیح دهید چه می‌خواهید، تا بات نسخه اولیه ویدیو را تولید کند. '; + } else if (name.contains('translate') || + name.contains('translator') || + name.contains('ترجمه')) { + return 'این بات به شما کمک می‌کند متن‌های خود را به‌صورت سریع و دقیق بین زبان‌های فارسی و انگلیسی ترجمه کنید. تنها کافی است جمله یا پاراگراف خود را وارد کنید تا ترجمه روان و قابل اتکا دریافت کنید. '; + } else if (name.contains('summarize') || + name.contains('aisummery') || + name.contains('خلاصه')) { + return 'با استفاده از این بات، می‌توانید هر متن طولانی را به نسخه‌ای کوتاه، روان و دقیق تبدیل کنید. کافی است متن خود را ارسال کنید تا بات، مهم‌ترین نکات آن را استخراج و در قالب خلاصه‌ای قابل‌فهم ارائه کند. '; + } else if (name.contains('speech') || + name.contains('text-to-speech') || + name.contains('aiaudio') || + name.contains('متن به صوت')) { + return 'این بات متن شما را با صدایی طبیعی و واضح به فایل صوتی تبدیل می‌کند. فقط کافی است متن موردنظر را وارد کنید تا نسخه صوتی آن را دریافت کنید. '; + } else if (name.contains('chart') || + name.contains('graph') || + name.contains('نمودار') || + name.contains('تحلیل')) { + return 'این بات امکان تولید انواع نمودارها و ترسیم‌های تحلیلی را برای شما فراهم می‌کند. تنها کافی است داده‌ها و نوع نمودار موردنظر را توضیح دهید تا خروجی تمیز و قابل استفاده دریافت کنید. اگر نتیجه مطابق نیازتان نبود، می‌توانید داده‌ها یا سبک نمودار را اصلاح و دوباره تولید کنید.'; + } + return _currentBot.description ?? ''; + } + + String? _getBotIcon() { + final name = _currentBot.name?.toLowerCase() ?? ''; + if (name.contains('dall-e') || + name.contains('dalle') || + name.contains('image')) { + return 'lib/assets/icons/CrateImage.svg'; + } else if (name.contains('veo') || name.contains('video')) { + return 'lib/assets/icons/VideoBot.svg'; + } else if (name.contains('translate') || + name.contains('translator') || + name.contains('ترجمه')) { + return 'lib/assets/icons/TranslateBot.svg'; + } else if (name.contains('summarize') || + name.contains('aisummery') || + name.contains('خلاصه')) { + return 'lib/assets/icons/SummeryBot.svg'; + } else if (name.contains('speech') || + name.contains('text-to-speech') || + name.contains('aiaudio') || + name.contains('متن به صوت')) { + return 'lib/assets/icons/TTV_Bot.svg'; + } else if (name.contains('chart') || + name.contains('graph') || + name.contains('نمودار') || + name.contains('تحلیل')) { + return 'lib/assets/icons/ChartBot.svg'; + } + return null; + } + @override Widget build(BuildContext context) { return WillPopScope( @@ -412,30 +502,91 @@ class _AiChatPageState extends State with TickerProviderStateMixin { ), ) : const SizedBox(height: 20), - TweenAnimationBuilder( - duration: const Duration(milliseconds: 1000), - tween: Tween(begin: 0.0, end: 1.0), - curve: Curves.easeOut, - builder: (context, value, child) { - return Opacity( - opacity: value, - child: Transform.translate( - offset: Offset(0, 20 * (1 - value)), - child: child, + if (widget.args.bot.id == 35) + TweenAnimationBuilder( + duration: const Duration(milliseconds: 1000), + tween: Tween(begin: 0.0, end: 1.0), + curve: Curves.easeOut, + builder: (context, value, child) { + return Center( + child: Opacity( + opacity: value, + child: Transform.translate( + offset: Offset(0, 20 * (1 - value)), + child: child, + ), + ), + ); + }, + child: Center( + child: Text( + 'چطور می‌تونم کمکت کنم؟', + style: TextStyle( + color: DesignConfig.isDark + ? const Color.fromARGB( + 255, 0, 90, 119) + : const Color.fromARGB( + 255, 0, 53, 70), + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + if (widget.args.bot.id != 35) + TweenAnimationBuilder( + duration: const Duration(milliseconds: 1000), + tween: Tween(begin: 0.0, end: 1.0), + curve: Curves.easeOut, + builder: (context, value, child) { + return Center( + child: Opacity( + opacity: value, + child: Transform.translate( + offset: Offset(0, 20 * (1 - value)), + child: child, + ), + ), + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24.0), + child: Column( + children: [ + if (_getBotIcon() != null) + const SizedBox( + height: 10, + ), + Padding( + padding: const EdgeInsets.only( + bottom: 16.0), + child: SvgPicture.asset( + _getBotIcon()!, + height: 70, + ), + ), + DidvanText( + _getBotTitle(), + textAlign: TextAlign.center, + fontSize: 18, + fontWeight: FontWeight.bold, + color: const Color.fromARGB( + 255, 27, 60, 89), + ), + const SizedBox(height: 12), + DidvanText( + _getBotSubtitle(), + textAlign: TextAlign.start, + fontSize: 14, + color: Theme.of(context) + .colorScheme + .caption, + ), + ], ), - ); - }, - child: Text( - 'چطور می‌تونم کمکت کنم؟', - style: TextStyle( - color: DesignConfig.isDark - ? const Color.fromARGB(255, 0, 90, 119) - : const Color.fromARGB(255, 0, 53, 70), - fontSize: 16, - fontWeight: FontWeight.bold, ), ), - ), const SizedBox(height: 70), if (widget.args.bot.id == 35) Column( @@ -529,14 +680,18 @@ class _AiChatPageState extends State with TickerProviderStateMixin { .messages .last .dateTime) - .toPersianDateStr() + .toPersianDateStr( + monthString: + '') .contains(DateTime.parse(DateTime .now() .subtract(const Duration( minutes: 210)) .toIso8601String()) - .toPersianDateStr())) { + .toPersianDateStr( + monthString: + ''))) { state.messages.last .prompts .add(Prompts( @@ -739,7 +894,7 @@ class _AiChatPageState extends State with TickerProviderStateMixin { borderRadius: DesignConfig.lowBorderRadius, ), child: DidvanText( - DateTime.parse(time).toPersianDateStr(), + DateTime.parse(time).toPersianDateStr(monthString: ''), style: Theme.of(context).textTheme.labelSmall, color: DesignConfig.isDark ? Theme.of(context).colorScheme.white @@ -793,7 +948,7 @@ class _AiChatPageState extends State with TickerProviderStateMixin { constraints: BoxConstraints( maxWidth: MediaQuery.sizeOf(context).width / 1.3), decoration: BoxDecoration( - borderRadius: DesignConfig.mediumBorderRadius.copyWith( + borderRadius: DesignConfig.highBorderRadius.copyWith( bottomLeft: !message.role.toString().contains('user') ? Radius.zero : null, @@ -804,11 +959,12 @@ class _AiChatPageState extends State with TickerProviderStateMixin { color: message.error != null && message.error! ? Theme.of(context).colorScheme.error.withOpacity(0.4) : (message.role.toString().contains('user') - ? Theme.of(context).colorScheme.surface - : Theme.of(context).colorScheme.focused) + ? Theme.of(context).colorScheme.focused + : Theme.of(context).colorScheme.surface) .withOpacity(0.9), border: Border.all( - color: Theme.of(context).colorScheme.border, + color: message.role.toString().contains('user') + ? const Color.fromARGB(0, 0, 0, 0) :Theme.of(context).colorScheme.border, width: 0.5, ), ), @@ -1000,20 +1156,16 @@ class _AiChatPageState extends State with TickerProviderStateMixin { Padding( padding: const EdgeInsets.all(8.0), child: InkWell( - onTap: () async { - state.isEdite = true; - state.message.text = - message.text.toString(); - state.update(); - }, - child: Icon( - Icons.edit_outlined, - size: 18, - color: Theme.of(context) - .colorScheme - .focusedBorder, - ), - ), + onTap: () async { + state.isEdite = true; + state.message.text = + message.text.toString(); + state.update(); + }, + child: SvgPicture.asset( + 'lib/assets/icons/edit-2.svg', + height: 20, + )), ), if (message.file != null && kIsWeb) Padding( @@ -1038,62 +1190,57 @@ class _AiChatPageState extends State with TickerProviderStateMixin { Padding( padding: const EdgeInsets.all(8.0), child: InkWell( - onTap: () async { - debugPrint( - "Download button tapped on iOS"); - final url = - '${RequestHelper.baseUrl + message.file.toString()}?accessToken=${RequestService.token}'; - await MediaService.downloadFile(url, - name: message.fileName); - }, - child: Icon( - DidvanIcons.download_solid, - size: 18, - color: Theme.of(context) - .colorScheme - .focusedBorder, - ), - ), + onTap: () async { + debugPrint( + "Download button tapped on iOS"); + final url = + '${RequestHelper.baseUrl + message.file.toString()}?accessToken=${RequestService.token}'; + await MediaService.downloadFile( + url, + name: message.fileName); + }, + child: SvgPicture.asset( + 'lib/assets/icons/material-symbols_download-rounded.svg', + height: 20, + )), ), if (message.error != null && message.error!) Padding( padding: const EdgeInsets.all(8.0), child: InkWell( - onTap: () async { - state.messages.last.prompts - .remove(message); - state.messages.last.prompts.add( - message.copyWith(error: false)); - state.file = file; - state.update(); - final botToUse = _isSearchMode && - _searchBot != null - ? _searchBot! - : _getSelectedBot(); - await state.postMessage( - botToUse, - widget.args.assistantsName != - null); - Future.delayed( - const Duration( - milliseconds: 100), () { - state.scrollController.animateTo( - state.scrollController.position - .maxScrollExtent, - duration: const Duration( - milliseconds: 300), - curve: Curves.easeOut, - ); - }); - }, - child: Icon( - DidvanIcons.refresh_solid, - size: 18, - color: Theme.of(context) - .colorScheme - .focusedBorder, - ), - ), + onTap: () async { + state.messages.last.prompts + .remove(message); + state.messages.last.prompts.add( + message.copyWith( + error: false)); + state.file = file; + state.update(); + final botToUse = _isSearchMode && + _searchBot != null + ? _searchBot! + : _getSelectedBot(); + await state.postMessage( + botToUse, + widget.args.assistantsName != + null); + Future.delayed( + const Duration( + milliseconds: 100), () { + state.scrollController + .animateTo( + state.scrollController + .position.maxScrollExtent, + duration: const Duration( + milliseconds: 300), + curve: Curves.easeOut, + ); + }); + }, + child: SvgPicture.asset( + 'lib/assets/icons/refresh-2.svg', + height: 20, + )), ), if (message.text != null && message.text!.isNotEmpty && @@ -1101,54 +1248,49 @@ class _AiChatPageState extends State with TickerProviderStateMixin { Padding( padding: const EdgeInsets.all(8.0), child: InkWell( - onTap: () async { - await Clipboard.setData( - ClipboardData( - text: state.messages[mIndex] - .prompts[index].text - .toString())); - Future.delayed( - Duration.zero, - () => ActionSheetUtils(context) - .showAlert( - AlertData( - message: - "متن با موفقیت کپی شد", - aLertType: ALertType.success, + onTap: () async { + await Clipboard.setData( + ClipboardData( + text: state + .messages[mIndex] + .prompts[index] + .text + .toString())); + Future.delayed( + Duration.zero, + () => ActionSheetUtils(context) + .showAlert( + AlertData( + message: + "متن با موفقیت کپی شد", + aLertType: + ALertType.success, + ), ), - ), - ); - }, - child: Icon( - DidvanIcons.copy_regular, - size: 18, - color: Theme.of(context) - .colorScheme - .focusedBorder, - ), - ), + ); + }, + child: SvgPicture.asset( + 'lib/assets/icons/copy.svg', + height: 20, + )), ), Padding( padding: const EdgeInsets.all(8.0), child: InkWell( - onTap: () async { - if (message.id != null) { - state.deleteMessage( - message.id!, mIndex, index); - } else { - state.messages[mIndex].prompts - .removeAt(index); - state.update(); - } - }, - child: Icon( - DidvanIcons.trash_solid, - size: 18, - color: Theme.of(context) - .colorScheme - .focusedBorder, - ), - ), + onTap: () async { + if (message.id != null) { + state.deleteMessage( + message.id!, mIndex, index); + } else { + state.messages[mIndex].prompts + .removeAt(index); + state.update(); + } + }, + child: SvgPicture.asset( + 'lib/assets/icons/trash.svg', + height: 20, + )), ), ], ), @@ -1165,7 +1307,8 @@ class _AiChatPageState extends State with TickerProviderStateMixin { mainAxisSize: MainAxisSize.min, children: [ DidvanText( - DateTimeUtils.timeWithAmPm(message.createdAt.toString()), + DateTimeUtils.timeWithAmPm(message.createdAt.toString()) + .toPersianDigit(), style: Theme.of(context).textTheme.labelSmall, color: Theme.of(context).colorScheme.caption, ), diff --git a/lib/views/ai/history_ai_chat_page.dart b/lib/views/ai/history_ai_chat_page.dart index 3f7ba84..8105c5c 100644 --- a/lib/views/ai/history_ai_chat_page.dart +++ b/lib/views/ai/history_ai_chat_page.dart @@ -95,7 +95,7 @@ class _HistoryAiChatPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8.0), child: Text('فهرست آرشیوشده‌ها', style: TextStyle( fontSize: 20, @@ -454,7 +454,7 @@ class _HistoryAiChatPageState extends State { DateTime.parse(chat .updatedAt .toString()) - .toPersianDateStr(), + .toPersianDateStr(monthString: ''), style: const TextStyle( fontSize: diff --git a/lib/views/authentication/screens/verification.dart b/lib/views/authentication/screens/verification.dart index 9d9f56c..0d3d84c 100644 --- a/lib/views/authentication/screens/verification.dart +++ b/lib/views/authentication/screens/verification.dart @@ -95,7 +95,7 @@ class _VerificationState extends State { crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ - DidvanText('کد تایید پنج رقمی به شماره ', + DidvanText('کد تایید شش رقمی به شماره ', style: Theme.of(context).textTheme.titleSmall, fontWeight: FontWeight.normal, color: Theme.of(context).colorScheme.caption), diff --git a/lib/views/comments/comments.dart b/lib/views/comments/comments.dart index 18c72fd..a32d9ee 100644 --- a/lib/views/comments/comments.dart +++ b/lib/views/comments/comments.dart @@ -75,7 +75,7 @@ class _CommentsState extends State { builder: (context, state, child) => SliverStateHandler( onRetry: state.getComments, state: state, - itemPadding: const EdgeInsets.only(top: 0, bottom: 20), + itemPadding: const EdgeInsets.only( bottom: 20), childCount: state.comments.length, placeholder: const _CommentPlaceholder(), centerEmptyState: false, @@ -109,7 +109,7 @@ class _CommentsState extends State { Positioned( left: 20, right: 20, - top: 0, + bottom: 20, child: CommentMessageBox(focusNode: _focusNode), ), ], diff --git a/lib/views/direct/widgets/message.dart b/lib/views/direct/widgets/message.dart index 844636c..9fe8a47 100644 --- a/lib/views/direct/widgets/message.dart +++ b/lib/views/direct/widgets/message.dart @@ -139,7 +139,7 @@ class _MessageState extends State with SingleTickerProviderStateMixin { if (widget.message.text != null) DidvanText( widget.message.text!, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.normal, ), if (widget.message.audio != null) AudioWave( @@ -263,7 +263,7 @@ class _MessageState extends State with SingleTickerProviderStateMixin { ), DidvanText( DateTimeUtils.timeWithAmPm( - widget.message.createdAt), + widget.message.createdAt).toPersianDigit(), style: Theme.of(context).textTheme.labelSmall, color: Theme.of(context).colorScheme.caption, ), diff --git a/lib/views/home/bookmarks/bookmarks.dart b/lib/views/home/bookmarks/bookmarks.dart index 1750e2a..c287cd0 100644 --- a/lib/views/home/bookmarks/bookmarks.dart +++ b/lib/views/home/bookmarks/bookmarks.dart @@ -102,7 +102,7 @@ class _BookmarksState extends State { iconWidget: 'lib/assets/icons/calendar.svg', color: const Color.fromARGB(255, 27, 60, 89), ), - const SizedBox(height: 8), + // const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ @@ -131,7 +131,7 @@ class _BookmarksState extends State { ], ), ), - const SizedBox(height: 28), + // const SizedBox(height: 28), ItemTitle( title: 'دسته بندی', style: Theme.of(context) @@ -179,8 +179,10 @@ class _BookmarksState extends State { hidePlayer: true, slivers: [ SliverAppBar( - pinned: true, + pinned: false, expandedHeight: 140, + scrolledUnderElevation: 0, + surfaceTintColor: Colors.transparent, backgroundColor: Theme.of(context).colorScheme.surface, automaticallyImplyLeading: false, flexibleSpace: FlexibleSpaceBar( diff --git a/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart b/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart index 12e64a6..9d03bff 100644 --- a/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart +++ b/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart @@ -161,6 +161,7 @@ class _FilteredBookmarksState extends State { onMarkChanged: (id, value) => _onBookmarkChanged(id, value, true, item.type), enableBookmark: true, + useCardStyle: true, // **اعمال استایل کارت در لیست بوکمارک** ); }, childCount: state.bookmarks.length + @@ -173,4 +174,4 @@ class _FilteredBookmarksState extends State { ), ); } -} +} \ No newline at end of file diff --git a/lib/views/home/main/main_page.dart b/lib/views/home/main/main_page.dart index d921bbd..7551859 100644 --- a/lib/views/home/main/main_page.dart +++ b/lib/views/home/main/main_page.dart @@ -164,10 +164,9 @@ class _DidvanSignalsTitle extends StatelessWidget { child: Row( children: [ SvgPicture.asset( - 'lib/assets/icons/voice-square.svg', - color: Theme.of(context).colorScheme.title, - width: 30, - height: 30, + 'lib/assets/icons/Signal.svg', + width: 25, + height: 25, ), const SizedBox(width: 5), DidvanText( @@ -192,10 +191,9 @@ class _DidvanVoiceTitle extends StatelessWidget { child: Row( children: [ SvgPicture.asset( - 'lib/assets/icons/voice-square.svg', - color: Theme.of(context).colorScheme.title, - width: 30, - height: 30, + 'lib/assets/icons/akar-icons_sound-on.svg', + width: 25, + height: 25, ), const SizedBox(width: 5), DidvanText( @@ -236,9 +234,9 @@ class _ExploreLatestTitle extends StatelessWidget { children: [ SvgPicture.asset( 'lib/assets/icons/discover.svg', - color: Theme.of(context).colorScheme.title, - width: 30, - height: 30, + width: 25, + height: 25, + color: const Color.fromARGB(255, 0, 126, 167), ), const SizedBox(width: 5), DidvanText( diff --git a/lib/views/home/main/widgets/didvan_plus_section.dart b/lib/views/home/main/widgets/didvan_plus_section.dart index feb0de2..768c7eb 100644 --- a/lib/views/home/main/widgets/didvan_plus_section.dart +++ b/lib/views/home/main/widgets/didvan_plus_section.dart @@ -109,10 +109,9 @@ class _DidvanPlusSectionState extends State { Row( children: [ SvgPicture.asset( - 'lib/assets/icons/voice-square.svg', - color: Theme.of(context).colorScheme.title, - width: 30, - height: 30, + 'lib/assets/icons/DidvanPlus.svg', + width: 25, + height: 25, ), const SizedBox(width: 5), DidvanText( diff --git a/lib/views/home/main/widgets/swot_item_card.dart b/lib/views/home/main/widgets/swot_item_card.dart index fd5b244..d79bfe8 100644 --- a/lib/views/home/main/widgets/swot_item_card.dart +++ b/lib/views/home/main/widgets/swot_item_card.dart @@ -9,6 +9,7 @@ import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:url_launcher/url_launcher_string.dart'; class SwotItemCard extends StatefulWidget { @@ -143,7 +144,7 @@ class _SwotItemCardState extends State { Text( "عدد ${widget.item.type == "THREAT" ? "تهدید" - : "فرصت"} : ${(widget.item.x1 * widget.item.y1).toStringAsFixed(1)}", + : "فرصت"} : ${(widget.item.x1 * widget.item.y1).toStringAsFixed(1).toPersianDigit()}", style: theme.textTheme.bodySmall?.copyWith( color: widget.item.type == "THREAT" ? Colors.red diff --git a/lib/views/home/media/podcast_tab_page.dart b/lib/views/home/media/podcast_tab_page.dart index 6954717..e45f0ee 100644 --- a/lib/views/home/media/podcast_tab_page.dart +++ b/lib/views/home/media/podcast_tab_page.dart @@ -292,10 +292,14 @@ class _PodcastTabPageState extends State { physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { final podcast = state.studios[index]; + + // NEW: بررسی اینکه آیا این آیتم، آخرین آیتم در لیست است + final bool isLast = index == state.studios.length - 1; return PodcastListCard( podcast: podcast, onTap: () => _navigateToDetails(index), + isLastItem: isLast, // NEW: ارسال پارامتر به ویجت کارت ); }, ), @@ -306,4 +310,4 @@ class _PodcastTabPageState extends State { onRetry: () => context.read().getStudios(page: 1), ); } -} +} \ No newline at end of file diff --git a/lib/views/home/media/video_details_page.dart b/lib/views/home/media/video_details_page.dart index 666265f..11a32c4 100644 --- a/lib/views/home/media/video_details_page.dart +++ b/lib/views/home/media/video_details_page.dart @@ -171,6 +171,8 @@ class _VideoDetailsPageState extends State appBar: PreferredSize( preferredSize: const Size.fromHeight(90.0), child: AppBar( + scrolledUnderElevation: 0, + surfaceTintColor: Colors.transparent, backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, automaticallyImplyLeading: false, @@ -232,18 +234,47 @@ class _VideoDetailsPageState extends State Positioned( top: 1, left: 1, - child: BookmarkButton( - value: state.studio.marked, - onMarkChanged: (value) { - if (widget.pageData['onMarkChanged'] != - null) { - widget.pageData['onMarkChanged']( - state.studio.id, value); - } - }, - gestureSize: 35, - type: 'video', - itemId: state.studio.id, + child: Row( + children: [ + Container( + height: 36, + decoration: BoxDecoration( + color: + Colors.black.withOpacity(0.6), + shape: BoxShape.circle, + ), + child: IconButton( + icon: SvgPicture.asset( + 'lib/assets/icons/fluent_mention-32-regular.svg', + width: 28, + height: 28, + ), + onPressed: () => + Navigator.of(context).pushNamed( + Routes.mentions, + arguments: { + 'id': state.studio.id, + 'type': 'studio', + 'title': state.studio.title, + }, + ), + ), + ), + BookmarkButton( + value: state.studio.marked, + onMarkChanged: (value) { + if (widget.pageData[ + 'onMarkChanged'] != + null) { + widget.pageData['onMarkChanged']( + state.studio.id, value); + } + }, + gestureSize: 35, + type: 'video', + itemId: state.studio.id, + ), + ], ), ), ], @@ -489,24 +520,24 @@ class _VideoDetailsPageState extends State return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - CircleAvatar( - radius: 20, - backgroundColor: DesignConfig.isDark - ? Colors.transparent - : Colors.white, - backgroundImage: - hasProfileImage ? NetworkImage(user.photo!) : null, - child: !hasProfileImage - ? Icon( - DidvanIcons.avatar_light, - size: 50, - color: DesignConfig.isDark - ? Colors.white - : Colors.black, - ) - : null, - ), - const SizedBox(width: 8), + // CircleAvatar( + // radius: 20, + // backgroundColor: DesignConfig.isDark + // ? Colors.transparent + // : Colors.white, + // backgroundImage: + // hasProfileImage ? NetworkImage(user.photo!) : null, + // child: !hasProfileImage + // ? Icon( + // DidvanIcons.avatar_light, + // size: 50, + // color: DesignConfig.isDark + // ? Colors.white + // : Colors.black, + // ) + // : null, + // ), + // const SizedBox(width: 8), Expanded( child: CommentMessageBox(focusNode: _focusNode), ), @@ -514,28 +545,32 @@ class _VideoDetailsPageState extends State ); }, ), - const SizedBox(height: 16), + SizedBox(height: state.studio.comments == 0 ? 0 : 16), SizedBox( width: double.infinity, - child: DidvanText( - 'نظرات کاربران:', - style: TextStyle( - color: DesignConfig.isDark - ? const Color.fromARGB(255, 0, 90, 119) - : const Color.fromARGB(255, 0, 53, 70), - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), + child: state.studio.comments == 0 + ? const SizedBox() + : DidvanText( + 'نظرات کاربران:', + style: TextStyle( + color: DesignConfig.isDark + ? const Color.fromARGB(255, 0, 90, 119) + : const Color.fromARGB(255, 0, 53, 70), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), ), const SizedBox(height: 16), - SizedBox( - height: 400, - child: Comments( - key: _commentsKey, - pageData: const {'isPage': false}, - ), - ), + state.studio.comments == 0 + ? const SizedBox() + : SizedBox( + height: 400, + child: Comments( + key: _commentsKey, + pageData: const {'isPage': false}, + ), + ), ], ), ), diff --git a/lib/views/home/media/widgets/podcast_list_card.dart b/lib/views/home/media/widgets/podcast_list_card.dart index 6c1c654..6038e69 100644 --- a/lib/views/home/media/widgets/podcast_list_card.dart +++ b/lib/views/home/media/widgets/podcast_list_card.dart @@ -12,11 +12,13 @@ import 'package:persian_number_utility/persian_number_utility.dart'; class PodcastListCard extends StatelessWidget { final OverviewData podcast; final VoidCallback onTap; + final bool isLastItem; // NEW: پارامتر جدید برای تشخیص آیتم آخر const PodcastListCard({ super.key, required this.podcast, required this.onTap, + this.isLastItem = false, // NEW: مقداردهی اولیه پارامتر }); String _formatDuration(int? duration) { @@ -141,14 +143,16 @@ class PodcastListCard extends StatelessWidget { ), ), ), - Divider( - color: Colors.grey[300], - thickness: 2, - indent: 16, - endIndent: 16, - ), + // NEW: چک می‌کنیم که آیا آیتم آخر است یا نه + if (!isLastItem) + Divider( + color: Colors.grey[300], + thickness: 2, + indent: 16, + endIndent: 16, + ), ], ), ); } -} +} \ No newline at end of file diff --git a/lib/views/home/search/widgets/search_result_item.dart b/lib/views/home/search/widgets/search_result_item.dart index 89371bc..7560307 100644 --- a/lib/views/home/search/widgets/search_result_item.dart +++ b/lib/views/home/search/widgets/search_result_item.dart @@ -16,6 +16,7 @@ import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -54,26 +55,26 @@ class SearchResultItem extends StatelessWidget { return null; } - IconData get _icon { + String get _icon { if (item.type == 'radar') { - return DidvanIcons.scanning_light; + return 'lib/assets/icons/Pouyesh_Ofogh_New.svg'; } if (item.type == 'news') { - return DidvanIcons.foolad_light; + return 'lib/assets/icons/Donye_Foolad.svg'; } if (item.type == 'video') { - return DidvanIcons.video_light; + return 'lib/assets/icons/video-play.svg'; } if (item.type == 'podcast') { - return DidvanIcons.podcast_light; + return 'lib/assets/icons/play-circle.svg'; } if (item.type == 'delphi') { - return DidvanIcons.saha_light; + return 'lib/assets/icons/Saha.svg'; } if (item.type == 'infography') { - return DidvanIcons.infography_regular; + return 'lib/assets/icons/hugeicons_chart-02.svg'; } - return DidvanIcons.radar_light; + return 'DidvanIcons.radar_light'; } void _openInteractiveViewer(BuildContext context, String image) { @@ -156,16 +157,16 @@ class SearchResultItem extends StatelessWidget { Container( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.secondary, - borderRadius: const BorderRadius.horizontal( + decoration: const BoxDecoration( + color: Color.fromARGB(255, 0, 126, 167), + borderRadius: BorderRadius.horizontal( left: Radius.circular(10), ), ), - child: Icon( + child: SvgPicture.asset( _icon, color: Theme.of(context).colorScheme.white, - size: 18, + height: 18, ), ), ], @@ -173,7 +174,7 @@ class SearchResultItem extends StatelessWidget { const SizedBox(width: 8), Expanded( child: SizedBox( - height: 200, + height: 80, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/views/mentions/mentions.dart b/lib/views/mentions/mentions.dart index e793115..edb034b 100644 --- a/lib/views/mentions/mentions.dart +++ b/lib/views/mentions/mentions.dart @@ -97,7 +97,7 @@ class _MentionsState extends State { subtitle: widget.pageData['title'], ) : null, - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 92), + padding: const EdgeInsets.only(bottom: 92), showSliversFirst: false, slivers: [ Consumer( @@ -105,14 +105,18 @@ class _MentionsState extends State { SliverStateHandler( onRetry: state.getComments, state: state, - itemPadding: const EdgeInsets.symmetric(vertical: 16), + itemPadding: const EdgeInsets.symmetric(vertical: 0), childCount: state.comments.length, placeholder: const _MentionPlaceholder(), centerEmptyState: _isPage, enableEmptyState: state.comments.isEmpty, - emptyState: EmptyState( - asset: Assets.emptyChat, - title: 'دوستان خود را فراخوانی کنید'), + paddingEmptyState: 0, + emptyState: const EmptyState( + asset: 'lib/assets/images/empty_states/Empty_List.png', + title: 'دوستان خود را فراخوانی کنید', + titleColor: Color.fromARGB(255, 0, 126, 167), + height: 550, + ), builder: (context, state, index) => Mention( key: ValueKey( state.comments[index].id.toString() + diff --git a/lib/views/monthly/monthly_list_page.dart b/lib/views/monthly/monthly_list_page.dart index fb552e0..d5da769 100644 --- a/lib/views/monthly/monthly_list_page.dart +++ b/lib/views/monthly/monthly_list_page.dart @@ -73,11 +73,11 @@ class _MonthlyListPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ ItemTitle( - title: 'تاریخ ایجاد', + title: 'تاریخ', style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), - const SizedBox(height: 8), + // const SizedBox(height: 8), Row( children: [ DatePickerButton( @@ -95,7 +95,7 @@ class _MonthlyListPageState extends State { ), ], ), - const SizedBox(height: 28), + // const SizedBox(height: 28), if (state.categories.isNotEmpty) ...[ ItemTitle( title: 'دسته بندی', diff --git a/lib/views/news/news.dart b/lib/views/news/news.dart index 0954f4c..f2ba1df 100644 --- a/lib/views/news/news.dart +++ b/lib/views/news/news.dart @@ -222,11 +222,11 @@ class _NewsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ ItemTitle( - title: 'تاریخ خبر', + title: 'تاریخ', style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), - const SizedBox(height: 8), + // const SizedBox(height: 8), Row( children: [ DatePickerButton( @@ -246,7 +246,7 @@ class _NewsState extends State { ), ], ), - const SizedBox(height: 28), + // const SizedBox(height: 28), ItemTitle( title: 'دسته بندی', icon: DidvanIcons.category_regular, diff --git a/lib/views/notification_settings/notification_settings.dart b/lib/views/notification_settings/notification_settings.dart index 5736663..bcb1322 100644 --- a/lib/views/notification_settings/notification_settings.dart +++ b/lib/views/notification_settings/notification_settings.dart @@ -286,32 +286,12 @@ class _NotificationSettingsState extends State ), TimeSliderPicker( selectedTime: state.selectedTime, - isDisabled: state.isAnytime, + isDisabled: false, onTimeChanged: (newTime) { state.selectedTime = newTime; state.update(); }, ), - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 16), - // child: Container( - // padding: const EdgeInsets.all(16), - // decoration: BoxDecoration( - // color: Theme.of(context).colorScheme.surface, - // border: Border.all( - // color: Theme.of(context).colorScheme.border, - // width: 1), - // borderRadius: BorderRadius.circular(18)), - // child: DidvanSwitch( - // value: state.isAnytime, - // title: "دریافت آنی اعلانات", - // onChanged: (val) { - // state.isAnytime = val; - // state.update(); - // }, - // ), - // ), - // ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.all(15.0), diff --git a/lib/views/onboarding/onboarding_indicator_painter.dart b/lib/views/onboarding/onboarding_indicator_painter.dart index ccafffc..f5bd5dc 100644 --- a/lib/views/onboarding/onboarding_indicator_painter.dart +++ b/lib/views/onboarding/onboarding_indicator_painter.dart @@ -13,7 +13,7 @@ class OnboardingIndicatorPainter extends CustomPainter { required this.currentPage, required this.activeColor, required this.inactiveColor, - this.strokeWidth = 6.0, + this.strokeWidth = 4.0, }); @override diff --git a/lib/views/onboarding/onboarding_page.dart b/lib/views/onboarding/onboarding_page.dart index 6da59c7..291ecd5 100644 --- a/lib/views/onboarding/onboarding_page.dart +++ b/lib/views/onboarding/onboarding_page.dart @@ -6,30 +6,28 @@ import 'package:didvan/views/onboarding/onboarding_indicator_painter.dart'; import 'package:flutter_svg/flutter_svg.dart'; final List onboardingPages = [ - OnboardingEntity( - imagePath: 'lib/assets/images/onboarding/1.png', - title: 'چرا دیدوان؟', - description: - 'جغرافیای زمانی هیچ‌گاه نمی‌ایستد و سفر به آینده یک سفر شگفت‌انگیز به جایی است که قسمت‌هایی از آن را قبلاً در گذشته دیده‌ایم، قسمت‌هایی از آن را طی مسیر به تدریج می‌بینیم و قسمت‌هایی از آن کاملاً بدیع هستند. برای سفر مطمئن در جغرافیای زمانی ما به سیستم‌های پیش‌نگر نیاز داریم و یکی از ارکان این سیستم‌ها، سامانه‌های رصد راهبردی هستند.', - ), OnboardingEntity( imagePath: 'lib/assets/images/onboarding/2.png', - title: 'رادارهای استراتژیک', + title: 'رصدخانه استراتژیک', description: - 'رادارهای استراتژیک، قلب دیدوان هستند. رادارهایی که قرار است دید 360 درجه‌ای برای مدیران ایجاد کنند و به صورت مستمر گزارش‌هایی تحلیلی را از محیط‌های دور و نزدیک هر کسب و کار در اختیار مدیران قرار ‌دهند. رادارها به صورت موضوعی شامل رادار روند، رادار ریسک، رادار تکنولوژی، رادار استارت‌آپ و ماژول فرصت و تهدید دسته بندی شده‌اند و مدیران می‌توانند پیرامون این گزارشات به بحث و تبادل نظر بپردازند.', - ), - OnboardingEntity( - imagePath: 'lib/assets/images/onboarding/3.png', - title: 'استودیو دیدوان', - description: - '''در استودیو آینده شما می‌توانید ویدیوهایی مستند از مسائلی مانند آموزش مفاهیم آینده‌گرا، تحلیل روندها، تحلیل صنعت و یا هر نوع اطلاعاتی که می‌تواند برای تصمیم‌گیری مدیران ارشد راهگشا باشد را ببینید. -همچنین در بخش دیگر این استودیو و در قالب پادکست، اطلاعاتی از جنس مسائل پیش گفته که ارزش تبدیل به فایل صوتی دارند را، می‌شنوید. - ''', + 'ارائه گزارش‌های تحلیلی از محیط‌های دور و نزدیک صنعت و آخرین اخبار مرتبط با محیط رقابتی، ایجاد دید ۳۶۰ درجه محیطی در قالب ماهنامه‌های تحلیلی، رادارهای روند، ریسک، استارت‌آپ، تکنولوژی، شناسایی فرصت‌ها و تهدیدها و سامانه هم اندیشی سها.', ), OnboardingEntity( imagePath: 'lib/assets/images/onboarding/4.png', title: 'نبض صنعت', - description: 'در دنیای امروز، تصمیم‌گیری‌های کلیدی در صنایع مختلف به شدت وابسته به داده‌های دقیق و به‌روز شده‌اند. این داده‌ها شامل اطلاعات اقتصادی، مالی، خرید و فروش، بازار سرمایه، ارزهای دیجیتال، شاخص‌های کلان اقتصادی و دیگر حوزه‌ها هستند. استفاده هوشمندانه از داده‌ها این امکان را به مدیران می‌دهد تا تصمیماتی آگاهانه بگیرند، ریسک‌ها را کاهش دهند و عملکرد کلی خود را به سطحی بالاتر ارتقاء دهند.', + description: + 'ارائه داده‌های دقیق و به‌روز از کل زنجیره صنعت فولاد از مواد اولیه تا محصولات نهایی، شاخص‌های کلان اقتصادی، بازار سرمایه و دیگر اطلاعات لازم به منظور تصمیم‌گیری آگاهانه، کاهش ریسک و ارتقای عملکرد مدیران.', + ), + OnboardingEntity( + imagePath: 'lib/assets/images/onboarding/3.png', + title: 'استودیو آینده', + description: + 'ارائه مفاهیم آینده‌گرا، تحلیل روندها، تحلیل صنعت و یا هر نوع اطلاعات مورد نیاز برای تصمیم‌گیری مدیران ارشد در قالب ویدیوکست، پادکست و اینفوگرافی.', + ), + OnboardingEntity( + imagePath: 'lib/assets/images/onboarding/1.png', + title: 'هوشان', + description: 'ارائه ابزارهای هوش مصنوعی مورد نیاز مدیران اعم از خلاصه‌ساز، ساخت عکس، تحلیل و ترسیم نمودار، تبدیل متن به صوت، ساخت ویدیو و ترجمه، امکان پرسش و پاسخ از مدل‌های زبانی مختلف و یا جستجوی هوشمند در محتوای داخلی از طریق دستیار اختصاصی هوش مصنوعی دیدوان.', ), ]; @@ -130,13 +128,13 @@ class _OnboardingPageState extends State { }, ), SizedBox( - width: 60, - height: 60, + width: 50, + height: 50, child: Stack( alignment: Alignment.center, children: [ CustomPaint( - size: const Size(60, 60), + size: const Size(50, 50), painter: OnboardingIndicatorPainter( pageCount: onboardingPages.length, currentPage: _currentPage, @@ -148,8 +146,8 @@ class _OnboardingPageState extends State { GestureDetector( onTap: _onNextTap, child: Container( - width: 60, - height: 45, + width: 50, + height: 35, decoration: const BoxDecoration( color: Color.fromARGB(255, 1, 35, 72), shape: BoxShape.circle, @@ -170,6 +168,7 @@ class _OnboardingPageState extends State { ], ), ), + const SizedBox(height: 10,), Expanded( flex: 2, child: Padding( @@ -196,7 +195,7 @@ class _OnboardingPageState extends State { style: theme.textTheme.bodyMedium?.copyWith( color: const Color.fromARGB(255, 41, 41, 41), height: 1.6, - fontSize: 15 + fontSize: 14 ), ), ], @@ -237,7 +236,7 @@ class _OnboardingPageState extends State { ), Positioned( - bottom: 40, + bottom: 30, left: 24, right: 24, child: Column( diff --git a/lib/views/podcasts/podcasts.dart b/lib/views/podcasts/podcasts.dart index 7b30944..4bf6f51 100644 --- a/lib/views/podcasts/podcasts.dart +++ b/lib/views/podcasts/podcasts.dart @@ -267,7 +267,7 @@ class _PodcastsState extends State { style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), - const SizedBox(height: 8), + // const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ diff --git a/lib/views/podcasts/studio_details/studio_details.mobile.dart b/lib/views/podcasts/studio_details/studio_details.mobile.dart index cf4ac7d..6dad1c3 100644 --- a/lib/views/podcasts/studio_details/studio_details.mobile.dart +++ b/lib/views/podcasts/studio_details/studio_details.mobile.dart @@ -312,6 +312,8 @@ class _StudioDetailsState extends State appBar: PreferredSize( preferredSize: const Size.fromHeight(90.0), child: AppBar( + scrolledUnderElevation: 0, + surfaceTintColor: Colors.transparent, backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, automaticallyImplyLeading: false, @@ -389,24 +391,55 @@ class _StudioDetailsState extends State scale: _bookmarkScaleAnimation, child: RotationTransition( turns: _bookmarkRotationAnimation, - child: BookmarkButton( - value: state.studio.marked, - onMarkChanged: (value) { - if (widget.pageData[ - 'onMarkChanged'] != - null) { - widget.pageData[ - 'onMarkChanged']( - state.studio.id, - value, - true); - } - }, - gestureSize: 35, - type: state.studio.type == 'video' - ? 'video' - : 'podcast', - itemId: state.studio.id, + child: Row( + children: [ + Container( + height: 36, + decoration: BoxDecoration( + color: Colors.black + .withOpacity(0.6), + shape: BoxShape.circle, + ), + child: IconButton( + icon: SvgPicture.asset( + 'lib/assets/icons/fluent_mention-32-regular.svg', + width: 28, + height: 28, + ), + onPressed: () => + Navigator.of(context) + .pushNamed( + Routes.mentions, + arguments: { + 'id': state.studio.id, + 'type': 'studio', + 'title': + state.studio.title, + }, + ), + ), + ), + BookmarkButton( + value: state.studio.marked, + onMarkChanged: (value) { + if (widget.pageData[ + 'onMarkChanged'] != + null) { + widget.pageData[ + 'onMarkChanged']( + state.studio.id, + value, + true); + } + }, + gestureSize: 35, + type: + state.studio.type == 'video' + ? 'video' + : 'podcast', + itemId: state.studio.id, + ), + ], ), ), ), @@ -710,28 +743,38 @@ class _StudioDetailsState extends State ); }, ), - const SizedBox(height: 16), + SizedBox(height: state.studio.comments == 0 ? 0 : 16), SizedBox( width: double.infinity, - child: DidvanText( - 'نظرات کاربران:', - style: TextStyle( - color: DesignConfig.isDark - ? const Color.fromARGB(255, 0, 90, 119) - : const Color.fromARGB(255, 0, 53, 70), - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), + child: state.studio.comments == 0 + ? const SizedBox( + height: 0, + ) + : DidvanText( + 'نظرات کاربران:', + style: TextStyle( + color: DesignConfig.isDark + ? const Color.fromARGB(255, 0, 90, 119) + : const Color.fromARGB(255, 0, 53, 70), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), ), const SizedBox(height: 16), - SizedBox( - height: 400, - child: Comments( - key: _commentsKey, - pageData: const {'isPage': false}, - ), - ), + state.studio.comments == 0 + ? const Padding( + padding: EdgeInsets.all(32.0), + child: SizedBox( + height: 5, + )) + : SizedBox( + height: 500, + child: Comments( + key: _commentsKey, + pageData: const {'isPage': false}, + ), + ), ], ), ), diff --git a/lib/views/profile/change_password/widgets/verify_otp_screen.dart b/lib/views/profile/change_password/widgets/verify_otp_screen.dart index 11069db..a3fe1d6 100644 --- a/lib/views/profile/change_password/widgets/verify_otp_screen.dart +++ b/lib/views/profile/change_password/widgets/verify_otp_screen.dart @@ -13,6 +13,7 @@ import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:provider/provider.dart'; @@ -272,7 +273,7 @@ class _VerifyOtpScreenState extends State ), children: [ const TextSpan( - text: 'ما یک کد تأیید 6 رقمی به شماره '), + text: 'ما یک کد تأیید ۶ رقمی به شماره '), TextSpan( text: phoneNumber.convertToPersianDigits(), style: TextStyle( @@ -404,7 +405,7 @@ class _VerifyOtpScreenState extends State if (_remainingTime > 0) ...[ const SizedBox(width: 8), DidvanText( - _formattedTime, + _formattedTime.toPersianDigit(), style: Theme.of(context) .textTheme .bodyMedium diff --git a/lib/views/profile/direct_list/widgets/direct_item.dart b/lib/views/profile/direct_list/widgets/direct_item.dart index 394ca39..9a7beb7 100644 --- a/lib/views/profile/direct_list/widgets/direct_item.dart +++ b/lib/views/profile/direct_list/widgets/direct_item.dart @@ -44,9 +44,8 @@ class ChatRoomItem extends StatelessWidget { Row( children: [ SvgPicture.asset( - chatRoom.unread != 0 - ? 'lib/assets/icons/sms-notification.svg' - : 'lib/assets/icons/sms.svg', + chatRoom.lastMessage.writedByAdmin? chatRoom.lastMessage.readed ?'lib/assets/icons/sms.svg' :'lib/assets/icons/sms-notification.svg' + : 'lib/assets/icons/sms-tracking.svg', height: 24, width: 24, color: chatRoom.unread != 0 @@ -88,12 +87,8 @@ class ChatRoomItem extends StatelessWidget { child: Padding( padding: const EdgeInsets.fromLTRB(10, 8, 10, 8), child: Text( - chatRoom.lastMessage.readed - ? chatRoom.lastMessage.writedByAdmin - ? 'پاسخ داده شده' - : 'در انتظار پاسخ' - : 'خوانده نشده', - ), + chatRoom.lastMessage.writedByAdmin? chatRoom.lastMessage.readed + ? 'پاسخ داده شده' : 'خوانده نشده' : 'در انتظار پاسخ',), ), ), // Icon( diff --git a/lib/views/profile/profile.dart b/lib/views/profile/profile.dart index b935e89..e1cc586 100644 --- a/lib/views/profile/profile.dart +++ b/lib/views/profile/profile.dart @@ -6,6 +6,7 @@ import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/providers/theme.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; +import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/profile/general_settings/settings_state.dart'; @@ -90,6 +91,7 @@ class _ProfilePageState extends State @override Widget build(BuildContext context) { final user = context.watch().user; + print('DEBUG - User Photo URL: ${user.photo}'); return Consumer( builder: (context, state, child) => StateHandler( @@ -105,6 +107,8 @@ class _ProfilePageState extends State backgroundColor: Theme.of(context).colorScheme.surface, automaticallyImplyLeading: false, leadingWidth: 200, + scrolledUnderElevation: 0, + surfaceTintColor: Colors.transparent, leading: Padding( padding: const EdgeInsetsDirectional.only(start: 0.0), child: SvgPicture.asset( @@ -155,7 +159,11 @@ class _ProfilePageState extends State image: (user.photo != null && user.photo!.isNotEmpty) ? DecorationImage( - image: NetworkImage(user.photo!), + image: NetworkImage('https://api.didvan.app${user.photo!}', + headers: { + 'Authorization': + 'Bearer ${RequestService.token}', + }), fit: BoxFit.cover, ) : null, diff --git a/lib/views/radar/radar.dart b/lib/views/radar/radar.dart index 3237871..f4fb2b7 100644 --- a/lib/views/radar/radar.dart +++ b/lib/views/radar/radar.dart @@ -388,7 +388,7 @@ class _RadarStateView extends State { style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), - const SizedBox(height: 8), + // const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ @@ -408,7 +408,7 @@ class _RadarStateView extends State { ], ), ), - const SizedBox(height: 28), + // const SizedBox(height: 28), ItemTitle( title: 'دسته بندی', icon: DidvanIcons.category_regular, diff --git a/lib/views/widgets/home_app_bar.dart b/lib/views/widgets/home_app_bar.dart index a0523f4..87bc11c 100644 --- a/lib/views/widgets/home_app_bar.dart +++ b/lib/views/widgets/home_app_bar.dart @@ -189,6 +189,7 @@ class HomeAppBar extends StatelessWidget { confrimTitle: 'نمایش نتایج', onDismissed: () => state.resetFilters(false), onConfirmed: () => state.searchAll(page: 1), + hasPadding: true, content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -202,7 +203,7 @@ class HomeAppBar extends StatelessWidget { iconWidget: 'lib/assets/icons/calendar.svg', color: const Color.fromARGB(255, 27, 60, 89), ), - const SizedBox(height: 8), + // const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ @@ -223,7 +224,7 @@ class HomeAppBar extends StatelessWidget { ], ), ), - const SizedBox(height: 28), + // const SizedBox(height: 28), ], ItemTitle( title: 'دسته بندی', diff --git a/lib/views/widgets/hoshan_home_app_bar.dart b/lib/views/widgets/hoshan_home_app_bar.dart index fb174dc..18d860f 100644 --- a/lib/views/widgets/hoshan_home_app_bar.dart +++ b/lib/views/widgets/hoshan_home_app_bar.dart @@ -580,7 +580,7 @@ class _HistoryDrawerContentState extends State { Widget _buildChatItem(dynamic chat, dynamic historyState) { final int index = historyState.chats.indexOf(chat); return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0), child: InkWell( onTap: () { Navigator.pop(context); @@ -596,7 +596,7 @@ class _HistoryDrawerContentState extends State { }, borderRadius: BorderRadius.circular(12), child: Padding( - padding: const EdgeInsets.fromLTRB(8, 5, 10, 5), + padding: const EdgeInsets.fromLTRB(8, 0, 10, 10), child: SizedBox( child: Row( children: [ diff --git a/lib/views/widgets/logo_app_bar.dart b/lib/views/widgets/logo_app_bar.dart index a155bfb..db6f4fa 100644 --- a/lib/views/widgets/logo_app_bar.dart +++ b/lib/views/widgets/logo_app_bar.dart @@ -155,7 +155,7 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), - const SizedBox(height: 8), + // const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ @@ -176,7 +176,7 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { ], ), ), - const SizedBox(height: 28), + // const SizedBox(height: 28), ], ItemTitle( title: 'دسته بندی', diff --git a/lib/views/widgets/overview/multitype.dart b/lib/views/widgets/overview/multitype.dart index daf9b3b..65999b1 100644 --- a/lib/views/widgets/overview/multitype.dart +++ b/lib/views/widgets/overview/multitype.dart @@ -5,6 +5,7 @@ import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; +import 'package:didvan/views/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text_field.dart'; @@ -21,6 +22,7 @@ class MultitypeOverview extends StatelessWidget { final bool enableCaption; final bool enableBookmark; final bool showDivider; + final bool useCardStyle; // **جدید: برای کنترل استایل کارت** const MultitypeOverview({ Key? key, @@ -30,9 +32,11 @@ class MultitypeOverview extends StatelessWidget { this.enableCaption = false, this.enableBookmark = false, this.showDivider = true, + this.useCardStyle = false, // **مقدار پیش‌فرض false** }) : super(key: key); get _targetPageArgs { +// ... if (item.type == 'radar') { return const RadarRequestArgs(page: 0); } @@ -43,6 +47,7 @@ class MultitypeOverview extends StatelessWidget { } String? get _targetPageRouteName { +// ... if (item.type == 'radar') { return Routes.radarDetails; } @@ -59,6 +64,7 @@ class MultitypeOverview extends StatelessWidget { } String get _icon { +// ... switch (item.type) { case 'radar': return 'lib/assets/icons/Pouyesh_Ofogh_New.svg'; @@ -82,7 +88,8 @@ class MultitypeOverview extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( + // محتوای داخلی کارت + final innerContent = Column( children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -224,33 +231,57 @@ class MultitypeOverview extends StatelessWidget { ), ), if (!enableCaption) const Spacer(), - // if (enableBookmark) ...[ - // const SizedBox( - // width: 12, - // ), - // BookmarkButton( - // value: item.marked, - // onMarkChanged: (value) => onMarkChanged(item.id, value), - // gestureSize: 32, - // type: item.type, - // itemId: item.id, - // askForConfirmation: true, - // svgIconOn: 'lib/assets/icons/bookmark_fill.svg', - // svgIconOff: 'lib/assets/icons/archive-tick.svg', - // color: Theme.of(context).colorScheme.primary, - // ), - // ], + if (enableBookmark) ...[ + const SizedBox( + width: 12, + ), + BookmarkButton( + value: item.marked, + onMarkChanged: (value) => onMarkChanged(item.id, value), + gestureSize: 20, + type: item.type, + itemId: item.id, + askForConfirmation: true, + svgIconOn: 'lib/assets/icons/bookmark_fill.svg', + svgIconOff: 'lib/assets/icons/archive-tick.svg', + color: Theme.of(context).colorScheme.primary, + ), + ], ], ), - const SizedBox( - height: 10, - ), - Visibility( - visible: showDivider, - child: Divider(color: Theme.of(context).colorScheme.border), - ), + // **اگر useCardStyle فعال نباشد، فاصله و خط جداکننده قبلی را اضافه می‌کنیم** + if (!useCardStyle) ...[ + const SizedBox( + height: 10, + ), + Visibility( + visible: showDivider, + child: Divider(color: Theme.of(context).colorScheme.border), + ), + ], ], ); + + // **اعمال استایل کارت (Container با Border و Padding) به صورت شرطی** + if (useCardStyle) { + return Padding( + padding: const EdgeInsets.all(20.0), + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: Theme.of(context).colorScheme.border, + width: 1, + ), + ), + child: innerContent, + ), + ); + } + + return innerContent; // در غیر این صورت، فقط محتوای داخلی را برمی‌گرداند. } static Widget get placeholder => const DidvanCard( @@ -275,4 +306,4 @@ class MultitypeOverview extends StatelessWidget { ], ), ); -} +} \ No newline at end of file diff --git a/lib/views/widgets/overview/news.dart b/lib/views/widgets/overview/news.dart index 5965b38..397961e 100644 --- a/lib/views/widgets/overview/news.dart +++ b/lib/views/widgets/overview/news.dart @@ -140,7 +140,7 @@ class NewsOverview extends StatelessWidget { askForConfirmation: hasUnmarkConfirmation, svgIconOn: 'lib/assets/icons/bookmark_fill.svg', svgIconOff: 'lib/assets/icons/archive-tick.svg', - color: Theme.of(context).colorScheme.caption, + color: const Color.fromARGB(255, 0, 126, 167), unbookmarkedColor: Theme.of(context).colorScheme.caption, ), ], diff --git a/lib/views/widgets/overview/radar.dart b/lib/views/widgets/overview/radar.dart index 97015f1..b32b81a 100644 --- a/lib/views/widgets/overview/radar.dart +++ b/lib/views/widgets/overview/radar.dart @@ -175,7 +175,7 @@ class RadarOverview extends StatelessWidget { askForConfirmation: hasUnmarkConfirmation, svgIconOn: 'lib/assets/icons/bookmark_fill.svg', svgIconOff: 'lib/assets/icons/archive-tick.svg', - color: Theme.of(context).colorScheme.caption, + color: const Color.fromARGB(255, 0, 126, 167), unbookmarkedColor: Theme.of(context).colorScheme.caption, ), diff --git a/lib/views/widgets/search_app_bar.dart b/lib/views/widgets/search_app_bar.dart index c92428a..8af5352 100644 --- a/lib/views/widgets/search_app_bar.dart +++ b/lib/views/widgets/search_app_bar.dart @@ -95,7 +95,7 @@ class SearchAppBar extends StatelessWidget implements PreferredSizeWidget { style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), - const SizedBox(height: 8), + // const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ @@ -116,7 +116,7 @@ class SearchAppBar extends StatelessWidget implements PreferredSizeWidget { ], ), ), - const SizedBox(height: 28), + // const SizedBox(height: 28), ], ItemTitle( title: 'دسته بندی', diff --git a/lib/views/widgets/state_handlers/empty_connection.dart b/lib/views/widgets/state_handlers/empty_connection.dart index ce8f496..6702eb4 100644 --- a/lib/views/widgets/state_handlers/empty_connection.dart +++ b/lib/views/widgets/state_handlers/empty_connection.dart @@ -13,7 +13,6 @@ class EmptyConnection extends StatelessWidget { title: 'اتصال اینترنت برقرار نیست', titleColor: const Color.fromARGB(255, 0, 126, 167), action: onRetry, - subtitle: 'لطفاً اتصال وای‌فای یا داده تلفن همراه خود را بررسی کنید.', buttonTitle: 'تلاش مجدد' , ); } diff --git a/lib/views/widgets/state_handlers/empty_list.dart b/lib/views/widgets/state_handlers/empty_list.dart index 8976bff..7e051b3 100644 --- a/lib/views/widgets/state_handlers/empty_list.dart +++ b/lib/views/widgets/state_handlers/empty_list.dart @@ -8,7 +8,7 @@ class EmptyList extends StatelessWidget { Widget build(BuildContext context) { return const EmptyState( asset: 'lib/assets/images/empty_states/Bookmark2.png', - height: 600, + height: 400, title: 'رصدخانه شما هنوز خالی است', titleColor: Color.fromARGB(255, 0, 126, 167), subtitle: diff --git a/lib/views/widgets/state_handlers/empty_state.dart b/lib/views/widgets/state_handlers/empty_state.dart index aa9c7c1..c8e8f65 100644 --- a/lib/views/widgets/state_handlers/empty_state.dart +++ b/lib/views/widgets/state_handlers/empty_state.dart @@ -29,8 +29,8 @@ class EmptyState extends StatelessWidget { final isSvg = asset.toLowerCase().endsWith('.svg'); return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: subtitle != null ? MainAxisAlignment.start : MainAxisAlignment.center, + crossAxisAlignment: subtitle != null ? CrossAxisAlignment.start : CrossAxisAlignment.center, children: [ SizedBox( width: double.infinity, @@ -40,26 +40,26 @@ class EmptyState extends StatelessWidget { : Image.asset(asset, fit: BoxFit.fitWidth, height: height, width: double.infinity), ), - const SizedBox(height: 10), + const SizedBox(height: 20), Padding( padding: const EdgeInsets.only(right: 20, left: 20), child: DidvanText( title, style: Theme.of(context).textTheme.displaySmall, color: titleColor ?? Theme.of(context).colorScheme.caption, - textAlign: TextAlign.center, + textAlign: TextAlign.start, ), ), - // if (subtitle != null) const SizedBox(height: 8), - // if (subtitle != null) - // Padding( - // padding: const EdgeInsets.only(right: 20, left: 20), - // child: DidvanText( - // subtitle!, - // color: Theme.of(context).colorScheme.caption, - // ), - // ), - // if (action != null) const SizedBox(height: 16), + if (subtitle != null) const SizedBox(height: 8), + if (subtitle != null) + Padding( + padding: const EdgeInsets.only(right: 20, left: 20), + child: DidvanText( + subtitle!, + color: Theme.of(context).colorScheme.caption, + ), + ), + if (action != null) const SizedBox(height: 16), if (action != null) Center( child: Padding(