// ignore_for_file: use_build_context_synchronously, deprecated_member_use_from_same_package import 'package:cross_file/cross_file.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hoshan/core/gen/assets.gen.dart'; import 'package:hoshan/core/services/file_manager/pick_file_services.dart'; import 'package:hoshan/core/utils/date_time.dart'; import 'package:hoshan/core/utils/file.dart'; import 'package:hoshan/core/utils/strings.dart'; import 'package:hoshan/data/model/empty_states_enum.dart'; import 'package:hoshan/data/model/ticket_model.dart'; import 'package:hoshan/ui/screens/ticket/bloc/send_ticket_bloc.dart'; import 'package:hoshan/ui/theme/colors.dart'; import 'package:hoshan/ui/theme/responsive.dart'; import 'package:hoshan/ui/theme/text.dart'; import 'package:hoshan/ui/widgets/components/audio/player.dart'; import 'package:hoshan/ui/widgets/components/audio/recorder.dart'; import 'package:hoshan/ui/widgets/components/button/circle_icon_btn.dart'; import 'package:hoshan/ui/widgets/components/dialog/bottom_sheets.dart'; import 'package:hoshan/ui/widgets/components/dialog/dialog_handler.dart'; import 'package:hoshan/ui/widgets/components/dropdown/more_popup_menu.dart'; import 'package:hoshan/ui/widgets/components/image/custome_image.dart'; import 'package:hoshan/ui/widgets/components/image/network_image.dart'; import 'package:hoshan/ui/widgets/components/snackbar/snackbar_manager.dart'; import 'package:hoshan/ui/widgets/sections/empty/empty_states.dart'; import 'package:hoshan/ui/widgets/sections/header/reversible_appbar.dart'; import 'package:hoshan/ui/widgets/sections/loading/random_container.dart'; class TicketPage extends StatefulWidget { const TicketPage({super.key}); @override State createState() => _TicketPageState(); } class _TicketPageState extends State { ValueNotifier visibleAttach = ValueNotifier(false); ValueNotifier visibleRecorder = ValueNotifier(false); ValueNotifier selectedFile = ValueNotifier(null); final TextEditingController message = TextEditingController(); final GlobalKey containerKey = GlobalKey(); @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: ReversibleAppbar( context, titleText: 'ارسال تیکت به پشتیبانی', ), body: Responsive(context).maxWidthInDesktop( child: (contxet, maxWidth) => BlocBuilder( builder: (context, state) { if (state is SendTicketInitial) { return ListView.builder( shrinkWrap: true, itemCount: 20, reverse: true, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.only(top: 24, bottom: 90), itemBuilder: (context, index) { return RandomContainer(isUser: index % 2 == 0); }, ); } return state.tickets.isEmpty ? EmptyStates.getEmptyState(status: EmptyStatesEnum.inbox) : SingleChildScrollView( reverse: true, physics: const BouncingScrollPhysics(), child: ListView.builder( shrinkWrap: true, itemCount: state.tickets.length, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.only(top: 24, bottom: 90), itemBuilder: (context, index) { final ticketList = state.tickets[index].tickets; return Column( children: [ Row( children: [ const Expanded(child: Divider()), Padding( padding: const EdgeInsets.symmetric( horizontal: 24.0), child: Text( state.tickets[index].date ?? '', style: AppTextStyles.body4.copyWith( color: Theme.of(context) .colorScheme .onSurface), textDirection: TextDirection.rtl, ), ), const Expanded(child: Divider()), ], ), const SizedBox( height: 8, ), ListView.builder( physics: const NeverScrollableScrollPhysics(), itemCount: ticketList.length, shrinkWrap: true, itemBuilder: (context, tIndex) { Ticket ticket = ticketList[tIndex]; return chatBubble(ticket, maxWidth, state.tickets[index].date ?? ''); }, ), ], ); }, ), ); }, ), ), bottomSheet: chatBar(), ); } Container chatBar() { return Container( key: containerKey, color: Theme.of(context).colorScheme.surface, child: ValueListenableBuilder( valueListenable: selectedFile, builder: (context, file, child) { return ValueListenableBuilder( valueListenable: visibleRecorder, builder: (context, inRrecording, child) { return inRrecording ? Container( margin: const EdgeInsets.symmetric( horizontal: 18.0, vertical: 12), padding: const EdgeInsets.all(4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Theme.of(context).colorScheme.surface), child: Recorder( play: true, onDelete: () { visibleRecorder.value = false; selectedFile.value = null; visibleAttach.value = false; }, onRecordFinish: (fileRecorded) { visibleRecorder.value = false; visibleAttach.value = false; selectedFile.value = fileRecorded; }, onError: (p0) { visibleRecorder.value = false; selectedFile.value = null; visibleAttach.value = false; }, ), ) : Column( mainAxisSize: MainAxisSize.min, children: [ ValueListenableBuilder( valueListenable: visibleAttach, builder: (context, show, child) { return show ? Container( margin: const EdgeInsets.fromLTRB( 32, 24, 16, 24) .copyWith(bottom: 0), child: Row( children: [ CircleIconBtn( icon: Assets .icon.outline.galleryAdd, color: Theme.of(context) .colorScheme .primary, iconColor: Colors.white, onTap: () async { await BottomSheetHandler( context) .showPickImage( onSelect: (file) { selectedFile.value = file; visibleAttach.value = false; }, ); }), const SizedBox( width: 8, ), CircleIconBtn( icon: Assets.icon.outline.musicnote, color: Theme.of(context) .colorScheme .primary, iconColor: Colors.white, onTap: () async { final file = await PickFileService(context) .getFile( fileType: FileType.audio); if (file != null) { selectedFile.value = file.single; visibleAttach.value = false; } }, ), const SizedBox( width: 8, ), CircleIconBtn( icon: Assets.icon.outline.cardAdd, color: Theme.of(context) .colorScheme .primary, iconColor: Colors.white, onTap: () async { final file = await PickFileService( context) .getFile( fileType: FileType.custom, allowedExtensions: [ 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'xlsm', 'xlsb', 'xlt', 'xltx', 'xltm' ]); if (file != null) { selectedFile.value = file.single; visibleAttach.value = false; } }) ], ), ) : const SizedBox.shrink(); }, ), if (file != null) Container( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 18), margin: const EdgeInsets.all(16).copyWith(bottom: 0), decoration: BoxDecoration( color: AppColors.primaryColor.defaultShade, borderRadius: BorderRadius.circular(16) .copyWith(topRight: Radius.zero)), child: file.isAudio() ? Player( fileUrl: file.path, inMessages: true, ) : Row( children: [ const SizedBox( width: 8, ), Expanded( child: Text( file.name, style: AppTextStyles.body4.copyWith( color: Theme.of(context) .colorScheme .onSurface), )), const SizedBox( width: 8, ), SizedBox( width: 46, child: AspectRatio( aspectRatio: 3 / 4, child: ClipRRect( borderRadius: BorderRadius.circular(10), child: file.isImage() ? GestureDetector( onTap: () => DialogHandler( context: context) .showImageHero( image: file .path), child: SizedBox( child: CustomeImage( src: file.path, fit: BoxFit.cover, )), ) : Container( color: Colors.white, child: const Icon( CupertinoIcons.doc)), ), ), ), ], ), ), Directionality( textDirection: TextDirection.rtl, child: Padding( padding: const EdgeInsets.all(16.0), child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Padding( padding: const EdgeInsets.only(bottom: 8.0), child: // ValueListenableBuilder( // valueListenable: message, // builder: (context, val, _) { // if (val.text.isEmpty) { // return CircleIconBtn( // icon: Assets // .icon.outline.microphoneChat, // color: Colors.white, // onTap: () { // visibleRecorder.value = true; // }, // ); // } // return ValueListenableBuilder( valueListenable: message, builder: (context, controller, _) { return controller .text.isEmpty && file == null ? CircleIconBtn( icon: Assets .icon .outline .microphoneChat, color: Theme.of(context) .colorScheme .primary, iconColor: Colors.white, onTap: () async { visibleRecorder .value = true; }) : CircleIconBtn( icon: Assets .icon.bold.send, color: Colors.white, onTap: () async { //SEND MESSAGE context .read< SendTicketBloc>() .add(SendTicket( text: message .text, file: selectedFile .value)); message.clear(); selectedFile.value = null; }, ); }) // ; // }), ), const SizedBox( width: 4, ), Expanded( child: ValueListenableBuilder( valueListenable: message, builder: (context, val, _) { return Directionality( textDirection: val.text.startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, child: TextField( controller: message, onChanged: (value) {}, // enabled: , minLines: 1, style: AppTextStyles.body4 .copyWith( color: Theme.of(context) .colorScheme .onSurface), maxLines: 6, // Set this keyboardType: TextInputType.multiline, decoration: InputDecoration( filled: true, hintText: 'بنویسید یا پیام صوتی بگذارید...', hintStyle: AppTextStyles.body4, fillColor: Theme.of(context) .scaffoldBackgroundColor, contentPadding: const EdgeInsets .fromLTRB( 18, 12, 18, 12), border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular( 24), ), ), ), ); })), const SizedBox( width: 4, ), Padding( padding: const EdgeInsets.only(bottom: 8.0), child: CircleIconBtn( icon: file != null ? Assets.icon.outline.trash : Assets.icon.outline.elementPlus, color: Theme.of(context) .colorScheme .primary, iconColor: Colors.white, onTap: () { if (file != null) { selectedFile.value = null; return; } visibleAttach.value = !visibleAttach.value; }, ), ), /* Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Row( children: [ ValueListenableBuilder( valueListenable: visibleAttach, builder: (context, isVisible, _) { return AnimatedVisibility( isVisible: isVisible, duration: const Duration( milliseconds: 200), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 4.0), child: Row(children: [ CircleIconBtn( icon: Assets.icon.outline .galleryAdd, color: Colors.white, onTap: () async { await BottomSheetHandler( context) .showPickImage( onSelect: (file) { selectedFile.value = file; }, ); }), Padding( padding: const EdgeInsets.only( right: 4.0), child: CircleIconBtn( icon: Assets.icon.outline .musicnote, color: Colors.white, onTap: () async { final file = await PickFileService .getFile( fileType: FileType .audio); if (file != null) { selectedFile.value = file.single; } }, ), ), Padding( padding: const EdgeInsets.only( right: 4.0), child: CircleIconBtn( icon: Assets.icon .outline.cardAdd, color: Colors.white, onTap: () async { final file = await PickFileService.getFile( fileType: FileType .custom, allowedExtensions: [ 'pdf' ]); if (file != null) { selectedFile.value = file.single; } }), ), ]), )); }), CircleIconBtn( icon: Assets.icon.outline.elementPlus, color: Colors.white, onTap: () => visibleAttach.value = !visibleAttach.value, ), ], ), )*/ ], )), ) ], ); }, ); }), ); } Widget chatBubble(Ticket ticket, double maxWidthDesktop, String date) { final GlobalKey containerKey = GlobalKey(); final isUser = ticket.role == 'user'; XFile? localFile = ticket.localFile; String? fileUrl = ticket.file; return GestureDetector( onLongPress: () { MorePopupMenuHandler(context: context).showMorePopupMenu( containerKey: containerKey, color: isUser ? AppColors.primaryColor.defaultShade : Theme.of(context).colorScheme.surface, items: [ if (isUser) PopUpMenuItemModel( popupMenuItem: PopupMenuItem( value: 0, child: MorePopupMenuHandler.morePopUpItem( color: isUser ? Colors.white : Theme.of(context).colorScheme.primary, icon: Assets.icon.outline.trash, title: 'حذف')), click: () { try { context .read() .add(DeleteTicket(id: ticket.id!, date: date)); } catch (e) { if (kDebugMode) { print("Error when delete message: $e"); } } }, ), if (ticket.text != null && ticket.text!.isNotEmpty) PopUpMenuItemModel( popupMenuItem: PopupMenuItem( value: 1, child: MorePopupMenuHandler.morePopUpItem( color: isUser ? Colors.white : Theme.of(context).colorScheme.primary, icon: Assets.icon.outline.copy, title: 'کپی', ), ), click: () async { await Clipboard.setData( ClipboardData(text: ticket.text ?? '')); Future.delayed( Duration.zero, () => SnackBarManager(context, id: 'Copy').show( status: SnackBarStatus.info, message: 'متن کپی شد 😃', isTop: false)); }, ), // PopUpMenuItemModel( // popupMenuItem: PopupMenuItem( // value: 1, // child: MorePopupMenuHandler.morePopUpItem( // icon: Assets.icon.outline.trash, // title: 'حذف', // ), // ), // click: () async { // await DialogHandler(context: context).showDeleteItem( // title: 'پیام مورد نظر پاک شود؟', // description: '.با این کار اطلاعات شما از بین خواهد رفت', // onConfirm: () async {}, // ); // }, // ) ]); }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( key: containerKey, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: ticket.id == -2 ? AppColors.red[50] : isUser ? AppColors.primaryColor.defaultShade : Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16).copyWith( bottomLeft: isUser ? const Radius.circular(16) : const Radius.circular(0), bottomRight: isUser ? const Radius.circular(0) : const Radius.circular(16)), ), constraints: BoxConstraints( maxWidth: MediaQuery.sizeOf(context).width * 0.8), child: Directionality( textDirection: (ticket.text ?? '').startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, child: Column( children: [ localFile != null ? Container( margin: const EdgeInsets.only(bottom: 8), child: localFile.name.isImage() ? GestureDetector( onTap: () => DialogHandler(context: context) .showImageHero(image: localFile.path), child: Container( constraints: BoxConstraints( maxWidth: Responsive(context).isMobile() ? MediaQuery.sizeOf(context) .width * 0.5 : maxWidthDesktop * 0.3, maxHeight: Responsive(context).isMobile() ? MediaQuery.sizeOf(context) .width * 0.6 : maxWidthDesktop * 0.4), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CustomeImage( src: localFile.path, fit: BoxFit.cover, )), ), ) : localFile.name.isAudio() ? Player( fileUrl: localFile.path, inMessages: true, ) : Container( decoration: BoxDecoration( color: AppColors.gray.defaultShade, borderRadius: BorderRadius.circular(10)), padding: const EdgeInsets.all(8), constraints: const BoxConstraints(minHeight: 64), child: Row( children: [ SizedBox( child: localFile.name.isDocument() ? const Icon( CupertinoIcons.doc) : const SizedBox.shrink(), ), Expanded( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 12.0), child: Text( localFile.name, textDirection: localFile.name .startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, style: const TextStyle( fontSize: 16), overflow: TextOverflow.ellipsis, maxLines: 2, ), )), ], ), ), ) : fileUrl != null ? Container( margin: const EdgeInsets.only(bottom: 8), child: fileUrl.isImage() ? GestureDetector( onTap: () => DialogHandler(context: context) .showImageHero( image: fileUrl, isUrl: true), child: Container( constraints: BoxConstraints( maxWidth: Responsive(context).isMobile() ? maxWidthDesktop * 0.5 : maxWidthDesktop * 0.3, maxHeight: Responsive(context).isMobile() ? maxWidthDesktop * 0.6 : maxWidthDesktop * 0.4), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: ImageNetwork( url: fileUrl, fit: BoxFit.cover, )), ), ) : fileUrl.isAudio() ? Player( fileUrl: fileUrl, inMessages: true, ) : Container( decoration: BoxDecoration( color: AppColors.gray.defaultShade, borderRadius: BorderRadius.circular(10)), padding: const EdgeInsets.all(8), constraints: const BoxConstraints( minHeight: 64), child: Row( children: [ SizedBox( child: fileUrl.isDocument() ? const Icon( CupertinoIcons.doc) : const SizedBox.shrink(), ), Expanded( child: Padding( padding: const EdgeInsets .symmetric( horizontal: 12.0), child: Text( fileUrl.split('/').last, textDirection: fileUrl .split('/') .last .startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, style: const TextStyle( fontSize: 16), overflow: TextOverflow.ellipsis, maxLines: 2, ), )), ], ), ), ) : const SizedBox.shrink(), Text( (ticket.text ?? ''), style: AppTextStyles.body4.copyWith( color: isUser ? Colors.white : Theme.of(context).colorScheme.onSurface), ), ], ), ), ), if (ticket.createdAt != null) Padding( padding: const EdgeInsets.fromLTRB(8, 4, 0, 0), child: ticket.id == -1 ? const SizedBox( width: 12, height: 12, child: CircularProgressIndicator()) : Text( DateTimeUtils.convertToSentTime(ticket.createdAt!), style: AppTextStyles.body5 .copyWith(color: AppColors.gray[700]), ), ) ], ), ), ); } }