// ignore_for_file: deprecated_member_use_from_same_package, use_build_context_synchronously import 'dart:math'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hoshan/core/gen/assets.gen.dart'; import 'package:hoshan/core/utils/date_time.dart'; import 'package:hoshan/core/utils/strings.dart'; import 'package:hoshan/data/model/ai/chats_history_model.dart'; import 'package:hoshan/data/model/empty_states_enum.dart'; import 'package:hoshan/data/model/popup_menu_model.dart'; import 'package:hoshan/ui/screens/main/home_page.dart'; import 'package:hoshan/ui/screens/library/bloc/chats_history_bloc.dart'; import 'package:hoshan/ui/screens/library/cubit/handle_archive_cubit.dart'; import 'package:hoshan/ui/theme/colors.dart'; import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart'; import 'package:hoshan/ui/theme/text.dart'; import 'package:hoshan/ui/screens/library/cubit/chat_row_edit_cubit.dart'; import 'package:hoshan/ui/widgets/components/button/circle_icon_btn.dart'; import 'package:hoshan/ui/widgets/components/dialog/dialog_handler.dart'; import 'package:hoshan/ui/widgets/components/image/network_image.dart'; import 'package:hoshan/ui/widgets/components/text/auth_text_field.dart'; import 'package:hoshan/ui/widgets/components/text/search_text_field.dart'; import 'package:hoshan/ui/widgets/sections/empty/empty_states.dart'; import 'package:hoshan/ui/widgets/sections/header/primary_appbar.dart'; import 'package:hoshan/ui/widgets/sections/loading/default_placeholder.dart'; import 'package:hoshan/ui/widgets/sections/loading/listview_placeholder.dart'; import 'package:shamsi_date/shamsi_date.dart'; class LibraryScreen extends StatefulWidget { final Function(Chats chat)? onTap; final String type; const LibraryScreen({super.key, required this.type, this.onTap}); @override State createState() => _LibraryScreenState(); } class _LibraryScreenState extends State { @override void dispose() { super.dispose(); EasyDebounce.cancelAll(); } ValueNotifier date = ValueNotifier(null); Jalali? dateJalali; final TextEditingController searchTextController = TextEditingController(); bool archive = false; final ScrollController scrollController = ScrollController(); @override void initState() { super.initState(); scrollController.addListener( () { if (scrollController.position.pixels == scrollController.position.maxScrollExtent && context.read().state is ChatsHistorySuccess) { context.read().add(GetAllChats( type: widget.type, archive: archive, date: date.value, search: searchTextController.text)); } }, ); } @override Widget build(BuildContext context) { return Column( children: [ PrimaryAppbar( context, onBack: () { Scaffold.of(context).closeDrawer(); }, actions: [ const SizedBox( width: 16, ), GestureDetector( onTap: () async { await DialogHandler(context: context).showDeleteItem( title: 'همه نتایج جستجو پاک شوند؟', description: 'با این کار اطلاعات شما ازبین خواهد رفت.', onConfirm: () { context .read() .add(RemoveAll(archive: archive)); }, ); }, child: SizedBox( width: 24, height: 24, child: Assets.icon.outline.trash .svg(color: Theme.of(context).colorScheme.onSurface))), const SizedBox( width: 24, ), GestureDetector( onTap: () async { if (context.read().state is ChatsHistoryLoading) { return; } archive = !archive; ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context.read().add(GetAllChats( type: widget.type, search: searchTextController.text, date: date.value, archive: archive)); setState(() {}); }, child: SizedBox( width: 24, height: 24, child: (archive ? Assets.icon.outline.directSend : Assets.icon.outline.directInbox) .svg(color: Theme.of(context).colorScheme.onSurface))), const SizedBox( width: 16, ), ], ), Expanded( child: RefreshIndicator( onRefresh: () async { ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context.read().add(GetAllChats( type: widget.type, search: searchTextController.text, date: date.value, archive: archive)); scrollController.jumpTo(0); }, backgroundColor: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.primary, child: SingleChildScrollView( controller: scrollController, physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics()), child: Column( children: [ SearchTextField( focusNode: searchFocus, controller: searchTextController, suffixIcon: ValueListenableBuilder( valueListenable: date, builder: (context, d, _) { return Row( mainAxisSize: MainAxisSize.min, children: [ if (d != null) Transform.scale( scale: 0.8, child: Transform.rotate( angle: pi / 4, child: CircleIconBtn( icon: Assets.icon.outline.add, color: context .read() .isDark() ? AppColors.black[900] : AppColors.secondryColor[50], iconColor: AppColors.secondryColor.defaultShade, onTap: () { date.value = null; dateJalali = null; ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context.read().add( GetAllChats( type: widget.type, search: searchTextController.text, date: date.value, archive: archive)); return; }, ), ), ), Padding( padding: const EdgeInsets.all(12), child: GestureDetector( onTap: () async { await DialogHandler(context: context) .showDatePicker( dateCounts: 1, onConfirm: (p0) { if (p0.isEmpty) { if (date.value != null) { date.value = null; dateJalali = null; ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context .read() .add(GetAllChats( type: widget.type, search: searchTextController .text, date: date.value, archive: archive)); } return; } dateJalali = p0.first; DateTime miladiDate = dateJalali!.toDateTime(); date.value = '${miladiDate.year}-${miladiDate.month}-${miladiDate.day}'; ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context.read().add( GetAllChats( type: widget.type, search: searchTextController.text, date: date.value, archive: archive)); }, ); }, child: Assets.icon.outline.filter.svg(), ), ), ], ); }), onChanged: (searchText) { if (searchText.isEmpty) { EasyDebounce.cancelAll(); ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context.read().add(GetAllChats( type: widget.type, date: date.value, archive: archive)); return; } EasyDebounce.debounce( 'my-debouncer', // <-- An ID for this particular debouncer const Duration( seconds: 1), // <-- The debounce duration () { ChatsHistoryBloc.page = 1; ChatsHistoryBloc.lastPage = null; context.read().add(GetAllChats( type: widget.type, search: searchText, date: date.value, archive: archive)); } // <-- The target method ); }, ), BlocConsumer( listener: (context, state) {}, builder: (context, state) { if (state is ChatsHistorySuccess || state is ChatsHistoryLoading) { if (state.chatsInDates.isEmpty) { return Center( child: EmptyStates.getEmptyState( status: archive ? EmptyStatesEnum.archive : EmptyStatesEnum.messages), ); } return SingleChildScrollView( physics: const NeverScrollableScrollPhysics(), child: Column( children: [ ListView.builder( itemCount: state.chatsInDates.length, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, allIndex) { return state .chatsInDates[allIndex].chats.isEmpty ? const SizedBox() : Column( children: [ if (allIndex != 0) const SizedBox( height: 8, ), Padding( padding: const EdgeInsets.symmetric( horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text( state.chatsInDates[allIndex] .title, style: AppTextStyles.body4 .copyWith( color: Theme.of( context) .colorScheme .onSurface, fontWeight: FontWeight .bold), ), ], ), ), ListView.builder( itemCount: state .chatsInDates[allIndex] .chats .length, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, chatIndex) { final chat = state .chatsInDates[allIndex] .chats[chatIndex]; return chatRow(context, chat); }, ), ], ); }, ), if (state is ChatsHistoryLoading) Padding( padding: const EdgeInsets.symmetric( vertical: 12.0), child: LinearProgressIndicator( color: AppColors.primaryColor.defaultShade, borderRadius: BorderRadius.circular(16), ), ), const SizedBox( height: 36, ) ], ), ); } return ListviewPlaceholder( child: Container( width: MediaQuery.sizeOf(context).width, height: 58, margin: const EdgeInsets.symmetric( horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16)), )); }, ), ], ), ), ), ), ], ); } Widget chatRow(BuildContext context, Chats chatModel) { final List popups = [ PopupMenuModel( id: 0, title: 'تغییر نام', icon: Assets.icon.outline.edit2), // PopupMenuModel( // id: 1, title: 'به اشتراک گذاری', icon: Assets.icon.outline.share), PopupMenuModel( id: 2, title: archive ? 'خارج کردن از آرشیو‌ها' : 'آرشیو کردن', icon: archive ? Assets.icon.outline.directSend : Assets.icon.outline.directInbox), PopupMenuModel(id: 3, title: 'پاک کردن', icon: Assets.icon.outline.trash), ]; late final TextEditingController editingController = TextEditingController( text: chatModel.title!.replaceAll("\"", ''), ); ValueNotifier isEdit = ValueNotifier(false); late Chats chat = chatModel; return MultiBlocProvider( providers: [ BlocProvider(create: (context) => ChatRowEditCubit()), BlocProvider( create: (context) => HandleArchiveCubit()), ], child: BlocConsumer( listener: (context, archState) { if (archState is HandleArchiveSuccess) { context .read() .add(RemoveChat(chats: chat, withCall: false)); } }, builder: (context, archState) { if (archState is HandleArchiveLoading) { return DefaultPlaceHolder( child: Container( width: MediaQuery.sizeOf(context).width, height: 58, margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8)), )); } return BlocBuilder( builder: (context, state) { return GestureDetector( onTap: () async { // Scaffold.of(context).closeDrawer(); // await Future.delayed(Duration(milliseconds: 300)); widget.onTap?.call(chat); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12), margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 4), width: MediaQuery.sizeOf(context).width, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8)), child: ValueListenableBuilder( valueListenable: isEdit, builder: (context, edit, _) { return Row( children: [ state is ChatRowEditLoading ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( color: AppColors.primaryColor.defaultShade, ), ) : edit ? GestureDetector( onTap: () async { await context .read() .editTitle( id: chat.id!, title: editingController.text); chat = chat.copyWith( title: editingController.text); isEdit.value = false; }, child: Assets.icon.outline.tickCircle .svg( color: AppColors.gray[context .read() .isDark() ? 600 : 900])) : PopupMenuButton( tooltip: '', offset: const Offset(0, 18), onSelected: (value) async { switch (value.id) { case 0: isEdit.value = true; break; case 1: break; case 2: archive ? await context .read< HandleArchiveCubit>() .removeFromArchive( chat.id!) : await context .read< HandleArchiveCubit>() .addToArchive(chat.id!); break; case 3: // widget.onDelete.call(); await DialogHandler( context: context) .showDeleteItem( title: 'تاریخچه گفتگو پاک شود؟', description: 'با این کار اطلاعات شما از بین خواهد رفت.', onConfirm: () { context .read() .add(RemoveChat( chats: chat)); }, ); break; default: } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8)), itemBuilder: (BuildContext context) { return >[ ...List.generate( popups.length, (index) => PopupMenuItem( value: popups[index], height: 32, child: Directionality( textDirection: TextDirection.rtl, child: ListTile( minTileHeight: 32, title: Text( popups[index].title, style: AppTextStyles .body5 .copyWith( color: Theme.of( context) .colorScheme .onSurface, fontWeight: FontWeight .bold), ), leading: popups[index] .icon! .svg( color: AppColors .secondryColor .defaultShade, width: 16, height: 16), )), ), ) ]; }, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10.0), child: Assets.icon.outline.more.svg( color: AppColors.gray[context .read() .isDark() ? 600 : 900]), )), const SizedBox( width: 4, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ edit ? AuthTextField( controller: editingController, maxLines: 4, minLines: 4, ) : Directionality( textDirection: chat.title!.startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, child: Text( chat.title!.replaceAll("\"", ''), style: AppTextStyles.body5.copyWith( color: Theme.of(context) .colorScheme .onSurface), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), const SizedBox( height: 4, ), Text( DateTimeUtils .convertStringIsoToStringInFormatted( chat.createdAt!), style: AppTextStyles.body6.copyWith( color: AppColors.gray[context .read() .isDark() ? 600 : 900]), ) ], ), ), if (widget.type != 'llm') Row( children: [ const SizedBox( width: 8, ), ImageNetwork( width: 32, height: 32, url: chat.bot!.image, radius: 360, ), ], ) ], ); }), ), ); }, ); }, ), ); } }