diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart index f2361ff..1425866 100644 --- a/lib/utils/action_sheet.dart +++ b/lib/utils/action_sheet.dart @@ -16,6 +16,7 @@ import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class ActionSheetUtils { final BuildContext context; @@ -286,118 +287,83 @@ class ActionSheetUtils { ); } - Future botsDialogSelect( - {required final BuildContext context, - required final HistoryAiChatState state}) async { + Future botsDialogSelect({ + required final BuildContext context, + }) async { ActionSheetUtils(context).openDialog( data: ActionSheetData( hasConfirmButton: false, hasDismissButton: false, - content: Column( - children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Padding( - // padding: const EdgeInsets.symmetric(vertical: 8.0), - // child: InkWell( - // onTap: () { - // ActionSheetUtils.pop(); - // }, - // child: const Icon(DidvanIcons.close_solid)), - // ) - // ], - // ), - // SearchField( - // title: 'هوش مصنوعی', - // value: state.search, - // onChanged: (value) { - // state.search = value; - // if (value.isEmpty) { - // state.getBots(); - // return; - // } - // state.timer?.cancel(); - // state.timer = Timer(const Duration(seconds: 1), () { - // state.getSearchBots(value); - // }); - // }, - // focusNode: FocusNode()), - // const SizedBox( - // height: 12, - // ), - SizedBox( - width: double.infinity, - height: MediaQuery.sizeOf(context).height / 3, - child: ValueListenableBuilder( - valueListenable: state.loadingBots, - builder: (context, value, child) => value - ? Center( - child: Image.asset( - Assets.loadingAnimation, - width: 60, - height: 60, + content: SizedBox( + width: double.infinity, + height: MediaQuery.sizeOf(context).height / 3, + child: Consumer( + builder: (context, state, child) { + return state.loadingBots + ? Center( + child: Image.asset( + Assets.loadingAnimation, + width: 60, + height: 60, + ), + ) + : state.bots.isEmpty + ? Padding( + padding: + const EdgeInsets.symmetric(horizontal: 12.0), + child: EmptyState( + asset: Assets.emptyResult, + title: 'نتیجه‌ای پیدا نشد', + height: 120, ), ) - : state.bots.isEmpty - ? Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12.0), - child: EmptyState( - asset: Assets.emptyResult, - title: 'نتیجه‌ای پیدا نشد', - height: 120, - ), - ) - : ListView.builder( - itemCount: state.bots.length, - physics: const BouncingScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - final bot = state.bots[index]; - return InkWell( - onTap: () { - ActionSheetUtils(context).pop(); - state.bot = bot; - state.update(); - }, - child: Container( - alignment: Alignment.center, - padding: const EdgeInsets.symmetric( - vertical: 8), - decoration: BoxDecoration( - border: index == state.bots.length - 1 - ? null - : Border( - bottom: BorderSide( - color: Theme.of(context) - .colorScheme - .border, - width: 1))), - child: Row( - children: [ - SkeletonImage( - imageUrl: bot.image.toString(), - width: 42, - height: 42, - borderRadius: - BorderRadius.circular(360), - ), - const SizedBox(width: 12), - Expanded( - child: DidvanText( - bot.name.toString(), - maxLines: 1, - overflow: TextOverflow.ellipsis, - )) - ], + : ListView.builder( + itemCount: state.bots.length, + physics: const BouncingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + final bot = state.bots[index]; + return InkWell( + onTap: () { + ActionSheetUtils(context).pop(); + state.bot = bot; + state.update(); + }, + child: Container( + alignment: Alignment.center, + padding: + const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + border: index == state.bots.length - 1 + ? null + : Border( + bottom: BorderSide( + color: Theme.of(context) + .colorScheme + .border, + width: 1))), + child: Row( + children: [ + SkeletonImage( + imageUrl: bot.image.toString(), + width: 42, + height: 42, + borderRadius: + BorderRadius.circular(360), ), - ), - ); - }), - ), - ) - ], + const SizedBox(width: 12), + Expanded( + child: DidvanText( + bot.name.toString(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + )) + ], + ), + ), + ); + }); + }), ))); } diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart index 7c68706..ffafe21 100644 --- a/lib/views/ai/ai.dart +++ b/lib/views/ai/ai.dart @@ -34,9 +34,9 @@ class _AiState extends State { Future.delayed( Duration.zero, () { - if (context.read().refresh) { - context.read().getChats(); - context.read().refresh = false; + if (state.refresh) { + state.getBots(); + state.refresh = false; } state.getBots(); }, @@ -85,8 +85,7 @@ class _AiState extends State { ), InkWell( onTap: () => ActionSheetUtils(context) - .botsDialogSelect( - context: context, state: state), + .botsDialogSelect(context: context), child: Container( decoration: BoxDecoration( borderRadius: @@ -287,26 +286,29 @@ class _AiState extends State { } }, ), - Positioned( - top: 32, - right: 0, - child: InkWell( - onTap: () => homeScaffKey.currentState!.openDrawer(), - child: Container( - width: 46, - height: 46, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(12), - bottomLeft: Radius.circular(12)), - boxShadow: DesignConfig.defaultShadow), - child: Icon( - DidvanIcons.angle_left_light, - color: Theme.of(context).colorScheme.title, - ), - )), - ) + Consumer(builder: (context, state, child) { + if (state.bots.isEmpty) return const SizedBox(); + return Positioned( + top: 32, + right: 0, + child: InkWell( + onTap: () => homeScaffKey.currentState!.openDrawer(), + child: Container( + width: 46, + height: 46, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(12), + bottomLeft: Radius.circular(12)), + boxShadow: DesignConfig.defaultShadow), + child: Icon( + DidvanIcons.angle_left_light, + color: Theme.of(context).colorScheme.title, + ), + )), + ); + }) ], ); } diff --git a/lib/views/ai/create_bot_assistants_page.dart b/lib/views/ai/create_bot_assistants_page.dart index 7ff6264..898e3b7 100644 --- a/lib/views/ai/create_bot_assistants_page.dart +++ b/lib/views/ai/create_bot_assistants_page.dart @@ -63,7 +63,7 @@ class _CreateBotAssistantsPageState extends State { super.initState(); } - void onConfirm(CreateBotAssistantsState state, int selectedBotId) async { + void onConfirm(CreateBotAssistantsState state) async { bool isValid = true; // if (!_formYouTubeKey.currentState!.validate()) { // isValid = false; @@ -95,7 +95,7 @@ class _CreateBotAssistantsPageState extends State { type: state.selectedBotType, name: state.name, description: state.desc, - botId: selectedBotId, + botId: state.initialBot!.id!, prompt: state.prompt, webLinks: state.countOfLink.first.isEmpty ? null : state.countOfLink, @@ -135,9 +135,6 @@ class _CreateBotAssistantsPageState extends State { ), body: Consumer(builder: (BuildContext context, CreateBotAssistantsState state, Widget? child) { - int selectedBotId = state.selectedBotType == 'text' - ? state.allBots.first.id! - : state.imageBots.first.id!; return state.loading ? Center( child: SpinKitThreeBounce( @@ -400,7 +397,7 @@ class _CreateBotAssistantsPageState extends State { .surface), // hintText: "انتخاب کنید", onChanged: (value) { - selectedBotId = value!.id!; + state.initialBot = value; }, ), ), @@ -804,7 +801,7 @@ class _CreateBotAssistantsPageState extends State { ? ' ' : 'ذخیره تغییرات', onPressed: () async => - onConfirm(state, selectedBotId), + onConfirm(state), ), if (state.loadingCreate) const Positioned.fill( @@ -871,8 +868,7 @@ class _CreateBotAssistantsPageState extends State { DidvanButton( title: state.loadingCreate ? ' ' : 'ذخیره', - onPressed: () async => - onConfirm(state, selectedBotId)), + onPressed: () async => onConfirm(state)), if (state.loadingCreate) const Positioned.fill( child: Center( diff --git a/lib/views/ai/create_bot_assistants_state.dart b/lib/views/ai/create_bot_assistants_state.dart index 6c9508a..42d1e86 100644 --- a/lib/views/ai/create_bot_assistants_state.dart +++ b/lib/views/ai/create_bot_assistants_state.dart @@ -29,7 +29,7 @@ class CreateBotAssistantsState extends CoreProvier { navigatorKey.currentContext!.read().bots; final List botModels = ['مدل زبانی', 'مدل تصویری']; - BotsModel? initialBot; + late BotsModel? initialBot = allBots.first; List countOfLink = ['']; List files = []; @@ -123,6 +123,9 @@ class CreateBotAssistantsState extends CoreProvier { } selectedBotType = initialBot?.responseType ?? 'text'; } + if (initialBot == null) { + selectedBotType == 'text' ? allBots.first : imageBots.first; + } appState = AppState.idle; loading = false; update(); diff --git a/lib/views/ai/history_ai_chat_state.dart b/lib/views/ai/history_ai_chat_state.dart index 1c30287..552c3f4 100644 --- a/lib/views/ai/history_ai_chat_state.dart +++ b/lib/views/ai/history_ai_chat_state.dart @@ -9,7 +9,6 @@ import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/utils/action_sheet.dart'; -import 'package:flutter/cupertino.dart'; class HistoryAiChatState extends CoreProvier { final List chats = []; @@ -17,15 +16,17 @@ class HistoryAiChatState extends CoreProvier { // final List chatsToDelete = []; final List bots = []; BotsModel? bot; - ValueNotifier loadingBots = ValueNotifier(false); + bool loadingBots = false; bool loadingchangeTitle = false; bool loadingdeleteAll = false; + bool loadinggetAll = false; bool refresh = false; Timer? timer; String search = ''; Future getChats({final bool archived = false}) async { appState = AppState.busy; + loadinggetAll = true; update(); final service = RequestService( archived ? RequestHelper.aiArchived() : RequestHelper.aiChats(), @@ -39,10 +40,13 @@ class HistoryAiChatState extends CoreProvier { .add(ChatsModel.fromJson(messages[i])); } appState = AppState.idle; + loadinggetAll = false; + update(); return; } appState = AppState.failed; + loadinggetAll = false; update(); } @@ -72,7 +76,9 @@ class HistoryAiChatState extends CoreProvier { } Future getBots() async { - loadingBots.value = true; + appState = AppState.busy; + loadingBots = true; + update(); final service = RequestService( RequestHelper.aiBots(), ); @@ -83,19 +89,23 @@ class HistoryAiChatState extends CoreProvier { for (var i = 0; i < messages.length; i++) { bots.add(BotsModel.fromJson(messages[i])); } - appState = AppState.idle; - loadingBots.value = false; bot = bots.first; + appState = AppState.idle; + loadingBots = false; update(); + getChats(); + return; } appState = AppState.failed; - loadingBots.value = false; + loadingBots = false; update(); } Future getSearchBots(String q) async { - loadingBots.value = true; + appState = AppState.busy; + + loadingBots = true; final service = RequestService( RequestHelper.aiSearchBots(q), ); @@ -107,13 +117,13 @@ class HistoryAiChatState extends CoreProvier { bots.add(BotsModel.fromJson(messages[i])); } appState = AppState.idle; - loadingBots.value = false; + loadingBots = false; update(); return; } appState = AppState.failed; - loadingBots.value = false; + loadingBots = false; update(); } diff --git a/lib/views/ai/widgets/ai_message_bar.dart b/lib/views/ai/widgets/ai_message_bar.dart index 63c5004..85c35e3 100644 --- a/lib/views/ai/widgets/ai_message_bar.dart +++ b/lib/views/ai/widgets/ai_message_bar.dart @@ -17,7 +17,6 @@ import 'package:didvan/services/media/media.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/ai/ai_chat_state.dart'; -import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/widgets/audio_wave.dart'; import 'package:didvan/views/ai/widgets/message_bar_btn.dart'; import 'package:didvan/views/widgets/animated_visibility.dart'; @@ -66,7 +65,6 @@ class _AiMessageBarState extends State { Timer? _timer; final theSource = AudioSource.microphone; final ValueNotifier _countTimer = ValueNotifier(Duration.zero); - late HistoryAiChatState historyState = context.read(); @override void initState() { @@ -256,7 +254,7 @@ class _AiMessageBarState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - recorderAndSendButton(state, historyState), + recorderAndSendButton(state), if (!(_mRecorder!.isStopped)) ValueListenableBuilder( valueListenable: _countTimer, @@ -453,13 +451,13 @@ class _AiMessageBarState extends State { ); } - recorderAndSendButton(AiChatState state, HistoryAiChatState historyState) { + recorderAndSendButton(AiChatState state) { return ValueListenableBuilder( valueListenable: state.message, builder: (context, message, child) => Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 8), child: message.text.isEmpty && - historyState.bot!.attachmentType!.contains('audio') && + widget.bot.attachmentType!.contains('audio') && state.file == null && widget.bot.attachment != 0 ? MessageBarBtn( @@ -531,150 +529,6 @@ class _AiMessageBarState extends State { ); } - // AnimatedVisibility attachmentLayout(AiChatState state, - // HistoryAiChatState historyState, BuildContext context) { - // return AnimatedVisibility( - // isVisible: openAttach, - // duration: DesignConfig.lowAnimationDuration, - // child: Container( - // padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 12), - // child: state.file != null && !(state.file!.isRecorded) - // ? fileContainer() - // : Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // if (historyState.bot!.attachmentType!.contains('pdf')) - // attachBtn( - // title: "PDF", - // icon: CupertinoIcons.doc_fill, - // color: Colors.redAccent, - // click: () async { - // MediaService.onLoadingPickFile(context); - // FilePickerResult? result = - // await MediaService.pickPdfFile(); - // if (result != null) { - // if (kIsWeb) { - // Uint8List? bytes = result.files.first - // .bytes; // Access the bytes property - // String? name = result.files.first.name; - - // // Store bytes and file name directly in your state or model - // state.file = FilesModel( - // '', // No need for a file path on web - // name: name, - // bytes: bytes, - // audio: false, - // image: false, - // ); - // } else { - // state.file = FilesModel(result.files.single.path!, - // audio: false, image: false); - // } - // } - // Future.delayed( - // Duration.zero, - // () => ActionSheetUtils(context).pop(), - // ); - // }, - // ), - // if (historyState.bot!.attachmentType!.contains('image')) - // attachBtn( - // title: "تصویر", - // icon: CupertinoIcons.photo, - // color: Colors.deepOrangeAccent, - // click: () async { - // MediaService.onLoadingPickFile(context); - - // final pickedFile = await MediaService.pickImage( - // source: ImageSource.gallery); - // File? file; - // if (pickedFile != null && !kIsWeb) { - // file = await ImageCropper().cropImage( - // sourcePath: pickedFile.path, - // androidUiSettings: const AndroidUiSettings( - // toolbarTitle: 'برش تصویر'), - // iosUiSettings: const IOSUiSettings( - // title: 'برش تصویر', - // doneButtonTitle: 'تایید', - // cancelButtonTitle: 'بازگشت', - // ), - // compressQuality: 30, - // ); - - // if (file == null) { - // await Future.delayed( - // Duration.zero, - // () => ActionSheetUtils(context).pop(), - // ); - - // return; - // } - // } - // if (pickedFile == null) { - // await Future.delayed( - // Duration.zero, - // () => ActionSheetUtils(context).pop(), - // ); - - // return; - // } - // state.file = kIsWeb - // ? FilesModel(pickedFile.path, - // name: pickedFile.name, - // image: true, - // audio: false) - // : FilesModel(file!.path, - // image: true, audio: false); - // await Future.delayed( - // Duration.zero, - // () => ActionSheetUtils(context).pop(), - // ); - // }, - // ), - // // if (!kIsWeb && !Platform.isIOS) - // if (historyState.bot!.attachmentType!.contains('audio')) - // attachBtn( - // title: "صوت", - // icon: CupertinoIcons.music_note_2, - // color: Colors.indigoAccent, - // click: () async { - // MediaService.onLoadingPickFile(context); - - // FilePickerResult? result = - // await MediaService.pickAudioFile(); - // if (result != null) { - // if (kIsWeb) { - // Uint8List? bytes = result.files.first - // .bytes; // Access the bytes property - // String? name = result.files.first.name; - - // // final blob = html.Blob([bytes]); - // // final blobUrl = - // // html.Url.createObjectUrlFromBlob(blob); - - // state.file = FilesModel( - // "", // No need for a file path on web - // name: name, - // bytes: bytes, - // audio: true, - // image: false, - // ); - // } else { - // state.file = FilesModel(result.files.single.path!, - // audio: true, image: false); - // } - // } - // await Future.delayed( - // Duration.zero, - // () => ActionSheetUtils(context).pop(), - // ); - // }, - // ) - // ], - // ), - // )); - // } - AnimatedVisibility attachmentLayout(AiChatState state, BuildContext context) { return AnimatedVisibility( isVisible: openAttach, @@ -683,45 +537,48 @@ class _AiMessageBarState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (historyState.bot!.attachmentType!.contains('pdf')) - MessageBarBtn( - enable: true, - icon: CupertinoIcons.doc_fill, - click: () async { - MediaService.onLoadingPickFile(context); - FilePickerResult? result = await MediaService.pickPdfFile(); - if (result != null) { - String? name = result.files.single.name; - - if (kIsWeb) { - Uint8List? bytes = - result.files.first.bytes; // Access the bytes property - - // Store bytes and file name directly in your state or model - state.file = FilesModel( - '', // No need for a file path on web - name: name, - bytes: bytes, - audio: false, - image: false, - ); - } else { - state.file = FilesModel(result.files.single.path!, - audio: false, image: false, name: name); - } - } - Future.delayed( - Duration.zero, - () => ActionSheetUtils(context).pop(), - ); - openAttach = !openAttach; - - state.update(); - }, - ), - if (historyState.bot!.attachmentType!.contains('image')) + if (widget.bot.attachmentType!.contains('pdf')) Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), + padding: const EdgeInsets.only(right: 8.0), + child: MessageBarBtn( + enable: true, + icon: CupertinoIcons.doc_fill, + click: () async { + MediaService.onLoadingPickFile(context); + FilePickerResult? result = await MediaService.pickPdfFile(); + if (result != null) { + String? name = result.files.single.name; + + if (kIsWeb) { + Uint8List? bytes = result + .files.first.bytes; // Access the bytes property + + // Store bytes and file name directly in your state or model + state.file = FilesModel( + '', // No need for a file path on web + name: name, + bytes: bytes, + audio: false, + image: false, + ); + } else { + state.file = FilesModel(result.files.single.path!, + audio: false, image: false, name: name); + } + } + Future.delayed( + Duration.zero, + () => ActionSheetUtils(context).pop(), + ); + openAttach = !openAttach; + + state.update(); + }, + ), + ), + if (widget.bot.attachmentType!.contains('image')) + Padding( + padding: const EdgeInsets.only(right: 8.0), child: MessageBarBtn( enable: true, icon: CupertinoIcons.photo, @@ -777,86 +634,53 @@ class _AiMessageBarState extends State { ), ), // if (!kIsWeb && !Platform.isIOS) - if (historyState.bot!.attachmentType!.contains('audio')) - MessageBarBtn( - enable: true, - icon: CupertinoIcons.music_note_2, - click: () async { - MediaService.onLoadingPickFile(context); + if (widget.bot.attachmentType!.contains('audio')) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: MessageBarBtn( + enable: true, + icon: CupertinoIcons.music_note_2, + click: () async { + MediaService.onLoadingPickFile(context); - FilePickerResult? result = await MediaService.pickAudioFile(); - if (result != null) { - String? name = result.files.first.name; + FilePickerResult? result = + await MediaService.pickAudioFile(); + if (result != null) { + String? name = result.files.first.name; - if (kIsWeb) { - Uint8List? bytes = - result.files.first.bytes; // Access the bytes property + if (kIsWeb) { + Uint8List? bytes = result + .files.first.bytes; // Access the bytes property - final blob = html.Blob([bytes]); - final blobUrl = html.Url.createObjectUrlFromBlob(blob); + final blob = html.Blob([bytes]); + final blobUrl = html.Url.createObjectUrlFromBlob(blob); - state.file = FilesModel( - blobUrl, // No need for a file path on web - name: name, - bytes: bytes, - audio: true, - image: false, - ); - } else { - state.file = FilesModel(result.files.single.path!, - name: name, audio: true, image: false); + state.file = FilesModel( + blobUrl, // No need for a file path on web + name: name, + bytes: bytes, + audio: true, + image: false, + ); + } else { + state.file = FilesModel(result.files.single.path!, + name: name, audio: true, image: false); + } } - } - await Future.delayed( - Duration.zero, - () => ActionSheetUtils(context).pop(), - ); - openAttach = !openAttach; + await Future.delayed( + Duration.zero, + () => ActionSheetUtils(context).pop(), + ); + openAttach = !openAttach; - state.update(); - }, + state.update(); + }, + ), ) ], )); } - // InkWell attachBtn( - // {required final String title, - // required final IconData icon, - // final Color? color, - // final Function()? click}) { - // final state = context.read(); - // return InkWell( - // onTap: () async { - // await click?.call(); - - // state.update(); - // }, - // child: Column( - // children: [ - // Container( - // width: 40, - // height: 40, - // decoration: BoxDecoration(shape: BoxShape.circle, color: color), - // padding: const EdgeInsets.all(0.8), - // margin: const EdgeInsets.symmetric(horizontal: 12), - // child: Icon( - // icon, - // size: 20, - // color: Theme.of(context).colorScheme.white, - // )), - // const SizedBox( - // height: 4, - // ), - // DidvanText( - // title, - // fontSize: 12, - // ) - // ], - // ), - // ); - // } - Widget audioContainer() { final state = context.watch(); diff --git a/lib/views/ai/widgets/hoshan_drawer.dart b/lib/views/ai/widgets/hoshan_drawer.dart index 6849ad7..d98db07 100644 --- a/lib/views/ai/widgets/hoshan_drawer.dart +++ b/lib/views/ai/widgets/hoshan_drawer.dart @@ -1,6 +1,5 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/constants/assets.dart'; import 'package:didvan/main.dart'; import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/chats_model.dart'; @@ -15,8 +14,9 @@ import 'package:didvan/views/widgets/didvan/divider.dart'; 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:didvan/views/widgets/state_handlers/empty_list.dart'; +import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; class HoshanDrawer extends StatefulWidget { @@ -27,6 +27,11 @@ class HoshanDrawer extends StatefulWidget { } class _HoshanDrawerState extends State { + @override + initState() { + super.initState(); + } + @override Widget build(BuildContext context) { return Drawer( @@ -139,88 +144,28 @@ class _HoshanDrawerState extends State { // height: 12, // ), Expanded( - child: state.loadingdeleteAll || - state.appState == AppState.busy - ? ListView.builder( - shrinkWrap: true, - itemCount: 10, - padding: const EdgeInsets.symmetric( - horizontal: 12), - physics: - const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return const Padding( - padding: EdgeInsets.symmetric( - vertical: 12.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - ClipOval( - child: ShimmerPlaceholder( - height: 24, - width: 24, - ), - ), - SizedBox(width: 12), - Expanded( - child: ShimmerPlaceholder( - height: 24, - ), - ), - SizedBox(width: 12), - ShimmerPlaceholder( - height: 24, - width: 24, - ), - SizedBox(width: 8), - ShimmerPlaceholder( - height: 24, - width: 12, - ), - ], - ), - ); - }, - ) - : state.chats.isEmpty - ? Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - children: [ - SvgPicture.asset( - Assets.emptyResult, - height: - MediaQuery.sizeOf(context) - .height / - 10, - ), - const DidvanText( - 'لیست خالی است', - fontSize: 14, - fontWeight: FontWeight.bold, - ) - ], - ), - ) - : ListView.builder( - shrinkWrap: true, - itemCount: state.chats.length, - padding: const EdgeInsets.symmetric( - horizontal: 12), - physics: - const BouncingScrollPhysics(), - itemBuilder: (context, index) { - final chat = state.chats[index]; - TextEditingController title = - TextEditingController( - text: chat.title); + child: CustomScrollView( + slivers: [ + SliverStateHandler( + state: state, + centerEmptyState: true, + emptyState: const EmptyList(), + // enableEmptyState: state.chats.isEmpty, + placeholder: chatRowPlaceholder(), + placeholderCount: 10, + builder: (context, state, index) { + final chat = state.chats[index]; + TextEditingController title = + TextEditingController( + text: chat.title); + + return chatRow(chat, title, state, index); + }, + childCount: state.chats.length, + onRetry: () => state.getChats()) + ], + )), - return chatRow( - chat, title, state, index); - }, - ), - ), // SizedBox( // height: 12, // ), @@ -268,6 +213,39 @@ class _HoshanDrawerState extends State { ); } + Padding chatRowPlaceholder() { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 12.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipOval( + child: ShimmerPlaceholder( + height: 24, + width: 24, + ), + ), + SizedBox(width: 12), + Expanded( + child: ShimmerPlaceholder( + height: 24, + ), + ), + SizedBox(width: 12), + ShimmerPlaceholder( + height: 24, + width: 24, + ), + SizedBox(width: 8), + ShimmerPlaceholder( + height: 24, + width: 12, + ), + ], + ), + ); + } + Widget drawerBtn( {final CrossAxisAlignment? crossAxisAlignment, required final IconData icon, diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart index e0095dd..6f9cf38 100644 --- a/lib/views/home/home.dart +++ b/lib/views/home/home.dart @@ -142,7 +142,6 @@ class _HomeState extends State if (_tabController.index == 2) { homeScaffKey.currentState!.closeDrawer(); - context.read().getChats(); context.read().getBots(); } }); diff --git a/lib/views/widgets/didvan/text_field.dart b/lib/views/widgets/didvan/text_field.dart index e10e459..9a8cc91 100644 --- a/lib/views/widgets/didvan/text_field.dart +++ b/lib/views/widgets/didvan/text_field.dart @@ -1,18 +1,18 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/utils/extension.dart'; import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:persian_number_utility/persian_number_utility.dart'; class DidvanTextField extends StatefulWidget { final void Function(String value)? onChanged; final void Function(String value)? onSubmitted; final bool enabled; final bool autoFocus; - final TextAlign textAlign; + final TextAlign? textAlign; final String? title; final String? hintText; final dynamic initialValue; @@ -37,7 +37,7 @@ class DidvanTextField extends StatefulWidget { this.initialValue, this.validator, this.textInputType, - this.textAlign = TextAlign.right, + this.textAlign, this.obsecureText = false, this.autoFocus = false, this.onSubmitted, @@ -69,7 +69,7 @@ class _DidvanTextFieldState extends State { } _hideContent = widget.obsecureText; _focusNode.addListener(() { - setState(() {}); + // setState(() {}); }); super.initState(); } @@ -98,47 +98,58 @@ class _DidvanTextFieldState extends State { ? null : Border.all(color: _borderColor()), ), - child: TextFormField( - inputFormatters: [ - if (!widget.acceptSpace) - FilteringTextInputFormatter.allow(RegExp("[0-9a-zA-Z]")), - ], - autofocus: widget.autoFocus, - obscureText: _hideContent, - textAlign: widget.textAlign, - keyboardType: widget.textInputType, - focusNode: _focusNode, - controller: _controller, - onFieldSubmitted: widget.onSubmitted, - onChanged: _onChanged, - validator: _validator, - maxLines: widget.maxLine, - minLines: widget.minLine, - maxLength: widget.maxLength, - obscuringCharacter: '*', - buildCounter: widget.showLen - ? null - : (context, - {required currentLength, - required isFocused, - required maxLength}) => - const SizedBox(), - style: (widget.isSmall - ? Theme.of(context).textTheme.bodySmall! - : Theme.of(context).textTheme.bodyMedium!) - .copyWith(fontFamily: DesignConfig.fontFamily.padRight(3)), - decoration: InputDecoration( - suffixIcon: _suffixBuilder(), - enabled: widget.enabled, - border: InputBorder.none, - hintText: widget.hintText, - errorStyle: const TextStyle(height: 0.01), - hintStyle: (widget.isSmall - ? Theme.of(context).textTheme.bodySmall! - : Theme.of(context).textTheme.bodyMedium!) - .copyWith(color: Theme.of(context).colorScheme.hint), - ), - ), + child: ValueListenableBuilder( + valueListenable: _controller, + builder: (context, val, _) { + return Directionality( + textDirection: val.text.startsWithEnglish() + ? TextDirection.ltr + : TextDirection.rtl, + child: TextFormField( + inputFormatters: [ + if (!widget.acceptSpace) + FilteringTextInputFormatter.allow( + RegExp("[0-9a-zA-Z]")), + ], + autofocus: widget.autoFocus, + obscureText: _hideContent, + textAlign: widget.textAlign ?? TextAlign.start, + keyboardType: widget.textInputType, + focusNode: _focusNode, + controller: _controller, + onFieldSubmitted: widget.onSubmitted, + onChanged: _onChanged, + validator: _validator, + maxLines: widget.maxLine, + minLines: widget.minLine, + maxLength: widget.maxLength, + obscuringCharacter: '*', + buildCounter: widget.showLen + ? null + : (context, + {required currentLength, + required isFocused, + required maxLength}) => + const SizedBox(), + style: (widget.isSmall + ? Theme.of(context).textTheme.bodySmall! + : Theme.of(context).textTheme.bodyMedium!) + .copyWith( + fontFamily: DesignConfig.fontFamily.padRight(3)), + decoration: InputDecoration( + suffixIcon: _suffixBuilder(), + enabled: widget.enabled, + border: InputBorder.none, + hintText: widget.hintText, + errorStyle: const TextStyle(height: 0.01), + hintStyle: (widget.isSmall + ? Theme.of(context).textTheme.bodySmall! + : Theme.of(context).textTheme.bodyMedium!) + .copyWith(color: Theme.of(context).colorScheme.hint), + ), + ), + ); + }), ), AnimatedVisibility( isVisible: _error != null, @@ -224,7 +235,7 @@ class _DidvanTextFieldState extends State { setState(() { _error = null; }); - value = value.toEnglishDigit(); + // value = value.toEnglishDigit(); widget.onChanged?.call(value); }