import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:provider/provider.dart'; import 'package:didvan/routes/routes.dart'; // ignore: depend_on_referenced_packages import 'package:shamsi_date/shamsi_date.dart'; class HoshanHomeAppBar extends StatefulWidget { const HoshanHomeAppBar({super.key}); @override State createState() => _HoshanHomeAppBarState(); } class _HoshanHomeAppBarState extends State { // String? _welcomeMessage; // bool _isLoadingWelcome = true; // @override // void initState() { // super.initState(); // fetchWelcomeMessage(); // } // Future fetchWelcomeMessage() async { ... } void showHistoryDrawer(BuildContext context) { showGeneralDialog( context: context, barrierDismissible: true, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, barrierColor: Colors.black54, transitionDuration: const Duration(milliseconds: 300), pageBuilder: (context, animation, secondaryAnimation) { return const HistoryDrawerContent(); }, transitionBuilder: (context, animation, secondaryAnimation, child) { return SlideTransition( position: Tween( begin: const Offset(-1, 0), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )), child: child, ); }, ); } void _startNewChat(BuildContext context) { final historyState = context.read(); void navigateToNewChat() { if (historyState.bots.isNotEmpty) { Navigator.of(context).pushNamed( Routes.aiChat, arguments: AiChatArgs( bot: historyState.bots.first, isTool: historyState.bots, ), ); } } if (historyState.bots.isEmpty) { historyState.getBots(); Future.delayed(const Duration(milliseconds: 500), navigateToNewChat); } else { navigateToNewChat(); } } @override Widget build(BuildContext context) { final userProvider = context.watch(); return Container( decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 78, 82, 84) : const Color.fromRGBO(230, 242, 246, 1), borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(32), bottomRight: Radius.circular(32), ), ), child: SafeArea( bottom: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SvgPicture.asset( Assets.horizontalLogoWithText, height: 60, color: Theme.of(context).colorScheme.title, ), const Spacer(), GestureDetector( onTap: () { print('history bottom tapped'); final historyState = context.read(); if (historyState.chats.isEmpty) { historyState.getChats(); } showHistoryDrawer(context); }, child: Container( padding: const EdgeInsets.all(8), child: SvgPicture.asset( 'lib/assets/icons/history.svg', height: 24, color: Theme.of(context).colorScheme.inputText, ), ), ), ], ), Container( padding: const EdgeInsets.fromLTRB(8, 35, 8, 35), alignment: Alignment.center, child: userProvider.isLoadingWelcome ? const SizedBox(height: 20) : userProvider.welcomeMessage != null ? DidvanText( userProvider.welcomeMessage!, fontSize: 21, fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.title, ) : const SizedBox(height: 20), ), GestureDetector( onTap: () { _startNewChat(context); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 11), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 195, 195, 196) : Colors.white, borderRadius: BorderRadius.circular(30), border: Border.all( color: const Color.fromARGB(255, 25, 93, 128), width: 1), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Row( children: [ GestureDetector( onTap: () { _startNewChat(context); }, child: Container( padding: const EdgeInsets.all(8), decoration: const BoxDecoration( color: Color.fromARGB(255, 25, 93, 128), shape: BoxShape.circle, ), child: SvgPicture.asset( 'lib/assets/icons/microphone-2.svg', width: 20, height: 20, colorFilter: const ColorFilter.mode( Colors.white, BlendMode.srcIn, ), ), ), ), const SizedBox(width: 12), const Expanded( child: DidvanText( 'سوال خود را بپرسید...', color: Color.fromARGB(255, 18, 17, 16), fontSize: 14, ), ), const SizedBox(width: 12), GestureDetector( onTap: () { _startNewChat(context); }, child: Container( padding: const EdgeInsets.all(1), child: SvgPicture.asset( 'lib/assets/icons/send_bold.svg', width: 30, height: 30, ), ), ), ], ), ), ), const SizedBox(height: 12), const DidvanText( 'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از وارد کردن اطلاعات حساس بپرهیزید.', textAlign: TextAlign.center, fontSize: 11, color: Color.fromARGB(255, 18, 17, 16), maxLines: 3, ), ], ), ), ), ); } } class HistoryDrawerContent extends StatefulWidget { const HistoryDrawerContent({super.key}); @override State createState() => _HistoryDrawerContentState(); } class _HistoryDrawerContentState extends State { final TextEditingController _searchController = TextEditingController(); String _searchQuery = ''; @override void initState() { super.initState(); _searchController.addListener(() { setState(() { _searchQuery = _searchController.text; }); }); } @override void dispose() { _searchController.dispose(); super.dispose(); } DateTime? _parseDateString(String? dateString) { if (dateString == null) return null; try { return DateTime.parse(dateString); } catch (e) { return null; } } String _getOlderGroupName(DateTime date) { final now = DateTime.now(); final nowJalali = Jalali.fromDateTime(now); final dateJalali = Jalali.fromDateTime(date); if (nowJalali.year != dateJalali.year) { return dateJalali.year.toString(); } return _getMonthName(dateJalali.month); } String _getMonthName(int month) { const monthNames = [ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند', ]; if (month >= 1 && month <= 12) { return monthNames[month - 1]; } return month.toString(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Align( alignment: Alignment.centerLeft, child: Material( color: Colors.transparent, child: Container( width: MediaQuery.of(context).size.width * 0.75, height: MediaQuery.of(context).size.height, decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 166, 166, 166) : const Color.fromARGB(255, 237, 237, 237), boxShadow: [ BoxShadow( // ignore: deprecated_member_use color: Colors.black.withOpacity(0.2), blurRadius: 20, offset: const Offset(5, 0), ), ], ), child: Consumer( builder: (context, historyState, child) { final filteredChats = historyState.chats.where((chat) { final title = chat.title?.toLowerCase() ?? 'گفتگو'; final query = _searchQuery.toLowerCase(); return title.contains(query); }).toList(); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final yesterday = today.subtract(const Duration(days: 1)); final last7Days = today.subtract(const Duration(days: 7)); final last30Days = today.subtract(const Duration(days: 30)); final List todayChats = []; final List yesterdayChats = []; final List last7DaysChats = []; final List last30DaysChats = []; final Map> olderChatsGrouped = {}; for (final chat in filteredChats) { final chatDate = _parseDateString(chat.updatedAt); final chatDay = chatDate != null ? DateTime(chatDate.year, chatDate.month, chatDate.day) : null; if (chatDay == null) { const groupName = 'قدیمی‌تر'; if (olderChatsGrouped[groupName] == null) { olderChatsGrouped[groupName] = []; } olderChatsGrouped[groupName]!.add(chat); } else if (chatDay.isAtSameMomentAs(today)) { todayChats.add(chat); } else if (chatDay.isAtSameMomentAs(yesterday)) { yesterdayChats.add(chat); } else if (chatDay.isAfter(last7Days)) { last7DaysChats.add(chat); } else if (chatDay.isAfter(last30Days)) { last30DaysChats.add(chat); } else { final String groupName = _getOlderGroupName(chatDate!); if (olderChatsGrouped[groupName] == null) { olderChatsGrouped[groupName] = []; } olderChatsGrouped[groupName]!.add(chat); } } return Column( children: [ SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ const SizedBox(height: 50), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.asset( 'lib/assets/icons/Online Chat Support.svg', height: 60, ), ], ), const SizedBox(height: 26), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 11), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 155, 154, 154) : const Color.fromARGB(255, 237, 237, 237), borderRadius: BorderRadius.circular(8), border: Border.all( color: DesignConfig.isDark ? const Color.fromARGB( 255, 107, 106, 106) : const Color.fromARGB( 255, 223, 223, 223), width: 1)), child: Row( children: [ SvgPicture.asset( 'lib/assets/icons/search-normal2.svg'), const SizedBox(width: 8), Expanded( child: TextField( controller: _searchController, decoration: InputDecoration( hintText: 'جستجو', hintStyle: TextStyle( color: DesignConfig.isDark ? const Color.fromARGB( 255, 113, 112, 1112) : const Color.fromARGB( 255, 161, 160, 160), fontSize: 14), border: InputBorder.none, isDense: true, contentPadding: EdgeInsets.zero, ), ), ), if (_searchQuery.isNotEmpty) GestureDetector( onTap: () { _searchController.clear(); }, child: Icon(Icons.close, color: Colors.grey[600], size: 20), ), ], ), ), Padding( padding: const EdgeInsets.only(top: 30.0), child: InkWell( onTap: () { Navigator.pop(context); Navigator.of(context) .pushNamed(Routes.aiArchivedHistory); }, child: Row( children: [ SvgPicture.asset( 'lib/assets/icons/direct-inbox.svg', color: const Color.fromARGB(255, 61, 61, 61), height: 20, ), const SizedBox(width: 8), DidvanText( 'گفت‌وگوهای آرشیو شده', style: TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ), ), ], ), ), ), Padding( padding: const EdgeInsets.fromLTRB(40, 10, 40, 0), child: Divider( height: 3, color: DesignConfig.isDark ? const Color.fromARGB(255, 113, 112, 1112) : const Color.fromARGB(255, 161, 160, 160), ), ), Expanded( child: historyState.loadinggetAll ? const Center(child: CircularProgressIndicator()) : historyState.chats.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.chat_bubble_outline, size: 64, color: Colors.grey[400], ), const SizedBox(height: 16), DidvanText( 'هنوز گفتگویی\nوجود ندارد', textAlign: TextAlign.center, color: Colors.grey[600], ), ], ), ) : filteredChats.isEmpty ? Center( child: DidvanText( 'موردی یافت نشد', textAlign: TextAlign.center, color: Colors.grey[600], ), ) : ListView( padding: const EdgeInsets.symmetric(vertical: 4), children: [ if (todayChats.isNotEmpty) ...[ _buildSectionHeader('امروز'), ...todayChats.map((chat) => _buildChatItem(chat, historyState)), ], if (yesterdayChats.isNotEmpty) ...[ _buildSectionHeader('دیروز'), ...yesterdayChats.map((chat) => _buildChatItem(chat, historyState)), ], if (last7DaysChats.isNotEmpty) ...[ _buildSectionHeader('۷ روز اخیر'), ...last7DaysChats.map((chat) => _buildChatItem(chat, historyState)), ], if (last30DaysChats.isNotEmpty) ...[ _buildSectionHeader('۳۰ روز اخیر'), ...last30DaysChats.map((chat) => _buildChatItem(chat, historyState)), ], ...olderChatsGrouped.keys .map((groupName) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSectionHeader(groupName), ...olderChatsGrouped[groupName]! .map((chat) => _buildChatItem( chat, historyState)), ], ); }), ], ), ), ], ); }, ), ), ), ); } Widget _buildSectionHeader(String title) { return Padding( padding: const EdgeInsets.fromLTRB(20, 16, 25, 16), child: DidvanText( title, fontSize: 14, fontWeight: FontWeight.bold, color: const Color.fromARGB(255, 0, 126, 167), ), ); } Widget _buildChatItem(dynamic chat, dynamic historyState) { final int index = historyState.chats.indexOf(chat); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0), child: InkWell( onTap: () { Navigator.pop(context); if (historyState.bots.isNotEmpty) { Navigator.of(context).pushNamed( Routes.aiChat, arguments: AiChatArgs( chat: chat, bot: historyState.bots.first, ), ); } }, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 10, 10), child: SizedBox( child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ DidvanText( chat.title ?? 'گفتگو', maxLines: 2, overflow: TextOverflow.ellipsis, fontSize: 14, fontWeight: FontWeight.w500, ), if (chat.updatedAt != null) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ DidvanText( _formatDateFromString(chat.updatedAt) .toPersianDigit(), fontSize: 12, color: Colors.grey[600], ), PopupMenuButton( icon: SvgPicture.asset('lib/assets/icons/more.svg'), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), elevation: 4, padding: EdgeInsets.zero, offset: const Offset(0, 10), onSelected: (value) async { if (index == -1) return; switch (value) { case 'rename': _showRenameDialog( chat, historyState, index); break; case 'archive': await historyState.archivedChat( chat.id!, index, refresh: false); historyState.update(); break; case 'delete': await historyState.deleteChat( chat.id!, index, refresh: false); historyState.update(); break; } }, itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: 'rename', padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 12), child: SizedBox( width: 180, child: Column( children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/edit-2.svg', height: 20, ), const SizedBox(width: 16), const Text('تغییر نام', style: TextStyle(fontSize: 13)), ], ), const SizedBox(height: 20), const Divider( height: 1, color: Color.fromARGB( 255, 227, 226, 225), ) ], ), ), ), PopupMenuItem( value: 'archive', padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 12), child: SizedBox( width: 180, child: Column( children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/direct-inbox.svg', height: 20, ), const SizedBox(width: 16), const Text('آرشیو', style: TextStyle(fontSize: 13)), ], ), const SizedBox(height: 20), const Divider( height: 1, color: Color.fromARGB( 255, 227, 226, 225), ) ], ), ), ), PopupMenuItem( value: 'delete', padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 1), child: SizedBox( width: 150, child: Row( children: [ SvgPicture.asset( 'lib/assets/icons/trash.svg', height: 20, // ignore: deprecated_member_use color: const Color.fromARGB( 255, 0, 126, 167), ), const SizedBox(width: 16), const Text('حذف', style: TextStyle( fontSize: 13, )), ], ), ), ), ], ), ], ), ], ], ), ), ], ), ), ), ), ); } String _formatDate(DateTime date) { final dateJalali = Jalali.fromDateTime(date); final year = dateJalali.year.toString(); final month = dateJalali.month.toString().padLeft(2, '0'); final day = dateJalali.day.toString().padLeft(2, '0'); final hour = date.hour.toString().padLeft(2, '0'); final minute = date.minute.toString().padLeft(2, '0'); return '$year.$month.$day - $hour:$minute'; } String _formatDateFromString(String? dateString) { if (dateString == null) return ''; try { final date = DateTime.parse(dateString); return _formatDate(date); } catch (e) { return ''; } } void _showRenameDialog( dynamic chat, HistoryAiChatState historyState, int index) { final controller = TextEditingController(text: chat.title); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('تغییر نام گفتگو'), content: TextField( controller: controller, decoration: const InputDecoration( hintText: 'نام جدید را وارد کنید', border: OutlineInputBorder(), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('لغو'), ), TextButton( onPressed: () async { if (controller.text.isNotEmpty) { await historyState.changeNameChat( chat.id!, index, controller.text, refresh: false, ); historyState.update(); } Navigator.pop(context); }, child: const Text('ذخیره'), ), ], ), ); } }