diff --git a/lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot-remix.svg b/lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot-remix.svg new file mode 100644 index 0000000..6457772 --- /dev/null +++ b/lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot-remix.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot.svg b/lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot.svg new file mode 100644 index 0000000..ae6a189 --- /dev/null +++ b/lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/main.dart b/lib/main.dart index 0d6b045..7f4effe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/notification/firebase_api.dart'; import 'package:didvan/services/notification/notification_service.dart'; import 'package:didvan/utils/my_custom_scroll_behavior.dart'; +import 'package:didvan/views/ai/ai_chat_state.dart'; import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/ai/bot_assistants_state.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; @@ -35,24 +36,6 @@ import 'package:sentry_flutter/sentry_flutter.dart'; final GlobalKey navigatorKey = GlobalKey(); -// @pragma('vm:entry-point') -// Future _initPushNotification(RemoteMessage message) async { -// if (!kIsWeb) { -// await NotificationService.startListeningNotificationEvents(); -// } -// await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); -// // if (kDebugMode) { -// print("background: ${NotificationData.fromJson(message.data).toJson()}"); -// // } -// try { -// NotificationService.showFirebaseNotification(message); -// } catch (e) { -// // if (kDebugMode) { -// print(e); -// // } -// } -// } - @pragma('vm:entry-point') Future _backgroundCallbackHomeWidget(Uri? uri) async { await HomeWidget.saveWidgetData("uri", uri!.host); @@ -76,9 +59,9 @@ void main() async { if (Platform.isAndroid) { await FlutterDownloader.initialize( debug: - true, // optional: set to false to disable printing logs to console (default: true) + true, ignoreSsl: - true // option: set to false to disable working with http links (default: false) + true ); } } catch (e) { @@ -86,7 +69,6 @@ void main() async { } } - // FirebaseMessaging.onBackgroundMessage(_initPushNotification); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform); await FirebaseApi().initNotification(); @@ -98,11 +80,7 @@ void main() async { (options) { options.dsn = 'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048'; - // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing. - // We recommend adjusting this value in production. options.tracesSampleRate = 1.0; - // The sampling rate for profiling is relative to tracesSampleRate - // Setting to 1.0 will profile 100% of sampled transactions: options.profilesSampleRate = 1.0; }, appRunner: () => runApp(const Didvan()), @@ -124,19 +102,13 @@ class _DidvanState extends State with WidgetsBindingObserver { @override void initState() { - // if (!kIsWeb) { - // NotificationService.startListeningNotificationEvents(); - // } WidgetsBinding.instance.addObserver(this); - // NotificationService.startListeningNotificationEvents(); - super.initState(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); - // MediaService.audioPlayer.dispose(); if (MediaService.currentPodcast != null) { MediaService.audioPlayer.dispose(); } @@ -151,7 +123,7 @@ class _DidvanState extends State with WidgetsBindingObserver { if (state == AppLifecycleState.resumed) { var r = await HomeWidget.getWidgetData("cRoute", defaultValue: ''); if (r!.toString() != Routes.splash) { - await HomeWidgetRepository.decideWhereToGo(); + await HomeWidgetRepository.decideWhereToGo(); NotificationMessage? data = HomeWidgetRepository.data; @@ -188,12 +160,12 @@ class _DidvanState extends State with WidgetsBindingObserver { ChangeNotifierProvider( create: (context) => AiState(), ), + ChangeNotifierProvider( + create: (context) => AiChatState(), + ), ChangeNotifierProvider( create: (context) => BotAssistantsState(), ), - // ChangeNotifierProvider( - // create: (context) => StoryViewerState(), - // ), ], child: Consumer( builder: (context, themeProvider, child) => Container( @@ -223,7 +195,6 @@ class _DidvanState extends State with WidgetsBindingObserver { onGenerateRoute: (settings) => RouteGenerator.generateRoute(settings), builder: BotToastInit(), - //1. call BotToastInit navigatorObservers: [BotToastNavigatorObserver()], initialRoute: "/", localizationsDelegates: const [ @@ -240,4 +211,4 @@ class _DidvanState extends State with WidgetsBindingObserver { ), ); } -} +} \ No newline at end of file diff --git a/lib/models/story_model.dart b/lib/models/story_model.dart index 6d747a9..03c9f36 100644 --- a/lib/models/story_model.dart +++ b/lib/models/story_model.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; enum MediaType { image, video, gif } -// Represents a single story item (image, gif, or video) class StoryItem { final int id; final String url; @@ -38,8 +37,6 @@ class StoryItem { id: json['id'], url: json['mediaUrl'], media: mediaType, - // API doesn't provide duration for images/gifs, so we use a default. - // For videos, the player controller will determine the duration. duration: const Duration(seconds: 10), ); } @@ -57,9 +54,8 @@ class User { }); } -// This class will wrap the API response and fit it into the UI's expected structure. class UserStories { - final int id; // The main story ID from the API + final int id; final User user; final List stories; @@ -76,14 +72,13 @@ class UserStories { return UserStories( id: json['id'], user: User( - name: json['title'], // Using title directly from the API + name: json['title'], profileImageUrl: - _mapCategoryToIcon(json['category']), // Mapping category to an icon + _mapCategoryToIcon(json['category']), createdAt: json['createdAt'], ), stories: items.map((item) { final storyItem = StoryItem.fromJson(item); - // Check if this story item has been viewed. if (completedIds.contains(storyItem.id)) { storyItem.isViewed.value = true; } diff --git a/lib/services/ai/ai_api_service.dart b/lib/services/ai/ai_api_service.dart index a1631f4..0bd80bb 100644 --- a/lib/services/ai/ai_api_service.dart +++ b/lib/services/ai/ai_api_service.dart @@ -1,7 +1,6 @@ // ignore_for_file: depend_on_referenced_packages, avoid_web_libraries_in_flutter import 'dart:async'; -import 'dart:convert'; import 'package:didvan/models/ai/files_model.dart'; import 'package:didvan/services/storage/storage.dart'; diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index 6f9ffda..1f35e4c 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -20,7 +20,7 @@ class RequestHelper { static const String _baseHomeUrl = '$baseUrl/home'; static const String _baseNewStats = '$baseUrl/home/statistic'; static const String _baseNewStatsSearch = '$baseUrl/home/statistic/search'; - static const String checkHasPassword = '$_baseUserUrl/checkHasPassword'; + static const String checkHasPassword = '$_baseUserUrl/checkHasPassword'; static const String mainPageContent = _baseHomeUrl; static String searchAll({ diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart index 1425866..580bc5e 100644 --- a/lib/utils/action_sheet.dart +++ b/lib/utils/action_sheet.dart @@ -206,7 +206,9 @@ class ActionSheetUtils { data.backgroundColor ?? Theme.of(context).colorScheme.surface, ), width: mediaQueryData.size.width * 0.8, - padding: const EdgeInsets.all(24.0), + // BEGIN: EDIT THIS LINE + padding: const EdgeInsets.fromLTRB(24, 16, 24, 24), // پدینگ بالا از 24 به 16 تغییر کرد + // END: EDIT THIS LINE child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -397,8 +399,8 @@ class ActionSheetUtils { ), ), Positioned( - right: 24, - top: 24, + right: 16, + top: 0, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, @@ -446,4 +448,4 @@ class ActionSheetUtils { DesignConfig.updateSystemUiOverlayStyle(); Navigator.of(context).pop(); } -} +} \ No newline at end of file diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart index c8dd299..8e02014 100644 --- a/lib/views/ai/ai.dart +++ b/lib/views/ai/ai.dart @@ -1,375 +1,309 @@ -import 'package:didvan/config/design_config.dart'; -import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/constants/assets.dart'; -import 'package:didvan/models/ai/ai_chat_args.dart'; -import 'package:didvan/routes/routes.dart'; -import 'package:didvan/utils/action_sheet.dart'; -import 'package:didvan/views/ai/ai_state.dart'; -import 'package:didvan/views/ai/history_ai_chat_state.dart'; -import 'package:didvan/views/ai/tool_screen.dart'; -import 'package:didvan/views/ai/widgets/message_bar_btn.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_connection.dart'; -import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; + // lib/views/ai/ai.dart -class Ai extends StatefulWidget { - const Ai({Key? key}) : super(key: key); + import 'package:didvan/config/design_config.dart'; + import 'package:didvan/config/theme_data.dart'; + import 'package:didvan/constants/app_icons.dart'; + import 'package:didvan/constants/assets.dart'; + import 'package:didvan/models/ai/ai_chat_args.dart'; + import 'package:didvan/views/ai/ai_state.dart'; + import 'package:didvan/views/ai/history_ai_chat_state.dart'; + import 'package:didvan/views/ai/tool_screen.dart'; + import 'package:didvan/views/ai/widgets/message_bar_btn.dart'; + import 'package:didvan/views/widgets/didvan/text.dart'; + import 'package:didvan/views/widgets/shimmer_placeholder.dart'; + import 'package:didvan/views/widgets/state_handlers/empty_connection.dart'; + import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; + import 'package:flutter/material.dart'; + import 'package:provider/provider.dart'; - @override - // ignore: library_private_types_in_public_api - _AiState createState() => _AiState(); -} + class Ai extends StatefulWidget { + const Ai({Key? key}) : super(key: key); -class _AiState extends State { - @override - void initState() { - super.initState(); - context.read().getTools(); + @override + _AiState createState() => _AiState(); } - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Consumer( - builder: (BuildContext context, state, Widget? child) { - switch (state.page) { - case 1: - return const ToolScreen(); - case 0: - default: - return Consumer( - builder: (context, state, child) { - if (state.bots.isEmpty) { - return Center( - child: Image.asset( - Assets.loadingAnimation, - width: 60, - height: 60, - ), - ); - } - final bot = state.bot!; - return Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Expanded( - child: SingleChildScrollView( - child: Padding( - padding: - const EdgeInsets.only(bottom: 24, top: 70), - child: Column( - children: [ - Consumer( - builder: (BuildContext context, - AiState state, Widget? child) { - final tools = state.tools; - if (tools != null && tools.isEmpty) { - return EmptyState( - asset: Assets.emptyResult, - title: 'لیست خالی است', - ); - } - if (state.loading) { - return toolsPlaceHolder(); - } - if (tools == null) { - return Padding( - padding: EdgeInsets.only( - top: MediaQuery.sizeOf(context) - .height * - 0.1), - child: Center( - child: EmptyConnection( - onRetry: () { - context - .read() - .getTools(); - }, - ), - ), - ); - } + class _AiState extends State { + @override + void initState() { + super.initState(); + context.read().getTools(); + context.read().getBots(); + } - return const SizedBox(); - }, - ) - ], + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Consumer( + builder: (BuildContext context, state, Widget? child) { + switch (state.page) { + case 1: + return const ToolScreen(); + case 0: + default: + return Consumer( + builder: (context, historyState, child) { + if (historyState.bots.isEmpty) { + return Center( + child: Image.asset( + Assets.loadingAnimation, + width: 60, + height: 60, + ), + ); + } + final bot = historyState.bot!; + return Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.only(bottom: 24, top: 70), + child: Column( + children: [ + Consumer( + builder: (BuildContext context, + AiState aiState, Widget? child) { + final tools = aiState.tools; + if (tools != null && tools.isEmpty) { + return EmptyState( + asset: Assets.emptyResult, + title: 'لیست خالی است', + ); + } + if (aiState.loading) { + return toolsPlaceHolder(); + } + if (tools == null) { + return Padding( + padding: EdgeInsets.only( + top: MediaQuery.sizeOf(context) + .height * + 0.1), + child: Center( + child: EmptyConnection( + onRetry: () { + context + .read() + .getTools(); + }, + ), + ), + ); + } + + return const SizedBox(); + }, + ) + ], + ), ), ), ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 12), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - InkWell( - onTap: () { - final historyState = - context.read(); - if (historyState.bots.isEmpty) { - historyState.getBots(); - } - ActionSheetUtils(context) - .botsDialogSelect(context: context); - }, - borderRadius: BorderRadius.circular(12), - child: const SizedBox(), - // Padding( - // padding: const EdgeInsets.symmetric( - // vertical: 8.0, horizontal: 12.0), - // child: Row( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: - // CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // Icon(DidvanIcons.angle_down_light, - // size: 16, - // color: Theme.of(context) - // .colorScheme - // .title), - // const SizedBox( - // width: 8, - // ), - // DidvanText( - // bot.name.toString(), - // color: Theme.of(context) - // .colorScheme - // .title, - // style: const TextStyle( - // fontWeight: FontWeight.bold), - // ), - // const SizedBox( - // width: 12, - // ), - // SkeletonImage( - // width: 28, - // height: 28, - // imageUrl: bot.image.toString(), - // borderRadius: - // BorderRadius.circular(360), - // ), - // ], - // ), - // ), - ), - InkWell( - onTap: () => Navigator.of(context) - .pushNamed(Routes.aiChat, - arguments: AiChatArgs( - bot: bot, - )), - child: Column( - children: [ - Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .surface, - border: Border.all( - color: const Color.fromARGB(255, 0, 126, 167),width: 1.5), - borderRadius: BorderRadius.circular(50)), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets - .symmetric( - horizontal: 8.0, - ), - child: Form( - child: Row( - children: [ - const MessageBarBtn( - enable: true, - icon: DidvanIcons - .mic_regular), - const SizedBox( - width: 8, - ), - Expanded( - child: - TextFormField( - textInputAction: - TextInputAction - .newline, - style: Theme.of( - context) - .textTheme - .bodyMedium, - minLines: 1, - enabled: - false, - decoration: - InputDecoration( - border: - InputBorder - .none, - hintText: - 'بنویسید یا پیام صوتی بگذارید...', - hintStyle: Theme.of( + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 12), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + InkWell( + onTap: () => + context.read().startChat( + AiChatArgs( + bot: bot, + isTool: historyState.bots)), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .surface, + border: Border.all( + color: + const Color.fromARGB( + 255, 0, 126, 167), + width: 1.5), + borderRadius: + BorderRadius.circular( + 50)), + child: Row( + children: [ + Expanded( + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: 8.0, + ), + child: Form( + child: Row( + children: [ + const MessageBarBtn( + enable: true, + icon: DidvanIcons + .mic_regular), + const SizedBox( + width: 8, + ), + Expanded( + child: + TextFormField( + textInputAction: + TextInputAction + .newline, + style: Theme.of( context) .textTheme - .bodySmall! - .copyWith( - color: - Theme.of(context).colorScheme.disabledText), + .bodyMedium, + minLines: 1, + enabled: + false, + decoration: + InputDecoration( + border: + InputBorder + .none, + hintText: + 'بنویسید یا پیام صوتی بگذارید...', + hintStyle: Theme.of( + context) + .textTheme + .bodySmall! + .copyWith( + color: + Theme.of(context).colorScheme.disabledText), + ), ), ), - ), - const SizedBox( - width: 8, - ), - MessageBarBtn( - click: () { - Navigator.of(context).pushNamed( - Routes - .aiChat, - arguments: AiChatArgs( + const SizedBox( + width: 8, + ), + MessageBarBtn( + click: () { + context + .read< + AiState>() + .startChat( + AiChatArgs( bot: bot, attach: - true)); - }, - enable: false, - icon: Icons - .add), - ], - )))) - ], + true, + )); + }, + enable: false, + icon: Icons + .add), + ], + )))) + ], + ), ), ), - ), - ], - ), - const Padding( - padding: - EdgeInsets.fromLTRB(8, 8, 8, 4), - child: FittedBox( - child: DidvanText( - 'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.',style: TextStyle(fontWeight: FontWeight.w600), - ), + ], ), - ) - ], - )), - ], + const Padding( + padding: + EdgeInsets.fromLTRB(8, 8, 8, 4), + child: FittedBox( + child: DidvanText( + 'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.', + style: TextStyle( + fontWeight: FontWeight.w600), + ), + ), + ) + ], + )), + ], + ), ), - ), - ], - ); - }, - ); - } - }, - ), - Consumer(builder: (context, state, child) { - if (state.bots.isEmpty) return const SizedBox(); - return Positioned( - top: 32, - right: 0, - child: InkWell( - onTap: () => Scaffold.of(context).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, - ), - )), - ); - }) - ], - ); - } + ], + ); + }, + ); + } + }, + ), + ], + ); + } - ListView toolsPlaceHolder() { - return ListView.builder( - itemCount: 10, - shrinkWrap: true, - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 24), - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return Container( - decoration: BoxDecoration( - borderRadius: DesignConfig.lowBorderRadius, - border: Border.all( - color: const Color(0xffB8B8B8), + ListView toolsPlaceHolder() { + return ListView.builder( + itemCount: 10, + shrinkWrap: true, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 24), + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Container( + decoration: BoxDecoration( + borderRadius: DesignConfig.lowBorderRadius, + border: Border.all( + color: const Color(0xffB8B8B8), + ), ), - ), - padding: const EdgeInsets.all(24), - margin: const EdgeInsets.symmetric(vertical: 8), - child: Row( - children: [ - ShimmerPlaceholder( - width: 75, - height: 75, - borderRadius: BorderRadius.circular(360), - ), - const SizedBox( - width: 8, - ), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ShimmerPlaceholder( - width: 120, - height: 24, - borderRadius: DesignConfig.mediumBorderRadius, - ), - ShimmerPlaceholder( - width: 60, - height: 24, - borderRadius: DesignConfig.mediumBorderRadius, - ), - ], - ), - SizedBox( - height: 8, - ), - ShimmerPlaceholder( - width: 120, - height: 24, - borderRadius: DesignConfig.mediumBorderRadius, - ), - SizedBox( - height: 8, - ), - ShimmerPlaceholder( - width: 240, - height: 46, - borderRadius: DesignConfig.mediumBorderRadius, - ), - ], + padding: const EdgeInsets.all(24), + margin: const EdgeInsets.symmetric(vertical: 8), + child: Row( + children: [ + ShimmerPlaceholder( + width: 75, + height: 75, + borderRadius: BorderRadius.circular(360), ), - ) - ], - ), - ); - }, - ); - } -} \ No newline at end of file + const SizedBox( + width: 8, + ), + const Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ShimmerPlaceholder( + width: 120, + height: 24, + borderRadius: DesignConfig.mediumBorderRadius, + ), + ShimmerPlaceholder( + width: 60, + height: 24, + borderRadius: DesignConfig.mediumBorderRadius, + ), + ], + ), + SizedBox( + height: 8, + ), + ShimmerPlaceholder( + width: 120, + height: 24, + borderRadius: DesignConfig.mediumBorderRadius, + ), + SizedBox( + height: 8, + ), + ShimmerPlaceholder( + width: 240, + height: 46, + borderRadius: DesignConfig.mediumBorderRadius, + ), + ], + ), + ) + ], + ), + ); + }, + ); + } + } \ No newline at end of file diff --git a/lib/views/ai/ai_chat_page.dart b/lib/views/ai/ai_chat_page.dart index a294bed..95d40cf 100644 --- a/lib/views/ai/ai_chat_page.dart +++ b/lib/views/ai/ai_chat_page.dart @@ -1,12 +1,14 @@ +// lib/views/ai/ai_chat_page.dart + // ignore_for_file: library_private_types_in_public_api, deprecated_member_use, depend_on_referenced_packages, unnecessary_import import 'dart:io' show Platform; +import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:didvan/config/design_config.dart'; 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'; @@ -28,7 +30,6 @@ import 'package:didvan/views/ai/widgets/audio_wave.dart'; import 'package:didvan/views/ai/widgets/hoshan_drawer.dart'; import 'package:didvan/views/widgets/didvan/didvan_markdown.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; -import 'package:didvan/views/widgets/hoshan_app_bar.dart'; import 'package:didvan/views/widgets/marquee_text.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/video/chat_video_player.dart'; @@ -52,6 +53,34 @@ class _AiChatPageState extends State { final GlobalKey scaffKey = GlobalKey(); FocusNode focusNode = FocusNode(); + @override + void didUpdateWidget(covariant AiChatPage oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.args.bot.id != oldWidget.args.bot.id) { + final state = context.read(); + state.clearChat(); + + if (widget.args.chat != null) { + state.chatId = widget.args.chat!.id!; + state.chat = widget.args.chat; + state.getAllMessages(state.chatId!); + } + + + if (widget.args.prompts != null) { + state.messages.add(MessageModel( + dateTime: DateTime.now() + .subtract(const Duration(minutes: 210)) + .toIso8601String(), + prompts: [widget.args.prompts!])); + + state.message.clear(); + state.update(); + state.postMessage( + widget.args.bot, widget.args.assistantsName != null); + } + } +} @override void initState() { final state = context.read(); @@ -59,6 +88,8 @@ class _AiChatPageState extends State { state.chatId = widget.args.chat!.id!; state.chat = widget.args.chat; } + + WidgetsBinding.instance.addPostFrameCallback((_) async { if (state.chatId != null) { state.getAllMessages(state.chatId!).then((value) => Future.delayed( @@ -68,6 +99,7 @@ class _AiChatPageState extends State { () => focusNode.requestFocus(), )); } else { + state.appState = AppState.idle; Future.delayed( const Duration( milliseconds: 100, @@ -103,145 +135,141 @@ class _AiChatPageState extends State { }, child: Consumer( builder: (context, state, child) => Scaffold( - appBar: HoshanAppBar( - onBack: () { - Navigator.pop(context); - }, - withActions: false, - ), + // appBar: HoshanAppBar( + // onBack: () { + // Navigator.pop(context); + // }, + // withActions: false, + // ), key: scaffKey, resizeToAvoidBottomInset: true, drawer: HoshanDrawer( scaffKey: scaffKey, ), - body: state.loading - ? Center( - child: Image.asset( - Assets.loadingAnimation, - width: 60, - height: 60, - ), - ) - : Stack( - children: [ - SingleChildScrollView( - reverse: true, - controller: state.scrollController, - child: Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 24, - ), - SkeletonImage( - width: 75, - height: 75, - imageUrl: widget.args.bot.image.toString(), - borderRadius: BorderRadius.circular(360), - ), - const SizedBox( - height: 12, - ), - DidvanText( - widget.args.assistantsName ?? - widget.args.bot.name.toString(), - fontSize: 17, - fontWeight: FontWeight.bold, - ), - if (state.messages.isEmpty) - Column( + body: StateHandler( + state: state, + onRetry: () { + if (state.chatId != null) { + state.getAllMessages(state.chatId!); + } + }, + builder: (context, state) { + return Stack( + children: [ + SingleChildScrollView( + reverse: true, + controller: state.scrollController, + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 24, + ), + // SkeletonImage( + // width: 75, + // height: 75, + // imageUrl: widget.args.bot.image.toString(), + // borderRadius: BorderRadius.circular(360), + // ), + // const SizedBox( + // height: 12, + // ), + // DidvanText( + // widget.args.assistantsName ?? + // widget.args.bot.name.toString(), + // fontSize: 17, + // fontWeight: FontWeight.bold, + // ), + if (state.messages.isEmpty) + Column( + children: [ + const SizedBox( + height: 16, + ), + Padding( + padding: const EdgeInsets.only(left: 60,right: 60), + child: Center( + child: DidvanText( + widget.args.bot.description ?? '', + fontSize: 12, + color: Theme.of(context) + .colorScheme + .caption, + textAlign: TextAlign.justify, + )), + ), + const SizedBox( + height: 100, + ), + ], + ) + ], + ), + if (state.messages.isNotEmpty) + ListView.builder( + itemCount: state.messages.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.only( + bottom: state.file != null && + !(state.file!.isRecorded) + ? 180 + : 100), + itemBuilder: (context, mIndex) { + final prompts = + state.messages[mIndex].prompts; + final time = + state.messages[mIndex].dateTime; + return Column( children: [ - const SizedBox( - height: 16, - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20.0), - child: Center( - child: DidvanText( - widget.args.bot.description ?? '', - fontSize: 12, - color: Theme.of(context) - .colorScheme - .caption, - textAlign: TextAlign.justify, - )), - ), - const SizedBox( - height: 100, + timeLabel(context, time), + ListView.builder( + itemCount: prompts.length, + shrinkWrap: true, + physics: + const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + final message = prompts[index]; + + return messageBubble(message, + context, state, index, mIndex); + }, ), ], - ) - ], - ), - if (state.messages.isNotEmpty) - ListView.builder( - itemCount: state.messages.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.only( - bottom: state.file != null && - !(state.file!.isRecorded) - ? 180 - : 100), - itemBuilder: (context, mIndex) { - final prompts = - state.messages[mIndex].prompts; - final time = - state.messages[mIndex].dateTime; - return Column( - children: [ - timeLabel(context, time), - ListView.builder( - itemCount: prompts.length, - shrinkWrap: true, - physics: - const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - final message = prompts[index]; - - return messageBubble(message, - context, state, index, mIndex); - }, - ), - ], - ); - }), - ], - ), + ); + }), + ], ), - Positioned( - top: 32, - right: 0, - child: InkWell( - onTap: () => scaffKey.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, - ), - )), - ) - ], - ), + ), + Positioned( + top: 32, + right: 0, + child: InkWell( + onTap: () => scaffKey.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, + ), + )), + ) + ], + ); + }, + ), bottomSheet: Column( mainAxisSize: MainAxisSize.min, children: [ - // Platform.isIOS - // ? AiMessageBarIOS( - // bot: widget.args.bot, - // ) - // : AiMessageBar( bot: widget.args.bot, attch: widget.args.attach, @@ -362,9 +390,7 @@ class _AiChatPageState extends State { : Column( children: [ if (file != null) - (file.isAudio() - // && (!kIsWeb && !Platform.isIOS) - ) + (file.isAudio()) ? Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8), @@ -428,104 +454,105 @@ class _AiChatPageState extends State { .toString() .contains('user') && widget.args.assistantsName == null) - // PopupMenuButton( - // offset: const Offset(0, 46), - // onSelected: (value) async { - // navigatorKey.currentState!.pushNamed( - // Routes.aiChat, - // arguments: AiChatArgs( - // bot: value, - // prompts: message, - // isTool: widget.args.isTool ?? - // context - // .read< - // HistoryAiChatState>() - // .bots)); - // }, - // itemBuilder: (BuildContext context) { - // final bots = widget.args.isTool ?? - // context - // .read() - // .bots; - // return [ - // ...List.generate( - // bots.length, - // (index) => PopupMenuItem( - // value: bots[index], - // height: 72, - // child: Container( - // constraints: - // const BoxConstraints( - // maxWidth: 200), - // child: Row( - // children: [ - // SkeletonImage( - // imageUrl: bots[index] - // .image - // .toString(), - // width: 42, - // height: 42, - // borderRadius: - // BorderRadius - // .circular(360), - // ), - // const SizedBox(width: 12), - // Expanded( - // child: Directionality( - // textDirection: - // TextDirection.ltr, - // child: DidvanText( - // bots[index] - // .name - // .toString(), - // maxLines: 1, - // overflow: - // TextOverflow - // .ellipsis, - // ), - // ), - // ), - // ], - // ), - // ), - // ), - // ) - // ]; - // }, - // // child: Container( - // // alignment: Alignment.center, - // // margin: const EdgeInsets.all(8), - // // padding: const EdgeInsets.symmetric( - // // horizontal: 8), - // // constraints: const BoxConstraints( - // // maxWidth: 100), - // // decoration: BoxDecoration( - // // borderRadius: - // // DesignConfig.lowBorderRadius, - // // border: Border.all( - // // color: Theme.of(context) - // // .colorScheme - // // .title)), - // // child: Row( - // // children: [ - // // Expanded( - // // child: Directionality( - // // textDirection: - // // TextDirection.ltr, - // // child: DidvanText( - // // '${widget.args.assistantsName ?? widget.args.bot.name}', - // // maxLines: 1, - // // overflow: - // // TextOverflow.ellipsis, - // // ), - // // ), - // // ), - // // const Icon( - // // DidvanIcons.angle_down_light), - // // ], - // // ), - // // ) - // ), + PopupMenuButton( + offset: const Offset(0, 46), + onSelected: (value) async { + navigatorKey.currentState!.pushNamed( + Routes.aiChat, + arguments: AiChatArgs( + bot: value, + prompts: message, + isTool: widget.args.isTool ?? + context + .read< + HistoryAiChatState>() + .bots)); + }, + itemBuilder: (BuildContext context) { + final bots = widget.args.isTool ?? + context + .read() + .bots; + return [ + ...List.generate( + bots.length, + (index) => PopupMenuItem( + value: bots[index], + height: 72, + child: Container( + constraints: + const BoxConstraints( + maxWidth: 200), + child: Row( + children: [ + SkeletonImage( + imageUrl: bots[index] + .image + .toString(), + width: 42, + height: 42, + borderRadius: + BorderRadius + .circular(360), + ), + const SizedBox(width: 12), + Expanded( + child: Directionality( + textDirection: + TextDirection.ltr, + child: DidvanText( + bots[index] + .name + .toString(), + maxLines: 1, + overflow: + TextOverflow + .ellipsis, + ), + ), + ), + ], + ), + ), + ), + ) + ]; + }, + child: const SizedBox(), + // Container( + // alignment: Alignment.center, + // margin: const EdgeInsets.all(8), + // padding: const EdgeInsets.symmetric( + // horizontal: 8), + // constraints: const BoxConstraints( + // maxWidth: 100), + // decoration: BoxDecoration( + // borderRadius: + // DesignConfig.lowBorderRadius, + // border: Border.all( + // color: Theme.of(context) + // .colorScheme + // .title)), + // child: Row( + // children: [ + // Expanded( + // child: Directionality( + // textDirection: + // TextDirection.ltr, + // child: DidvanText( + // '${widget.args.assistantsName ?? widget.args.bot.name}', + // maxLines: 1, + // overflow: + // TextOverflow.ellipsis, + // ), + // ), + // ), + // const Icon( + // DidvanIcons.angle_down_light), + // ], + // ), + // ) + ), if (message.role .toString() .contains('user') && @@ -691,7 +718,6 @@ class _AiChatPageState extends State { children: [ DidvanText( DateTimeUtils.timeWithAmPm(message.createdAt.toString()), - // DateTimeUtils.timeWithAmPm(message.createdAt), style: Theme.of(context).textTheme.labelSmall, color: Theme.of(context).colorScheme.caption, ), @@ -733,18 +759,6 @@ class _AiChatPageState extends State { : TextDirection.rtl, ), ), - // if (state.file != null && !kIsWeb) - // FutureBuilder( - // future: state.file!.main.length(), - // builder: (context, snapshot) { - // if (!snapshot.hasData) { - // return const SizedBox(); - // } - // return DidvanText( - // 'File Size ${(snapshot.data! / 1000).round()} KB', - // fontSize: 12, - // ); - // }) ], ), ) @@ -772,4 +786,4 @@ class _AiChatPageState extends State { child: Image.file(file.main)), ); } -} +} \ No newline at end of file diff --git a/lib/views/ai/ai_chat_state.dart b/lib/views/ai/ai_chat_state.dart index aed21fc..8379cb1 100644 --- a/lib/views/ai/ai_chat_state.dart +++ b/lib/views/ai/ai_chat_state.dart @@ -290,4 +290,13 @@ class AiChatState extends CoreProvier { message: 'خطا در برقراری ارتباط', aLertType: ALertType.error)); update(); } + void clearChat() { + messages.clear(); + chatId = null; + chat = null; + file = null; + message.clear(); + isEdite = false; + notifyListeners(); +} } diff --git a/lib/views/ai/ai_state.dart b/lib/views/ai/ai_state.dart index 5bc2a02..6919591 100644 --- a/lib/views/ai/ai_state.dart +++ b/lib/views/ai/ai_state.dart @@ -1,3 +1,6 @@ +// lib/views/ai/ai_state.dart + +import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/tools_model.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/providers/core.dart'; @@ -7,10 +10,23 @@ import 'package:didvan/services/network/request_helper.dart'; class AiState extends CoreProvier { int page = 0; Tools? tool; + AiChatArgs? currentChatArgs; + bool isChatting = false; bool loading = true; List? tools; - + + void startChat(AiChatArgs args) { + currentChatArgs = args; + isChatting = true; + notifyListeners(); + } + + void endChat() { + currentChatArgs = null; + isChatting = false; + notifyListeners(); + } void getTools() async { final service = RequestService( @@ -41,4 +57,4 @@ class AiState extends CoreProvier { this.tool = tool; update(); } -} +} \ No newline at end of file diff --git a/lib/views/ai/history_ai_chat_page.dart b/lib/views/ai/history_ai_chat_page.dart index 8c70262..b8fddb2 100644 --- a/lib/views/ai/history_ai_chat_page.dart +++ b/lib/views/ai/history_ai_chat_page.dart @@ -1,3 +1,5 @@ +// lib/views/ai/history_ai_chat_page.dart + // ignore_for_file: library_private_types_in_public_api, deprecated_member_use import 'dart:async'; @@ -62,10 +64,8 @@ class _HistoryAiChatPageState extends State { child: DidvanScaffold( hidePlayer: true, physics: const BouncingScrollPhysics(), - // floatingActionButton: openAiListBtn(context), padding: EdgeInsets.zero, scrollController: scrollController, - showSliversFirst: false, slivers: [ SliverAppBar( @@ -97,6 +97,7 @@ class _HistoryAiChatPageState extends State { builder: (context, state, child) { return SliverStateHandler( state: state, + centerEmptyState: false, emptyState: EmptyState( asset: Assets.emptyResult, title: 'لیست خالی است', @@ -106,8 +107,6 @@ class _HistoryAiChatPageState extends State { : state.chats.isEmpty, placeholder: const _HistoryPlaceholder(), placeholderCount: 8, - - // builder: (context, state, index) => _HistoryPlaceholder(), builder: (context, state, index) { final chat = archived ? state.archivedChats[index] @@ -216,20 +215,20 @@ class _HistoryAiChatPageState extends State { }, child: InkWell( onTap: () { - // if (state.chatsToDelete.isEmpty) { - navigatorKey.currentState!.pushNamed(Routes.aiChat, - arguments: AiChatArgs( - bot: chat.bot!, - chat: chat, - assistantsName: chat.assistantsName)); - // } else { - // if (state.chatsToDelete.contains(chat.id)) { - // state.chatsToDelete.remove(chat.id!); - // } else { - // state.chatsToDelete.add(chat.id!); - // } - // } - // state.update(); + if (chat.bot != null) { + navigatorKey.currentState!.pushNamed(Routes.aiChat, + arguments: AiChatArgs( + bot: chat.bot!, + chat: chat, + assistantsName: chat.assistantsName)); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'امکان باز کردن این چت وجود ندارد.'), + ), + ); + } }, onLongPress: () { if (archived) { @@ -241,11 +240,6 @@ class _HistoryAiChatPageState extends State { state.chats[index].copyWith(isEditing: true); } state.update(); - - // if (state.chatsToDelete.isEmpty) { - // state.chatsToDelete.add(chat.id!); - // } - // state.update(); }, child: Container( padding: const EdgeInsets.symmetric( @@ -280,7 +274,6 @@ class _HistoryAiChatPageState extends State { chat.assistantsName ?? chat.bot!.name.toString(), fontWeight: FontWeight.bold, - // fontSize: 18, ), DidvanText( DateTimeUtils.momentGenerator( @@ -377,8 +370,6 @@ class _HistoryAiChatPageState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, - // fontWeight: FontWeight.bold, - // fontSize: 16, ), if (chat.prompts != null && chat.prompts!.isNotEmpty && @@ -475,30 +466,6 @@ class _HistoryAiChatPageState extends State { ), ); } - - // Widget openAiListBtn(BuildContext context) { - // final watch = context.watch(); - // final state = context.read(); - // return FloatingActionButton( - // backgroundColor: watch.chatsToDelete.isEmpty - // ? Theme.of(context).colorScheme.primary - // : Theme.of(context).colorScheme.error, - // shape: const OvalBorder(), - // mini: true, - // onPressed: () { - // if (watch.chatsToDelete.isEmpty) { - // state.getBots(); - // state.search = ''; - // _botsDialogSelect(context); - // } else { - // state.addChatToDelete(); - // } - // }, - // child: watch.chatsToDelete.isEmpty - // ? const Icon(DidvanIcons.add_regular) - // : const Icon(DidvanIcons.trash_regular), - // ); - // } } class _HistoryPlaceholder extends StatelessWidget { @@ -552,4 +519,4 @@ class _HistoryPlaceholder extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/views/ai/widgets/ai_message_bar.dart b/lib/views/ai/widgets/ai_message_bar.dart index 09c391e..2d9d701 100644 --- a/lib/views/ai/widgets/ai_message_bar.dart +++ b/lib/views/ai/widgets/ai_message_bar.dart @@ -297,11 +297,12 @@ class _AiMessageBarState extends State { MediaQuery.of(context).viewInsets.bottom == 0 ? const Padding( padding: EdgeInsets.fromLTRB(3, 8, 3, 4), - child: DidvanText( - 'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.', - fontSize: 11, - fontWeight: FontWeight.bold, - ), + child: SizedBox(height: 12,) + // DidvanText( + // 'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.', + // fontSize: 11, + // fontWeight: FontWeight.bold, + // ), ) : const SizedBox( height: 12, diff --git a/lib/views/ai/widgets/hoshan_drawer.dart b/lib/views/ai/widgets/hoshan_drawer.dart index e141bd6..fcc5a14 100644 --- a/lib/views/ai/widgets/hoshan_drawer.dart +++ b/lib/views/ai/widgets/hoshan_drawer.dart @@ -1,3 +1,4 @@ +// lib/views/ai/widgets/hoshan_drawer.dart import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/main.dart'; @@ -8,6 +9,7 @@ import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/action_sheet.dart'; +import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; @@ -28,15 +30,10 @@ class HoshanDrawer extends StatefulWidget { class _HoshanDrawerState extends State { @override - void initState() { + initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - final historyState = context.read(); - if (historyState.chats.isEmpty && !historyState.loadinggetAll) { - historyState.getChats(archived: false); - } - }); } + @override Widget build(BuildContext context) { return Drawer( @@ -141,13 +138,6 @@ class _HoshanDrawerState extends State { const SizedBox( height: 12, ), - // SearchField( - // title: 'title', - // onChanged: (value) {}, - // focusNode: FocusNode()), - // SizedBox( - // height: 12, - // ), Expanded( child: CustomScrollView( slivers: [ @@ -169,11 +159,6 @@ class _HoshanDrawerState extends State { onRetry: () => state.getChats()) ], )), - - // SizedBox( - // height: 12, - // ), - // Text('نمایش قدیمی‌ترها') ], ), ), @@ -315,11 +300,19 @@ class _HoshanDrawerState extends State { padding: const EdgeInsets.symmetric(vertical: 8.0), child: InkWell( onTap: () { - navigatorKey.currentState!.pushNamed(Routes.aiChat, - arguments: AiChatArgs( - bot: chat.bot!, - chat: chat, - assistantsName: chat.assistantsName)); + if (chat.bot != null) { + navigatorKey.currentState!.pushNamed(Routes.aiChat, + arguments: AiChatArgs( + bot: chat.bot!, + chat: chat, + assistantsName: chat.assistantsName)); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('امکان باز کردن این چت وجود ندارد.'), + ), + ); + } }, child: Row( children: [ @@ -440,4 +433,4 @@ class _HoshanDrawerState extends State { ), ); } -} +} \ No newline at end of file diff --git a/lib/views/ai/widgets/tool_category_view_widget.dart b/lib/views/ai/widgets/tool_category_view_widget.dart index 1d7e867..a5d04a8 100644 --- a/lib/views/ai/widgets/tool_category_view_widget.dart +++ b/lib/views/ai/widgets/tool_category_view_widget.dart @@ -1,17 +1,21 @@ +// lib/views/ai/widgets/tool_category_view_widget.dart + import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/tools_model.dart'; -import 'package:didvan/routes/routes.dart'; +import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; class ToolCategoryViewWidget extends StatelessWidget { final Tools tool; - const ToolCategoryViewWidget({Key? key, required this.tool}) : super(key: key); + const ToolCategoryViewWidget({Key? key, required this.tool}) + : super(key: key); @override Widget build(BuildContext context) { @@ -32,15 +36,18 @@ class ToolCategoryViewWidget extends StatelessWidget { tool.image!, width: 64, height: 64, - placeholderBuilder: (BuildContext context) => const SizedBox( + placeholderBuilder: (BuildContext context) => + const SizedBox( width: 64, height: 64, - child: Center(child: CircularProgressIndicator(strokeWidth: 2.0)), + child: Center( + child: CircularProgressIndicator(strokeWidth: 2.0)), ), ), const SizedBox(height: 8), Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), margin: const EdgeInsets.symmetric(horizontal: 16), child: DidvanText( tool.name ?? "ابزار هوش مصنوعی", @@ -81,19 +88,22 @@ class ToolCategoryViewWidget extends StatelessWidget { final bot = tool.bots![index]; return InkWell( onTap: () { - Navigator.of(context).pushNamed(Routes.aiChat, - arguments: AiChatArgs(bot: bot, isTool: tool.bots)); + context + .read() + .startChat(AiChatArgs(bot: bot, isTool: tool.bots)); }, - child: Container( + child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.only( - topLeft: DesignConfig.highBorderRadius.topLeft, - topRight: DesignConfig.highBorderRadius.topRight, - bottomRight: DesignConfig.highBorderRadius.bottomRight, - bottomLeft: DesignConfig.highBorderRadius.bottomLeft, - ), + topLeft: DesignConfig.highBorderRadius.topLeft, + topRight: DesignConfig.highBorderRadius.topRight, + bottomRight: + DesignConfig.highBorderRadius.bottomRight, + bottomLeft: DesignConfig.highBorderRadius.bottomLeft, + ), color: Theme.of(context).colorScheme.surface, - border: Border.all(color: Theme.of(context).colorScheme.border)), + border: Border.all( + color: Theme.of(context).colorScheme.border)), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -107,55 +117,67 @@ class ToolCategoryViewWidget extends StatelessWidget { ), ), ), - Expanded( + Expanded( child: Padding( - padding: const EdgeInsets.fromLTRB(12, 8, 12, 12), + padding: + const EdgeInsets.fromLTRB(12, 8, 12, 12), child: Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ if (bot.image != null) SkeletonImage( imageUrl: bot.image!, - width: 70, + width: 70, height: 70, borderRadius: BorderRadius.circular(360), ) else - Icon(DidvanIcons.ai_solid, size: 50, color: Theme.of(context).colorScheme.primary), + Icon(DidvanIcons.ai_solid, + size: 50, + color: Theme.of(context) + .colorScheme + .primary), const SizedBox(height: 15), Container( decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(12)), + borderRadius: + BorderRadius.all(Radius.circular(12)), color: customBackgroundColor, ), - - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 4), child: Padding( padding: const EdgeInsets.all(4.0), child: DidvanText( bot.name ?? '', fontSize: 14, fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onPrimary, + color: Theme.of(context) + .colorScheme + .onPrimary, maxLines: 1, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), ), ), - if (bot.short != null && bot.short!.isNotEmpty) ...[ + if (bot.short != null && + bot.short!.isNotEmpty) ...[ const SizedBox(height: 8), Expanded( child: DidvanText( bot.short!, - fontSize: 10, - color: Theme.of(context).colorScheme.caption, - maxLines: 2, + fontSize: 10, + color: Theme.of(context) + .colorScheme + .caption, + maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), ), - ] else const Spacer(), + ] else + const Spacer(), ], ), ), @@ -167,9 +189,11 @@ class ToolCategoryViewWidget extends StatelessWidget { }, ) else - Padding( + Padding( padding: const EdgeInsets.all(32.0), - child: Center(child: DidvanText("هیچ رباتی برای ابزار '${tool.name ?? "انتخابی"}' موجود نیست.")), + child: Center( + child: DidvanText( + "هیچ رباتی برای ابزار '${tool.name ?? "انتخابی"}' موجود نیست.")), ), ], ), diff --git a/lib/views/ai_section/ai_section_page.dart b/lib/views/ai_section/ai_section_page.dart index 4342152..b884466 100644 --- a/lib/views/ai_section/ai_section_page.dart +++ b/lib/views/ai_section/ai_section_page.dart @@ -1,6 +1,11 @@ +// lib/views/ai_section/ai_section_page.dart +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/tools_model.dart'; -import 'package:didvan/models/enums.dart'; import 'package:didvan/views/ai/ai.dart'; +import 'package:didvan/views/ai/ai_chat_page.dart'; import 'package:didvan/views/ai/widgets/hoshan_drawer.dart'; import 'package:didvan/views/ai_section/widgets/ai_section_bnb.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; @@ -19,7 +24,7 @@ class ImageGenerationScreen extends StatelessWidget { final aiState = context.watch(); if (aiState.tools == null || aiState.tools!.isEmpty) { - if (aiState.loading || aiState.appState == AppState.busy) { + if (aiState.loading) { return const Center(child: CircularProgressIndicator()); } return const Center( @@ -37,7 +42,7 @@ class VoiceChatScreen extends StatelessWidget { final aiState = context.watch(); if (aiState.tools == null || aiState.tools!.isEmpty) { - if (aiState.loading || aiState.appState == AppState.busy) { + if (aiState.loading) { return const Center(child: CircularProgressIndicator()); } return const Center( @@ -56,7 +61,7 @@ class ChartAnalysisScreen extends StatelessWidget { final aiState = context.watch(); if (aiState.tools == null || aiState.tools!.isEmpty) { - if (aiState.loading || aiState.appState == AppState.busy) { + if (aiState.loading) { return const Center(child: CircularProgressIndicator()); } return const Center( @@ -81,7 +86,7 @@ class TranslationScreen extends StatelessWidget { final aiState = context.watch(); if (aiState.tools == null || aiState.tools!.isEmpty) { - if (aiState.loading || aiState.appState == AppState.busy) { + if (aiState.loading) { return const Center(child: CircularProgressIndicator()); } return const Center( @@ -97,7 +102,6 @@ class TranslationScreen extends StatelessWidget { return ToolCategoryViewWidget(tool: translationToolCategory); } } - class AiSectionPage extends StatefulWidget { const AiSectionPage({super.key}); @@ -105,25 +109,22 @@ class AiSectionPage extends StatefulWidget { State createState() => _AiSectionPageState(); } -class _AiSectionPageState extends State - with SingleTickerProviderStateMixin { - late TabController _tabController; +class _AiSectionPageState extends State { int _currentTabIndex = 2; final GlobalKey _aiSectionScaffoldKey = GlobalKey(); + final List _pages = const [ + ImageGenerationScreen(), + VoiceChatScreen(), + Ai(), + ChartAnalysisScreen(), + TranslationScreen(), + ]; + @override void initState() { super.initState(); - _tabController = TabController(length: 5, vsync: this, initialIndex: 2); - _tabController.addListener(() { - if (mounted) { - setState(() { - _currentTabIndex = _tabController.index; - }); - } - }); - WidgetsBinding.instance.addPostFrameCallback((_) { final historyAiChatState = context.read(); if (historyAiChatState.bots.isEmpty) { @@ -131,28 +132,64 @@ class _AiSectionPageState extends State } final aiState = context.read(); - aiState.page = 0; + aiState.endChat(); if (aiState.tools == null) { aiState.getTools(); } }); } - @override - void dispose() { - _tabController.dispose(); - super.dispose(); + void _onTabChanged(int index) { + final aiState = context.read(); + aiState.endChat(); + + setState(() { + _currentTabIndex = index; + }); + + if (aiState.tools != null && aiState.tools!.isNotEmpty) { + Tools? tool; + switch (index) { + case 0: + tool = aiState.tools![0]; + break; + case 1: + tool = aiState.tools!.last; + break; + case 2: + return; + case 3: + if (aiState.tools!.length > 1) { + tool = aiState.tools![1]; + } + break; + case 4: + if (aiState.tools!.length > 2) { + tool = aiState.tools![2]; + } + break; + } + + if (tool != null && tool.bots != null && tool.bots!.isNotEmpty) { + final firstBot = tool.bots!.first; + aiState.startChat(AiChatArgs(bot: firstBot)); + } + } } @override Widget build(BuildContext context) { + final aiState = context.watch(); + return Scaffold( key: _aiSectionScaffoldKey, appBar: HoshanAppBar( onBack: () { - final aiState = context.read(); - if (aiState.page != 0) { - aiState.goToAi(); + if (aiState.isChatting) { + aiState.endChat(); + setState(() { + _currentTabIndex = 2; + }); } else { Navigator.of(context).pop(); } @@ -160,22 +197,40 @@ class _AiSectionPageState extends State withActions: true, ), drawer: HoshanDrawer(scaffKey: _aiSectionScaffoldKey), - body: TabBarView( - controller: _tabController, - physics: const NeverScrollableScrollPhysics(), - children: const [ - ImageGenerationScreen(), - VoiceChatScreen(), - Ai(), - ChartAnalysisScreen(), - TranslationScreen(), + body: Stack( + children: [ + aiState.isChatting + ? AiChatPage(args: aiState.currentChatArgs!) + : IndexedStack( + index: _currentTabIndex, + children: _pages, + ), + Positioned( + top: 32, + right: 0, + child: InkWell( + onTap: () => _aiSectionScaffoldKey.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, + ), + ), + ), + ) ], ), bottomNavigationBar: AiSectionBNB( currentTabIndex: _currentTabIndex, - onTabChanged: (index) { - _tabController.animateTo(index); - }, + onTabChanged: _onTabChanged, ), ); } diff --git a/lib/views/ai_section/widgets/ai_section_bnb.dart b/lib/views/ai_section/widgets/ai_section_bnb.dart index 6dac1e0..6fa50cd 100644 --- a/lib/views/ai_section/widgets/ai_section_bnb.dart +++ b/lib/views/ai_section/widgets/ai_section_bnb.dart @@ -1,14 +1,8 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/models/ai/ai_chat_args.dart'; -import 'package:didvan/models/ai/tools_model.dart'; -import 'package:didvan/routes/routes.dart'; -import 'package:didvan/services/webview.dart'; -import 'package:didvan/views/ai/ai_state.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:provider/provider.dart'; class AiSectionBNB extends StatelessWidget { final int currentTabIndex; @@ -21,15 +15,20 @@ class AiSectionBNB extends StatelessWidget { @override Widget build(BuildContext context) { const String cameraSolid = 'lib/assets/icons/houshanNav/imagegeneratorS.svg'; - const String cameraRegular = 'lib/assets/icons/houshanNav/imagegeneratorU.svg'; - const String micSolid = 'lib/assets/icons/houshanNav/aichatS.svg'; - const String micRegular = 'lib/assets/icons/houshanNav/aichatU.svg'; - const String aiSolid = 'lib/assets/icons/houshanNav/houshan.svg'; - const String aiRegular = 'lib/assets/icons/houshanNav/houshan.svg'; + const String cameraRegular = + 'lib/assets/icons/houshanNav/imagegeneratorU.svg'; + // const String micSolid = 'lib/assets/icons/houshanNav/aichatS.svg'; + // const String micRegular = 'lib/assets/icons/houshanNav/aichatU.svg'; + const String aiSolid = + 'lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot-remix.svg'; + const String aiRegular = + 'lib/assets/icons/houshanNav/streamline-flex_ai-scanner-robot.svg'; const String searchSolid = 'lib/assets/icons/houshanNav/searchS.svg'; const String searchRegular = 'lib/assets/icons/houshanNav/searchU.svg'; - const String translateSolid = 'lib/assets/icons/houshanNav/translateS.svg'; - const String translateRegular = 'lib/assets/icons/houshanNav/translateU.svg'; + const String translateSolid = + 'lib/assets/icons/houshanNav/translateS.svg'; + const String translateRegular = + 'lib/assets/icons/houshanNav/translateU.svg'; return Container( height: 72, @@ -50,91 +49,38 @@ class AiSectionBNB extends StatelessWidget { children: [ _AiNavBarItem( isSelected: currentTabIndex == 0, - title: 'عکس ساز', + title: 'تصویرساز', selectedIcon: cameraSolid, unselectedIcon: cameraRegular, - onTap: () { - final aiState = context.read(); - if (aiState.tools != null && aiState.tools!.isNotEmpty) { - final Tools imageGenTool = aiState.tools![0]; // ابزار عکس ساز - if (imageGenTool.bots != null && imageGenTool.bots!.isNotEmpty) { - final bot = imageGenTool.bots!.first; - Navigator.of(context).pushNamed( - Routes.aiChat, - arguments: AiChatArgs(bot: bot, isTool: imageGenTool.bots), - ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('ابزار عکس ساز در حال بارگذاری است...')), - ); - } - }, - ), - _AiNavBarItem( - isSelected: currentTabIndex == 1, - title: 'چت صوتی', - selectedIcon: micSolid, - unselectedIcon: micRegular, - onTap: () { - NativeWebViewLauncher.openWebView( - 'https://www.aisada.ir/app/page1.html'); - }, + onTap: () => onTabChanged(0), ), + // _AiNavBarItem( + // isSelected: currentTabIndex == 1, + // title: 'چت صوتی', + // selectedIcon: micSolid, + // unselectedIcon: micRegular, + // onTap: () => onTabChanged(1), + // ), _AiNavBarItem( isSelected: currentTabIndex == 2, - title: '', + title: 'گفت‌وگو', selectedIcon: aiSolid, unselectedIcon: aiRegular, onTap: () => onTabChanged(2), ), _AiNavBarItem( isSelected: currentTabIndex == 3, - title: 'جست و جو', + title: 'جست‌وجو', selectedIcon: searchSolid, unselectedIcon: searchRegular, - onTap: () { - final aiState = context.read(); - // نکته: طبق کد شما، تب جستجو (اندیس ۳) ویجت تحلیل نمودار را نمایش می‌دهد - // که از ابزار اندیس ۱ استفاده می‌کند. - if (aiState.tools != null && aiState.tools!.length > 1) { - final Tools chartAnalysisTool = aiState.tools![1]; - if (chartAnalysisTool.bots != null && chartAnalysisTool.bots!.isNotEmpty) { - final bot = chartAnalysisTool.bots!.first; - Navigator.of(context).pushNamed( - Routes.aiChat, - arguments: AiChatArgs(bot: bot, isTool: chartAnalysisTool.bots), - ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('ابزار جستجو در حال بارگذاری است...')), - ); - } - }, + onTap: () => onTabChanged(3), ), _AiNavBarItem( isSelected: currentTabIndex == 4, title: 'ترجمه', selectedIcon: translateSolid, unselectedIcon: translateRegular, - onTap: () { - final aiState = context.read(); - if (aiState.tools != null && aiState.tools!.length > 2) { - final Tools translationToolCategory = aiState.tools![2]; - if (translationToolCategory.bots != null && translationToolCategory.bots!.isNotEmpty) { - final gptTranslatorBot = translationToolCategory.bots!.first; - Navigator.of(context).pushNamed( - Routes.aiChat, - arguments: AiChatArgs(bot: gptTranslatorBot, isTool: translationToolCategory.bots), - ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('ابزار ترجمه در حال بارگذاری است...')), - ); - } - }, + onTap: () => onTabChanged(4), ), ], ), @@ -142,7 +88,6 @@ class AiSectionBNB extends StatelessWidget { } } - class _AiNavBarItem extends StatelessWidget { final VoidCallback onTap; final bool isSelected; @@ -161,7 +106,6 @@ class _AiNavBarItem extends StatelessWidget { @override Widget build(BuildContext context) { - final double iconSize = title.isEmpty ? 50.0 : 32.0; return Expanded( diff --git a/lib/views/authentication/authentication_state.dart b/lib/views/authentication/authentication_state.dart index 013740b..00de5d3 100644 --- a/lib/views/authentication/authentication_state.dart +++ b/lib/views/authentication/authentication_state.dart @@ -43,10 +43,14 @@ class AuthenticationState extends CoreProvier { currentPageIndex = 2; } } else { - appState = AppState.failed; - ActionSheetUtils(navigatorKey.currentContext!) - .showAlert(AlertData(message: service.errorMessage)); + appState = AppState.failed; + String message = service.errorMessage; + if (service.statusCode == 404) { + message = 'کاربر موجود نمی‌باشد.'; } + ActionSheetUtils(navigatorKey.currentContext!) + .showAlert(AlertData(message: message)); +} } Future login(UserProvider userProvider) async { diff --git a/lib/views/authentication/screens/verification.dart b/lib/views/authentication/screens/verification.dart index ea3c8b5..6d0ec09 100644 --- a/lib/views/authentication/screens/verification.dart +++ b/lib/views/authentication/screens/verification.dart @@ -9,9 +9,11 @@ import 'package:didvan/views/authentication/authentication_state.dart'; import 'package:didvan/views/authentication/widgets/authentication_layout.dart'; import 'package:didvan/views/widgets/didvan/button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:provider/provider.dart'; +import 'package:sms_autofill/sms_autofill.dart'; class Verification extends StatefulWidget { const Verification({Key? key}) : super(key: key); @@ -39,7 +41,23 @@ class _VerificationState extends State { ); _handleTimer(); super.initState(); + _listenForSms(); } + + Future _listenForSms() async { + if (kDebugMode) { + print("<<<<< در حال تلاش برای دریافت امضای برنامه >>>>>"); + } + + final appSignature = await SmsAutoFill().getAppSignature; + + if (kDebugMode) { + print("App Signature: $appSignature"); + } + + await SmsAutoFill().listenForCode(); +} + @override Widget build(BuildContext context) { diff --git a/lib/views/authentication/widgets/authentication_layout.dart b/lib/views/authentication/widgets/authentication_layout.dart index 1ea1962..bd4a0cb 100644 --- a/lib/views/authentication/widgets/authentication_layout.dart +++ b/lib/views/authentication/widgets/authentication_layout.dart @@ -1,7 +1,7 @@ // ignore_for_file: deprecated_member_use import 'package:didvan/views/authentication/widgets/authentication_app_bar.dart'; -import 'package:didvan/views/widgets/logos/didvan_vertical_logo.dart'; +import 'package:didvan/views/widgets/logos/didvan_horizontal_logo.dart'; import 'package:flutter/material.dart'; class AuthenticationLayout extends StatelessWidget { @@ -23,10 +23,10 @@ class AuthenticationLayout extends StatelessWidget { toolbarHeight: 56, backgroundColor: Theme.of(context).colorScheme.background, flexibleSpace: Padding( - padding: EdgeInsets.only( + padding: const EdgeInsets.only( left: 16, right: 16, - top: MediaQuery.of(context).padding.top, + top: 7, ), child: AuthenticationAppBar( title: appBarTitle, @@ -41,7 +41,9 @@ class AuthenticationLayout extends StatelessWidget { bottom: 40, ), sliver: const SliverToBoxAdapter( - child: DidvanHorizontalLogo(), + child: DidvanVerticalLogo( + height: 145, + ), ), ), SliverPadding( diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart index baebb6c..33a7093 100644 --- a/lib/views/home/home.dart +++ b/lib/views/home/home.dart @@ -63,13 +63,11 @@ class _HomeState extends State ), ], ), - const SizedBox(height: 8), - + const SizedBox(height: 15), SvgPicture.asset(Assets.horizontalLogoWithText, height: 80), const SizedBox(height: 24), - DidvanText( - 'به دیدوان، چشم همیشه باز مدیران خوش آمدید', + 'به سوپر اپلیکیشن دیدوان خوش آمدید', textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, ), @@ -82,42 +80,47 @@ class _HomeState extends State data: ActionSheetData( backgroundColor: Theme.of(context).colorScheme.background, isBackgroundDropBlur: true, - content: Column( + content: Stack( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - InkWrapper( - onPressed: () { - Future.delayed( - Duration.zero, - () => Navigator.of(context).pop(), - ); - }, - child: const Icon( - DidvanIcons.close_solid, - size: 24, + Padding( + padding: const EdgeInsets.only(top: 24.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DidvanText( + 'شخصی سازی محتوا', + style: + Theme.of(context).textTheme.displaySmall, + color: Theme.of(context).colorScheme.text, + ), + ], ), - ), - DidvanText( - 'شخصی سازی محتوا', - style: Theme.of(context).textTheme.displaySmall, - color: Theme.of(context).colorScheme.text, - ), - const InkWrapper( - child: Icon( - DidvanIcons.close_regular, - size: 24, - color: Colors.transparent, + const SizedBox( + height: 12, ), + const DidvanText( + "کاربر گرامی\nلطفا جهت شخصی‌سازی و استفاده بهتر از برنامه، دسته‌بندی‌های مورد علاقه خود و زمان دریافت اعلانات را انتخاب نمایید.") + ], + ), + ), + Positioned( + top: 0, + left: 0, + child: InkWrapper( + onPressed: () { + Future.delayed( + Duration.zero, + () => Navigator.of(context).pop(), + ); + }, + child: const Icon( + DidvanIcons.close_solid, + size: 24, ), - ], + ), ), - const SizedBox( - height: 12, - ), - const DidvanText( - "کاربر گرامی\nلطفا جهت شخصی‌سازی و استفاده بهتر از برنامه، دسته‌بندی‌های مورد علاقه خود و زمان دریافت اعلانات را انتخاب نمایید.") ], ), onConfirmed: () { @@ -141,20 +144,22 @@ class _HomeState extends State if (widget.showDialogs ?? false) { _showDialog(context); } - // if (!kIsWeb) { - // NotificationService.startListeningNotificationEvents(); - // } final state = context.read(); DesignConfig.updateSystemUiOverlayStyle(); _tabController = TabController(length: 4, vsync: this, initialIndex: 0); state.tabController = _tabController; + + // این قسمت را اضافه یا جایگزین کنید _tabController.addListener(() { state.currentPageIndex = _tabController.index; if (_tabController.index == 2) { - final state = context.read(); - - state.getBots(); + // با هر بار ورود به تب هوشان، لیست چت‌ها ریست می‌شود + final historyState = context.read(); + historyState.chats.clear(); + historyState.archivedChats.clear(); + historyState.update(); // برای اطمینان از به‌روزرسانی UI + historyState.getBots(); } }); if (!kIsWeb) { diff --git a/lib/views/home/main/main_page.dart b/lib/views/home/main/main_page.dart index 4f85f62..c063b76 100644 --- a/lib/views/home/main/main_page.dart +++ b/lib/views/home/main/main_page.dart @@ -208,7 +208,7 @@ class _SwotSection extends StatelessWidget { /// Swot Items Slider DidvanSlider( - height: 300, + height: 335, itemCount: items.length, viewportFraction: 0.65, itemBuilder: (context, index, realIndex) => Padding( diff --git a/lib/views/home/main/main_page_state.dart b/lib/views/home/main/main_page_state.dart index 727bb19..ea0c12a 100644 --- a/lib/views/home/main/main_page_state.dart +++ b/lib/views/home/main/main_page_state.dart @@ -33,11 +33,9 @@ class MainPageState extends CoreProvier { Future _fetchStories() async { try { stories = await StoryService.getStories(); - // [اضافه شود] تعداد استوری های دریافت شده را چاپ کنید print("Fetched ${stories.length} stories."); } catch (e) { stories = []; - // [اضافه شود] خطای رخ داده را چاپ کنید debugPrint("Could not fetch stories: $e"); } } diff --git a/lib/views/home/main/widgets/main_content.dart b/lib/views/home/main/widgets/main_content.dart index 5fe9349..d0de86a 100644 --- a/lib/views/home/main/widgets/main_content.dart +++ b/lib/views/home/main/widgets/main_content.dart @@ -3,7 +3,6 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/views/home/main/widgets/banner.dart'; import 'package:didvan/views/home/widgets/categories.dart'; -import 'package:didvan/views/widgets/ai_banner.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; diff --git a/lib/views/home/main/widgets/story_section.dart b/lib/views/home/main/widgets/story_section.dart index ca435c2..4f8a909 100644 --- a/lib/views/home/main/widgets/story_section.dart +++ b/lib/views/home/main/widgets/story_section.dart @@ -1,7 +1,6 @@ import 'package:didvan/models/story_model.dart'; import 'package:didvan/routes/routes.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; class StorySection extends StatelessWidget { final List stories; @@ -67,8 +66,8 @@ class _StoryCircle extends StatelessWidget { valueListenable: allStoriesViewed, builder: (context, isViewed, child) { return Container( - width: 80.0, - height: 80.0, + width: 85.0, + height: 85.0, decoration: BoxDecoration( shape: BoxShape.circle, gradient: isViewed @@ -80,8 +79,8 @@ class _StoryCircle extends StatelessWidget { ) : const LinearGradient( colors: [ - Color.fromARGB(255, 27, 60, 79), - Color.fromARGB(255, 27, 60, 79) + Color.fromARGB(255, 1, 35, 54), + Color.fromARGB(255, 178, 4, 54), ], begin: Alignment.topRight, end: Alignment.bottomLeft, diff --git a/lib/views/home/main/widgets/swot_item_card.dart b/lib/views/home/main/widgets/swot_item_card.dart index e7bd4c0..1ad2b51 100644 --- a/lib/views/home/main/widgets/swot_item_card.dart +++ b/lib/views/home/main/widgets/swot_item_card.dart @@ -1,6 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/home_page_content/swot.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/network/request.dart'; @@ -22,6 +23,23 @@ class SwotItemCard extends StatefulWidget { } class _SwotItemCardState extends State { + String _getCategoryName(String category) { + switch (category) { + case 'MACRO_TRENDS': + return 'کلان روند'; + case 'INDUSTRY_ENVIRONMENT': + return 'محیط صنعت'; + case 'CONCEPTS': + return 'مفاهیم'; + case 'MACRO_ENVIRONMENT': + return 'محیط کلان'; + case 'INVESTMENTS': + return 'سرمایه گذاری'; + default: + return category; + } + } + @override Widget build(BuildContext context) { return GestureDetector( @@ -33,8 +51,8 @@ class _SwotItemCardState extends State { ); }, child: Container( + height: 500 , width: 250, - height: 50, margin: const EdgeInsets.only(right: 0), padding: const EdgeInsets.all(0), decoration: BoxDecoration( @@ -82,7 +100,7 @@ class _SwotItemCardState extends State { ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 0), child: Text( widget.item.title, style: Theme.of(context).textTheme.titleMedium?.copyWith( @@ -91,7 +109,6 @@ class _SwotItemCardState extends State { overflow: TextOverflow.ellipsis, ), ), - const SizedBox(height: 5), Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -128,24 +145,25 @@ class _SwotItemCardState extends State { ) ], ), - // GestureDetector( - // onTap: () {}, child: Icon(DidvanIcons.bookmark_solid) - // // Icon( - // // widget.item.marked - // // ? DidvanIcons.bookmark_solid - // // : DidvanIcons.bookmark_regular, - // // color: widget.item.marked - // // ? Theme.of(context).colorScheme.secondary - // // : Theme.of(context).colorScheme.caption, - // // ), - // ), + const SizedBox(height: 10), + Row( + children: [ + const Icon(DidvanIcons.puzzle_light,size: 17,), + const SizedBox(width: 5,), + Text( + _getCategoryName(widget.item.category), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + const SizedBox(height: 30), ], ), ), ], ), Positioned( - bottom: 0, + bottom: 3, left: 0, child: BookmarkIcon(postId: widget.item.id), ) @@ -154,4 +172,4 @@ class _SwotItemCardState extends State { ), ); } -} +} \ No newline at end of file diff --git a/lib/views/podcasts/studio_details/studio_details.mobile.dart b/lib/views/podcasts/studio_details/studio_details.mobile.dart index 5240a0a..f01af12 100644 --- a/lib/views/podcasts/studio_details/studio_details.mobile.dart +++ b/lib/views/podcasts/studio_details/studio_details.mobile.dart @@ -3,6 +3,7 @@ import 'package:chewie/chewie.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/assets.dart'; +import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart'; @@ -26,9 +27,8 @@ class StudioDetails extends StatefulWidget { } class _StudioDetailsState extends State { - // ignore: unused_field int _currentlyPlayingId = 0; - late VideoPlayerController _videoPlayerController; + VideoPlayerController? _videoPlayerController; ChewieController? _chewieController; @override @@ -40,26 +40,9 @@ class _StudioDetailsState extends State { Future.delayed( Duration.zero, () => state.getStudioDetails(widget.pageData['id']).then((_) { - if (!mounted) return; - _videoPlayerController = VideoPlayerController.network( - state.studio.link, - ); - _videoPlayerController.initialize().then((_) { - _chewieController = ChewieController( - videoPlayerController: _videoPlayerController, - customControls: const PrimaryControls(), - autoPlay: true, - looping: true, - aspectRatio: 16 / 9, - materialProgressColors: ChewieProgressColors( - playedColor: Theme.of(context).colorScheme.title, - handleColor: Theme.of(context).colorScheme.title, - ), - ); - setState(() { - _currentlyPlayingId = state.studio.id; - }); - }); + if (mounted) { + _initializePlayer(state.studio); + } }), ); @@ -72,9 +55,9 @@ class _StudioDetailsState extends State { () => Navigator.of(context).pushNamed( Routes.mentions, arguments: { - 'id': state.studio.id, + 'id': context.read().studio.id, 'type': 'studio', - 'title': state.studio.title, + 'title': context.read().studio.title, }, ), ); @@ -82,99 +65,135 @@ class _StudioDetailsState extends State { } } + Future _initializePlayer(StudioDetailsData studio) async { + // Disposing old controllers before creating new ones. + _videoPlayerController?.dispose(); + _chewieController?.dispose(); + + _videoPlayerController = VideoPlayerController.network(studio.link); + + try { + await _videoPlayerController!.initialize(); + if (mounted) { + setState(() { + _chewieController = ChewieController( + videoPlayerController: _videoPlayerController!, + customControls: const PrimaryControls(), + autoPlay: true, + looping: true, + aspectRatio: 16 / 9, + materialProgressColors: ChewieProgressColors( + playedColor: Theme.of(context).colorScheme.title, + handleColor: Theme.of(context).colorScheme.title, + ), + ); + _currentlyPlayingId = studio.id; + }); + } + } catch (e) { + debugPrint("Error initializing video player: $e"); + } + } + @override Widget build(BuildContext context) { final d = MediaQuery.of(context); return Consumer( - builder: (context, state, child) => StateHandler( - state: state, - onRetry: () { - try { - state.getStudioDetails(state.studio.id); - } catch (e) { - state.getStudioDetails(widget.pageData['id']); - } - }, - builder: (context, state) { - if (!state.isStudioLoaded) { - return Center( - child: Image.asset( - Assets.loadingAnimation, - width: 100, - height: 100, + builder: (context, state, child) { + if (state.isStudioLoaded && _currentlyPlayingId != state.studio.id) { + Future.microtask(() => _initializePlayer(state.studio)); + } + return StateHandler( + state: state, + onRetry: () { + try { + state.getStudioDetails(state.studio.id); + } catch (e) { + state.getStudioDetails(widget.pageData['id']); + } + }, + builder: (context, state) { + if (!state.isStudioLoaded) { + return Center( + child: Image.asset( + Assets.loadingAnimation, + width: 100, + height: 100, + ), + ); + } + return WillPopScope( + onWillPop: () async { + if (MediaService.currentPodcast != null) { + state.studio = MediaService.currentPodcast!; + } + state.handleTracking(id: state.studio.id); + return true; + }, + child: SafeArea( + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.surface, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(56), + child: DidvanAppBar( + appBarData: AppBarData( + trailing: BookmarkButton( + itemId: state.studio.id, + type: 'video', + value: state.studio.marked, + onMarkChanged: (value) { + widget.pageData['onMarkChanged']( + state.studio.id, value); + }, + gestureSize: 48, + ), + isSmall: true, + title: state.studio.title, + ), + ), + ), + body: SingleChildScrollView( + child: SizedBox( + height: d.size.height - d.padding.top - 56, + child: Column( + children: [ + AspectRatio( + aspectRatio: 16 / 9, + child: (_chewieController != null && + _chewieController! + .videoPlayerController.value.isInitialized) + ? Chewie(controller: _chewieController!) + : Center( + child: Image.asset( + Assets.loadingAnimation, + width: 100, + height: 100, + ), + ), + ), + Expanded( + child: StudioDetailsWidget( + onMarkChanged: (id, value) => widget + .pageData['onMarkChanged'](id, value, true), + ), + ), + ], + ), + ), + ), + ), ), ); - } - return WillPopScope( - onWillPop: () async { - if (MediaService.currentPodcast != null) { - state.studio = MediaService.currentPodcast!; - } - state.handleTracking(id: state.studio.id); - return true; - }, - child: SafeArea( - child: Scaffold( - backgroundColor: Theme.of(context).colorScheme.surface, - appBar: PreferredSize( - preferredSize: const Size.fromHeight(56), - child: DidvanAppBar( - appBarData: AppBarData( - trailing: BookmarkButton( - itemId: state.studio.id, - type: 'video', - value: state.studio.marked, - onMarkChanged: (value) { - widget.pageData['onMarkChanged']( - state.studio.id, value); - }, - gestureSize: 48, - ), - isSmall: true, - title: state.studio.title, - ), - ), - ), - body: SingleChildScrollView( - child: SizedBox( - height: d.size.height - d.padding.top - 56, - child: Column( - children: [ - AspectRatio( - aspectRatio: 16 / 9, - child: _chewieController != null - ? Chewie(controller: _chewieController!) - : Center( - child: Image.asset( - Assets.loadingAnimation, - width: 100, - height: 100, - ), - ), - ), - Expanded( - child: StudioDetailsWidget( - onMarkChanged: (id, value) => widget - .pageData['onMarkChanged'](id, value, true), - ), - ), - ], - ), - ), - ), - ), - ), - ); - }, - ), + }, + ); + }, ); } @override void dispose() { - _videoPlayerController.pause(); - _videoPlayerController.dispose(); + _videoPlayerController?.dispose(); _chewieController?.dispose(); super.dispose(); } -} +} \ No newline at end of file diff --git a/lib/views/podcasts/studio_details/studio_details_state.dart b/lib/views/podcasts/studio_details/studio_details_state.dart index e337f61..be39e35 100644 --- a/lib/views/podcasts/studio_details/studio_details_state.dart +++ b/lib/views/podcasts/studio_details/studio_details_state.dart @@ -18,11 +18,14 @@ class StudioDetailsState extends CoreProvier { StudioRequestArgs? args; StudioRequestArgs? podcastArgs; final List relatedQueue = []; - bool _positionListenerActivated = false; AppState alongSideState = AppState.idle; int _trackingTimerCounter = 0; Timer? _trackingTimer; + // BEGIN: ADD THIS LINE + StreamSubscription? _positionSubscription; + // END: ADD THIS LINE + int _selectedDetailsIndex = 0; Timer? timer; int timerValue = 10; @@ -134,6 +137,8 @@ class StudioDetailsState extends CoreProvier { } Future _handlePodcastPlayback(StudioDetailsData studio) async { + _positionSubscription?.cancel(); + if (args?.type == 'podcast') { MediaService.currentPodcast = studio; MediaService.podcastPlaylistArgs = args; @@ -149,20 +154,21 @@ class StudioDetailsState extends CoreProvier { } }, ); - if (nextStudio != null && !_positionListenerActivated) { - _positionListenerActivated = true; - MediaService.audioPlayer.positionStream.listen((event) { - if (MediaService.audioPlayerTag?.contains('message') == true) { + if (nextStudio != null) { + _positionSubscription = + MediaService.audioPlayer.positionStream.listen((event) { + if (this.args?.type != 'podcast' || + MediaService.audioPlayerTag?.contains('message') == true) { return; } final duration = - MediaService.duration ?? Duration(seconds: studio.duration); - if (event.compareTo(duration) > 0 && nextStudio != null) { - if (stopOnPodcastEnds) { + MediaService.duration ?? Duration(seconds: this.studio.duration); + if (event.compareTo(duration) > 0 && this.nextStudio != null) { + if (this.stopOnPodcastEnds) { MediaService.resetAudioPlayer(); return; } - getStudioDetails(nextStudio!.id, isForward: true); + this.getStudioDetails(this.nextStudio!.id, isForward: true); } }); } @@ -199,7 +205,7 @@ class StudioDetailsState extends CoreProvier { notifyListeners(); } - Future handleTracking({ + Future handleTracking({ required int id, bool sendRequest = true, }) async { @@ -207,6 +213,7 @@ class StudioDetailsState extends CoreProvier { _trackingTimerCounter = 0; _trackingTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _trackingTimerCounter++; + notifyListeners(); }); return; } @@ -224,6 +231,7 @@ class StudioDetailsState extends CoreProvier { @override void dispose() { _trackingTimer?.cancel(); + _positionSubscription?.cancel(); super.dispose(); } } \ No newline at end of file diff --git a/lib/views/story_viewer/story_viewer_page.dart b/lib/views/story_viewer/story_viewer_page.dart index f9ff67f..22db765 100644 --- a/lib/views/story_viewer/story_viewer_page.dart +++ b/lib/views/story_viewer/story_viewer_page.dart @@ -3,11 +3,10 @@ import 'package:didvan/models/story_model.dart'; import 'package:didvan/services/story_service.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:video_player/video_player.dart'; -// Main PageViewer to swipe between users class StoryViewerPage extends StatefulWidget { final List stories; final int tappedIndex; @@ -137,7 +136,7 @@ class _UserStoryViewerState extends State _animationController.duration = _videoController!.value.duration; _videoController!.play(); - // _animationController.forward(); + _animationController.forward(); } else { print( "Video failed to initialize or has zero duration. Skipping."); @@ -232,6 +231,7 @@ class _UserStoryViewerState extends State switch (story.media) { case MediaType.image: return CachedNetworkImage( + placeholder: (context, url) => const ShimmerPlaceholder(), imageUrl: story.url, fit: BoxFit.cover, width: double.infinity, diff --git a/lib/views/widgets/ai_banner.dart b/lib/views/widgets/ai_banner.dart index 063087a..c05e796 100644 --- a/lib/views/widgets/ai_banner.dart +++ b/lib/views/widgets/ai_banner.dart @@ -1,7 +1,5 @@ import 'package:didvan/services/webview.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_svg/svg.dart'; class AiBanner extends StatelessWidget { const AiBanner({ diff --git a/lib/views/widgets/didvan/text_field.dart b/lib/views/widgets/didvan/text_field.dart index 78c3a86..f3ffb1e 100644 --- a/lib/views/widgets/didvan/text_field.dart +++ b/lib/views/widgets/didvan/text_field.dart @@ -107,48 +107,51 @@ class _DidvanTextFieldState extends State { textDirection: val.text.startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, - child: TextFormField( - inputFormatters: [ - if (!widget.acceptSpace) - FilteringTextInputFormatter.allow( - RegExp("[0-9a-zA-Z\u0600-\u06FF]")), - ], - autofocus: widget.autoFocus, - obscureText: _hideContent, - textAlign: widget.textAlign ?? TextAlign.start, - keyboardType: widget.textInputType, - textInputAction: widget.textInputAction, - focusNode: _focusNode, - controller: _controller, - onFieldSubmitted: widget.onSubmitted, - onChanged: _onChanged, - validator: _validator, - maxLines: _hideContent ? 1 : 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 + child: Padding( + padding: const EdgeInsets.fromLTRB(8,8,0,8), + child: TextFormField( + inputFormatters: [ + if (!widget.acceptSpace) + FilteringTextInputFormatter.allow( + RegExp("[0-9a-zA-Z\u0600-\u06FF]")), + ], + autofocus: widget.autoFocus, + obscureText: _hideContent, + textAlign: widget.textAlign ?? TextAlign.start, + keyboardType: widget.textInputType, + textInputAction: widget.textInputAction, + focusNode: _focusNode, + controller: _controller, + onFieldSubmitted: widget.onSubmitted, + onChanged: _onChanged, + validator: _validator, + maxLines: _hideContent ? 1 : 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(color: Theme.of(context).colorScheme.hint), + .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), + ), ), ), ); diff --git a/lib/views/widgets/hoshan_app_bar.dart b/lib/views/widgets/hoshan_app_bar.dart index 5f563d2..2ebf2ec 100644 --- a/lib/views/widgets/hoshan_app_bar.dart +++ b/lib/views/widgets/hoshan_app_bar.dart @@ -4,10 +4,8 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/ai/ai_state.dart'; -import 'package:didvan/views/ai/bot_assistants_state.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -65,24 +63,24 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { Navigator.pushNamed(context, Routes.info); }), if (withActions) - Stack( - children: [ - DidvanIconButton( - icon: DidvanIcons.ai_regular, - size: 32, - onPressed: () { - context.read().getMyAssissmant(); + // Stack( + // children: [ + // DidvanIconButton( + // icon: DidvanIcons.ai_regular, + // size: 32, + // onPressed: () { + // context.read().getMyAssissmant(); - Navigator.pushNamed(context, Routes.botAssistants); - }, - ), - Icon( - CupertinoIcons.plus, - color: Theme.of(context).colorScheme.primary, - size: 16, - ) - ], - ), + // Navigator.pushNamed(context, Routes.botAssistants); + // }, + // ), + // Icon( + // CupertinoIcons.plus, + // color: Theme.of(context).colorScheme.primary, + // size: 16, + // ) + // ], + // ), if (context.watch().page != 0 || !withActions) Transform.rotate( angle: 180 * pi / 180, diff --git a/lib/views/widgets/video/primary_controls.dart b/lib/views/widgets/video/primary_controls.dart index 7686acd..b4ce37c 100644 --- a/lib/views/widgets/video/primary_controls.dart +++ b/lib/views/widgets/video/primary_controls.dart @@ -351,21 +351,25 @@ class _PrimaryControlsState extends State { Duration duration = chewieController.videoPlayerController.value.duration; + if (duration.inSeconds == 0) { + return const SizedBox(); // Ya namayesh-e yek loading bar + } + + double maxValue = duration.inMilliseconds.toDouble(); + double currentValue = p.inMilliseconds.toDouble(); + return Column( children: [ SliderTheme( data: SliderThemeData( trackHeight: 2, - // thumbColor: Colors.transparent, overlayShape: SliderComponentShape.noOverlay, thumbShape: const RoundSliderThumbShape( - // elevation: 0, - // pressedElevation: 0, enabledThumbRadius: 8)), child: Slider( min: 0, - max: duration.inMilliseconds.toDouble(), - value: p.inMilliseconds.toDouble(), + max: maxValue, + value: currentValue.clamp(0.0, maxValue), // Estefade az clamp onChanged: (value) async { await chewieController.pause(); position.value = Duration(milliseconds: value.round()); diff --git a/pubspec.lock b/pubspec.lock index a5052d2..63a7aff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1101,6 +1101,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.0.1" + pin_input_text_field: + dependency: transitive + description: + name: pin_input_text_field + sha256: f45683032283d30b670ec343781660655e3e1953438b281a0bc6e2d358486236 + url: "https://pub.dev" + source: hosted + version: "4.5.2" platform: dependency: transitive description: @@ -1237,6 +1245,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + shimmer: + dependency: "direct main" + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" skeleton_text: dependency: "direct main" description: @@ -1250,6 +1266,14 @@ packages: description: flutter source: sdk version: "0.0.0" + sms_autofill: + dependency: "direct main" + description: + name: sms_autofill + sha256: c65836abe9c1f62ce411bb78d5546a09ece4297558070b1bd871db1db283aaf9 + url: "https://pub.dev" + source: hosted + version: "2.4.1" source_span: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fc543d0..ca657b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -110,6 +110,8 @@ dependencies: package_info_plus: ^8.3.0 flutter_local_notifications: ^19.1.0 flutter_inappwebview: ^6.1.5 + sms_autofill: ^2.4.1 + shimmer: ^3.0.0 # fading_edge_scrollview: ^4.1.1 dev_dependencies: flutter_test: