diff --git a/lib/assets/icons/CONCEPTS.svg b/lib/assets/icons/CONCEPTS.svg
new file mode 100644
index 0000000..5b579ac
--- /dev/null
+++ b/lib/assets/icons/CONCEPTS.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/assets/icons/INDUSTRY_ENVIRONMENT.svg b/lib/assets/icons/INDUSTRY_ENVIRONMENT.svg
new file mode 100644
index 0000000..d84f675
--- /dev/null
+++ b/lib/assets/icons/INDUSTRY_ENVIRONMENT.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/assets/icons/MACRO_ENVIRONMENT.svg b/lib/assets/icons/MACRO_ENVIRONMENT.svg
new file mode 100644
index 0000000..89b28c0
--- /dev/null
+++ b/lib/assets/icons/MACRO_ENVIRONMENT.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/assets/icons/MACRO_TRENDS.svg b/lib/assets/icons/MACRO_TRENDS.svg
new file mode 100644
index 0000000..f63cb1d
--- /dev/null
+++ b/lib/assets/icons/MACRO_TRENDS.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/assets/icons/MODERN_INVESTMENTS.svg b/lib/assets/icons/MODERN_INVESTMENTS.svg
new file mode 100644
index 0000000..153817f
--- /dev/null
+++ b/lib/assets/icons/MODERN_INVESTMENTS.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/assets/icons/Mic Solid.svg b/lib/assets/icons/Mic Solid.svg
new file mode 100644
index 0000000..5cbfced
--- /dev/null
+++ b/lib/assets/icons/Mic Solid.svg
@@ -0,0 +1,4 @@
+
diff --git a/lib/assets/icons/houshanNav/aichatS.svg b/lib/assets/icons/houshanNav/aichatS.svg
new file mode 100644
index 0000000..9c63017
--- /dev/null
+++ b/lib/assets/icons/houshanNav/aichatS.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/assets/icons/houshanNav/aichatU.svg b/lib/assets/icons/houshanNav/aichatU.svg
new file mode 100644
index 0000000..674f6ec
--- /dev/null
+++ b/lib/assets/icons/houshanNav/aichatU.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/assets/icons/houshanNav/houshan.svg b/lib/assets/icons/houshanNav/houshan.svg
new file mode 100644
index 0000000..03645a8
--- /dev/null
+++ b/lib/assets/icons/houshanNav/houshan.svg
@@ -0,0 +1,4 @@
+
diff --git a/lib/assets/icons/houshanNav/imagegeneratorS.svg b/lib/assets/icons/houshanNav/imagegeneratorS.svg
new file mode 100644
index 0000000..d752854
--- /dev/null
+++ b/lib/assets/icons/houshanNav/imagegeneratorS.svg
@@ -0,0 +1,11 @@
+
diff --git a/lib/assets/icons/houshanNav/imagegeneratorU.svg b/lib/assets/icons/houshanNav/imagegeneratorU.svg
new file mode 100644
index 0000000..a894dff
--- /dev/null
+++ b/lib/assets/icons/houshanNav/imagegeneratorU.svg
@@ -0,0 +1,13 @@
+
diff --git a/lib/assets/icons/houshanNav/searchS.svg b/lib/assets/icons/houshanNav/searchS.svg
new file mode 100644
index 0000000..6136990
--- /dev/null
+++ b/lib/assets/icons/houshanNav/searchS.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/assets/icons/houshanNav/searchU.svg b/lib/assets/icons/houshanNav/searchU.svg
new file mode 100644
index 0000000..bd214df
--- /dev/null
+++ b/lib/assets/icons/houshanNav/searchU.svg
@@ -0,0 +1,4 @@
+
diff --git a/lib/assets/icons/houshanNav/translateS.svg b/lib/assets/icons/houshanNav/translateS.svg
new file mode 100644
index 0000000..f066e22
--- /dev/null
+++ b/lib/assets/icons/houshanNav/translateS.svg
@@ -0,0 +1,11 @@
+
diff --git a/lib/assets/icons/houshanNav/translateU.svg b/lib/assets/icons/houshanNav/translateU.svg
new file mode 100644
index 0000000..ef4d8ca
--- /dev/null
+++ b/lib/assets/icons/houshanNav/translateU.svg
@@ -0,0 +1,13 @@
+
diff --git a/lib/assets/icons/paperclip.svg b/lib/assets/icons/paperclip.svg
new file mode 100644
index 0000000..6a900fc
--- /dev/null
+++ b/lib/assets/icons/paperclip.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/main.dart b/lib/main.dart
index e820f1a..0d6b045 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -191,6 +191,9 @@ class _DidvanState extends State with WidgetsBindingObserver {
ChangeNotifierProvider(
create: (context) => BotAssistantsState(),
),
+ // ChangeNotifierProvider(
+ // create: (context) => StoryViewerState(),
+ // ),
],
child: Consumer(
builder: (context, themeProvider, child) => Container(
diff --git a/lib/models/story_model.dart b/lib/models/story_model.dart
new file mode 100644
index 0000000..22d9b98
--- /dev/null
+++ b/lib/models/story_model.dart
@@ -0,0 +1,122 @@
+import 'package:didvan/constants/assets.dart';
+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;
+ final MediaType media;
+ final Duration duration;
+ final ValueNotifier isViewed;
+
+ StoryItem({
+ required this.id,
+ required this.url,
+ required this.media,
+ required this.duration,
+ bool viewed = false,
+ }) : isViewed = ValueNotifier(viewed);
+
+ factory StoryItem.fromJson(Map json) {
+ MediaType mediaType;
+ switch (json['mediaType']) {
+ case 'VIDEO':
+ mediaType = MediaType.video;
+ break;
+ case 'GIF':
+ mediaType = MediaType.gif;
+ break;
+ case 'IMAGE':
+ default:
+ mediaType = MediaType.image;
+ break;
+ }
+
+ return 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),
+ );
+ }
+}
+
+class User {
+ final String name;
+ final String profileImageUrl;
+ final String createdAt;
+
+ const User({
+ required this.name,
+ required this.profileImageUrl,
+ required this.createdAt,
+ });
+}
+
+// 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 User user;
+ final List stories;
+
+ UserStories({
+ required this.id,
+ required this.user,
+ required this.stories,
+ });
+
+ factory UserStories.fromApiJson(Map json) {
+ final List items = json['items'] ?? [];
+ final List completedIds = json['completedStoryItemIds'] ?? [];
+
+ return UserStories(
+ id: json['id'],
+ user: User(
+ name: json['title'], // Using title directly from the API
+ profileImageUrl:
+ _mapCategoryToIcon(json['category']), // Mapping category to an icon
+ 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;
+ }
+ return storyItem;
+ }).toList(),
+ );
+ }
+}
+
+
+String _mapCategoryToIcon(String category) {
+ switch (category) {
+ case 'MACRO_TRENDS':
+ return 'lib/assets/icons/MACRO_TRENDS.svg';
+ case 'INDUSTRY_ENVIRONMENT':
+ return 'lib/assets/icons/INDUSTRY_ENVIRONMENT.svg';
+ case 'COMPETITION_ENVIRONMENT':
+ return Assets.startup;
+ case 'OPPORTUNITIES':
+ return Assets.hugeideas;
+ case 'THREATS':
+ return Assets.risk;
+ case 'TECHNOLOGY':
+ return Assets.tech;
+ case 'STARTUPS':
+ return Assets.startup;
+ case 'CONCEPTS':
+ return 'lib/assets/icons/CONCEPTS.svg';
+ case 'MACRO_ENVIRONMENT':
+ return 'lib/assets/icons/MACRO_ENVIRONMENT.svg';
+ case 'MODERN_INVESTMENTS':
+ return 'lib/assets/icons/MODERN_INVESTMENTS.svg';
+ default:
+ return Assets.stats;
+ }
+}
\ No newline at end of file
diff --git a/lib/providers/user.dart b/lib/providers/user.dart
index 77fd775..464412a 100644
--- a/lib/providers/user.dart
+++ b/lib/providers/user.dart
@@ -67,6 +67,8 @@ class UserProvider extends CoreProvier {
await _registerFirebaseToken();
+ notifyListeners();
+
return true;
} catch (e) {
return false;
diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart
index 01394e7..7ab73a4 100644
--- a/lib/routes/route_generator.dart
+++ b/lib/routes/route_generator.dart
@@ -1,13 +1,12 @@
-// ignore_for_file: unused_import, deprecated_member_use
-
import 'package:didvan/models/ai/ai_chat_args.dart';
+import 'package:didvan/models/story_model.dart';
import 'package:didvan/views/ai/ai_chat_page.dart';
import 'package:didvan/views/ai/ai_chat_state.dart';
+import 'package:didvan/views/ai_section/ai_section_page.dart';
import 'package:didvan/views/ai/bot_assistants_page.dart';
import 'package:didvan/views/ai/create_bot_assistants_page.dart';
import 'package:didvan/views/ai/create_bot_assistants_state.dart';
import 'package:didvan/views/ai/history_ai_chat_page.dart';
-import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/info_page.dart';
import 'package:didvan/views/ai/info_state.dart';
import 'package:didvan/views/authentication/authentication.dart';
@@ -25,7 +24,6 @@ import 'package:didvan/views/home/infography/infography_screen.dart';
import 'package:didvan/views/home/infography/infography_screen_state.dart';
import 'package:didvan/views/home/main/main_page_state.dart';
import 'package:didvan/views/home/home_state.dart';
-import 'package:didvan/views/home/new_statistic/new_statistic.dart';
import 'package:didvan/views/home/new_statistic/new_statistics_state.dart';
import 'package:didvan/views/home/new_statistic/statistics_details/stat_cats_general_screen.dart';
import 'package:didvan/views/home/new_statistic/statistics_details/stat_cats_general_state.dart';
@@ -61,6 +59,8 @@ import 'package:didvan/views/podcasts/studio_details/studio_details.mobile.dart'
if (dart.library.html) 'package:didvan/views/podcasts/studio_details/studio_details.web.dart';
import 'package:didvan/views/splash/splash.dart';
import 'package:didvan/routes/routes.dart';
+import 'package:didvan/views/story_viewer/story_viewer_page.dart';
+
import 'package:didvan/views/webview/web_view.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -80,45 +80,68 @@ class RouteGenerator {
return _createRoute(
const Splash(),
);
- case Routes.notificationTime:
+ case Routes.aiSection:
return _createRoute(
- ChangeNotifierProvider(
- create: (context) => NotificationTimeState(),
- child: NotificationTime(
- pageData: settings.arguments as Map,
- )),
+ const AiSectionPage(),
);
+ case Routes.notificationTime:
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => NotificationTimeState(),
+ child: NotificationTime(
+ pageData: settings.arguments as Map,
+ )),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
case Routes.favouritesStep:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => CustomizeCategoryState(),
- child: FavoritesStep(
- pageData: settings.arguments as Map,
- )),
- );
-
- // case Routes.newStatic:
- // return _createRoute(ChangeNotifierProvider(
- // create: (context) => NewStatisticState(),
- // child: const NewStatistic()));
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => CustomizeCategoryState(),
+ child: FavoritesStep(
+ pageData: settings.arguments as Map,
+ )),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
case Routes.notificationStatusStep:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => CustomizeCategoryState(),
- child: NotificationStatusStep(
- pageData: settings.arguments as Map,
- )),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => CustomizeCategoryState(),
+ child: NotificationStatusStep(
+ pageData: settings.arguments as Map,
+ )),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
case Routes.authenticaion:
+ bool isResetPassword = false;
+ if (settings.arguments is bool) {
+ isResetPassword = settings.arguments as bool;
+ } else if (settings.arguments != null) {
+ print('Warning: Invalid argument type for isResetPassword. Expected bool, got ${settings.arguments.runtimeType}. Using default false.');
+ }
return _createRoute(
ChangeNotifierProvider(
create: (context) => AuthenticationState(),
- child: Authentication(isResetPassword: settings.arguments as bool),
+ child: Authentication(isResetPassword: isResetPassword),
),
);
case Routes.home:
+ bool? showDialogsArg;
+ if (settings.arguments is bool?) {
+ showDialogsArg = settings.arguments as bool?;
+ } else if (settings.arguments != null) {
+ print('Warning: Invalid argument type for showDialogs. Expected bool?, got ${settings.arguments.runtimeType}. Using default null.');
+ }
return _createRoute(
MultiProvider(
providers: [
@@ -138,7 +161,7 @@ class RouteGenerator {
create: (context) => NewStatisticState())
],
child: Home(
- showDialogs: settings.arguments as bool?,
+ showDialogs: showDialogsArg,
),
),
);
@@ -195,51 +218,76 @@ class RouteGenerator {
));
case Routes.radarDetails:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => RadarDetailsState(),
- child: RadarDetails(
- pageData: settings.arguments as Map,
- ),
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => RadarDetailsState(),
+ child: RadarDetails(
+ pageData: settings.arguments as Map,
+ ),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.statGeneral:
- return _createRoute(MultiProvider(
- providers: [
- ChangeNotifierProvider(
- create: (context) => StatGeneralScreenState()),
- ChangeNotifierProvider(
- create: (context) => NewStatisticState()),
- ],
- child: StatGeneralScreen(
- pageData: settings.arguments as Map),
- ));
+ if (settings.arguments is Map) {
+ return _createRoute(MultiProvider(
+ providers: [
+ ChangeNotifierProvider(
+ create: (context) => StatGeneralScreenState()),
+ ChangeNotifierProvider(
+ create: (context) => NewStatisticState()),
+ ],
+ child: StatGeneralScreen(
+ pageData: settings.arguments as Map),
+ ));
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.newsDetails:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => NewsDetailsState(),
- child: NewsDetails(
- pageData: settings.arguments as Map,
- ),
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => NewsDetailsState(),
+ child: NewsDetails(
+ pageData: settings.arguments as Map,
+ ),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.studioDetails:
- return _createRoute(
- StudioDetails(
- pageData: settings.arguments as Map,
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ StudioDetails(
+ pageData: settings.arguments as Map,
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.statisticDetails:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => StatisticDetailsState(),
- child: StatisticDetails(
- pageData: settings.arguments as Map,
- ),
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => StatisticDetailsState(),
+ child: StatisticDetails(
+ pageData: settings.arguments as Map,
+ ),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.stock:
return _createRoute(MultiProvider(
@@ -259,30 +307,46 @@ class RouteGenerator {
),
);
case Routes.direct:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => DirectState(),
- child: Direct(pageData: settings.arguments as Map),
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => DirectState(),
+ child:
+ Direct(pageData: settings.arguments as Map),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.comments:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => CommentsState(),
- child: Comments(
- pageData: settings.arguments as Map,
- ),
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => CommentsState(),
+ child: Comments(
+ pageData: settings.arguments as Map,
+ ),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.mentions:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => MentionsState(),
- child: Mentions(
- pageData: settings.arguments as Map,
- ),
- ),
- );
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => MentionsState(),
+ child: Mentions(
+ pageData: settings.arguments as Map,
+ ),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
+
case Routes.bookmarks:
return _createRoute(
ChangeNotifierProvider(
@@ -291,39 +355,58 @@ class RouteGenerator {
),
);
case Routes.hashtag:
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => HashtagState(),
- child:
- Hashtag(pageData: settings.arguments as Map),
- ),
- );
- case Routes.filteredBookmarks:
- final args = settings.arguments as Map;
- final type = args['type'] as int;
- final onDeleted = args['onDeleted'] as void Function(int id)?;
+ if (settings.arguments is Map) {
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => HashtagState(),
+ child: Hashtag(
+ pageData: settings.arguments as Map),
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map.');
- return _createRoute(
- ChangeNotifierProvider(
- create: (context) => FilteredBookmarksState(
- type,
- ),
- child: FilteredBookmarks(
- onDeleted: onDeleted,
- type: type,
- ),
- ),
- );
+
+ case Routes.filteredBookmarks:
+ if (settings.arguments is Map) {
+ final args = settings.arguments as Map;
+ if (args['type'] is int) {
+ final type = args['type'] as int;
+ final onDeleted = args['onDeleted'] as void Function(int id)?;
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => FilteredBookmarksState(type),
+ child: FilteredBookmarks(
+ onDeleted: onDeleted,
+ type: type,
+ ),
+ ),
+ );
+ } else {
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: "type" key is missing or not an int.');
+ }
+ } else {
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected a Map.');
+ }
case Routes.aiChat:
- return _createRoute(ChangeNotifierProvider(
- create: (context) => AiChatState(),
- child: AiChatPage(
- args: settings.arguments as AiChatArgs,
- )));
+ if (settings.arguments is AiChatArgs) {
+ return _createRoute(ChangeNotifierProvider(
+ create: (context) => AiChatState(),
+ child: AiChatPage(
+ args: settings.arguments as AiChatArgs,
+ )));
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected AiChatArgs.');
+
+
case Routes.aiHistory:
return _createRoute(HistoryAiChatPage(
- archived: settings.arguments as bool?,
+ archived: settings.arguments as bool?,
));
case Routes.botAssistants:
@@ -354,22 +437,42 @@ class RouteGenerator {
child: const InfoPage()));
case Routes.web:
- return _createRoute(WebView(
- src: settings.arguments as String,
- ));
+ if (settings.arguments is String) {
+ return _createRoute(WebView(
+ src: settings.arguments as String,
+ ));
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected String.');
+
+ case Routes.storyViewer:
+ if (settings.arguments is Map) {
+ final args = settings.arguments as Map;
+ final stories = args['stories'] as List;
+ final tappedIndex = args['tappedIndex'] as int;
+ return _createRoute(
+ StoryViewerPage(
+ stories: stories,
+ tappedIndex: tappedIndex,
+ ),
+ );
+ }
+ return _errorRoute(
+ 'Invalid arguments for ${settings.name}: Expected Map with stories and tappedIndex.');
+
default:
- return _errorRoute(settings.name ?? '');
+ return _errorRoute(settings.name ?? 'Unknown route');
}
}
- static Route _errorRoute(String name) {
+ static Route _errorRoute(String message) {
return MaterialPageRoute(builder: (_) {
return Scaffold(
appBar: AppBar(
- title: const Text('Error'),
+ title: const Text('خطا در مسیریابی'),
),
body: Center(
- child: Text('$name is not valid'),
+ child: Text('مسیر "$message" معتبر نیست یا آرگومانهای نادرستی ارسال شده است.'),
),
);
});
@@ -411,4 +514,4 @@ class RouteGenerator {
},
);
}
-}
+}
\ No newline at end of file
diff --git a/lib/routes/routes.dart b/lib/routes/routes.dart
index 12bb84b..872d5af 100644
--- a/lib/routes/routes.dart
+++ b/lib/routes/routes.dart
@@ -7,6 +7,8 @@ class Routes {
static const String info = '/info-page';
static const String home = '/home';
+ static const String aiSection = '/ai-section';
+
static const String radars = '/radars';
static const String news = '/news';
static const String infography = '/infography';
@@ -38,4 +40,5 @@ class Routes {
static const String widgetSetting = '/widget-setting';
static const String newStatic = '/new-static';
static const String web = '/web';
-}
+ static const String storyViewer = '/story-viewer';
+}
\ No newline at end of file
diff --git a/lib/services/ai/ai_api_service.dart b/lib/services/ai/ai_api_service.dart
index 695a301..a1631f4 100644
--- a/lib/services/ai/ai_api_service.dart
+++ b/lib/services/ai/ai_api_service.dart
@@ -68,7 +68,6 @@ class AiApiService {
// bytes = reader.result as Uint8List;
}
} else {
- // For other platforms
bytes = await file.main.readAsBytes();
}
if (file.isRecorded) {
@@ -98,20 +97,19 @@ class AiApiService {
Future>> getResponse(http.MultipartRequest req) async {
try {
final response = await http.Client().send(req);
- if (response.statusCode == 400) {
- // Handle 400 response
- final errorResponse = await response.stream.bytesToString();
- final errorJson = jsonDecode(errorResponse);
- throw Exception(errorJson['error'] ?? 'Bad Request');
- } else if (response.statusCode != 200) {
- // Handle other non-200 responses
- throw Exception('Failed to load data');
+ if (response.statusCode != 200) {
+ final errorBody = await response.stream.bytesToString();
+ print(
+ 'body: ${req.url} sc: ${response.statusCode}');
+ print('res: $errorBody');
+ throw Exception(
+ 'sc is ${response.statusCode}');
} else {
return response.stream.asBroadcastStream();
}
} catch (e) {
- // Handle any other errors
- throw Exception('Failed to load data');
+ print('req ${req.url}: $e');
+ throw Exception('Failed to load data due to an exception.');
}
}
}
diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart
index fbac640..6f9ffda 100644
--- a/lib/services/network/request_helper.dart
+++ b/lib/services/network/request_helper.dart
@@ -7,6 +7,8 @@ import 'package:didvan/models/requests/studio.dart';
class RequestHelper {
static const String baseUrl = 'https://api.didvan.app';
static const String baseUrl2 = 'http://opportunity-threat.didvan.com';
+ static const String storiesV1 = '$baseUrl2/api/v1/stories';
+ static const String storyActivity = '$baseUrl2/api/v1/stories/activity';
static const String _baseUserUrl = '$baseUrl/user';
static const String _baseRadarUrl = '$baseUrl/radar';
static const String _baseNewsUrl = '$baseUrl/news';
@@ -18,6 +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 mainPageContent = _baseHomeUrl;
static String searchAll({
diff --git a/lib/services/story_service.dart b/lib/services/story_service.dart
new file mode 100644
index 0000000..0f70a9e
--- /dev/null
+++ b/lib/services/story_service.dart
@@ -0,0 +1,71 @@
+import 'dart:convert';
+import 'package:didvan/main.dart';
+import 'package:didvan/models/story_model.dart';
+import 'package:didvan/providers/user.dart';
+import 'package:didvan/services/network/request.dart';
+import 'package:didvan/services/network/request_helper.dart';
+import 'package:http/http.dart' as http;
+import 'package:provider/provider.dart';
+
+class StoryService {
+ static Future> getStories() async {
+ // دریافت UserProvider از طریق navigatorKey
+ final userProvider =
+ Provider.of(navigatorKey.currentContext!, listen: false);
+ final userId = userProvider.user.id;
+ final token = RequestService.token;
+
+ // اضافه کردن userId به عنوان پارامتر به URL
+ final uri = Uri.parse(RequestHelper.storiesV1).replace(queryParameters: {
+ 'userId': userId.toString(),
+ });
+
+ final response = await http.get(
+ uri, // استفاده از uri جدید
+ headers: {
+ 'Authorization': 'Bearer $token',
+ 'Content-Type': 'application/json',
+ },
+ );
+
+ // ... بقیه کد بدون تغییر باقی میماند
+ if (response.statusCode == 200) {
+ final decodedBody = utf8.decode(response.bodyBytes);
+ final dynamic jsonData = json.decode(decodedBody);
+
+ List storyList;
+
+ if (jsonData is List) {
+ storyList = jsonData;
+ } else if (jsonData is Map &&
+ jsonData.containsKey('content')) {
+ storyList = jsonData['content'];
+ } else {
+ throw Exception('Unexpected JSON structure for stories response');
+ }
+
+ return storyList.map((json) => UserStories.fromApiJson(json)).toList();
+ } else {
+ throw Exception(
+ 'Failed to load stories with status code: ${response.statusCode}');
+ }
+ }
+
+ static Future markStoryAsViewed(int storyId, int storyItemId) async {
+ final userProvider =
+ Provider.of(navigatorKey.currentContext!, listen: false);
+ final userId = userProvider.user.id;
+
+ final service = RequestService(
+ RequestHelper.storyActivity,
+ body: {
+ 'userId': userId,
+ 'storyId': storyId,
+ 'storyItemId': storyItemId,
+ },
+ );
+
+ await service.post();
+ return service.isSuccess;
+ }
+}
\ No newline at end of file
diff --git a/lib/utils/date_time.dart b/lib/utils/date_time.dart
index d6e3387..57cb924 100644
--- a/lib/utils/date_time.dart
+++ b/lib/utils/date_time.dart
@@ -159,26 +159,26 @@ class DateTimeUtils {
double interval = seconds / 31536000;
if (interval > 1) {
- return "${interval.floor()} سال پیش";
+ return "منتشر شده در ${interval.floor()} سال پیش";
}
interval = seconds / 2592000;
if (interval > 1) {
- return "${interval.floor()} ماه پیش";
+ return "منتشر شده در ${interval.floor()} ماه پیش";
}
interval = seconds / 86400;
if (interval > 1) {
if (interval.floor() == 1) return 'دیروز';
- return "${interval.floor()} روز پیش";
+ return "منتشر شده در ${interval.floor()} روز پیش";
}
interval = seconds / 3600;
if (interval > 1) {
- return "${interval.floor()} ساعت پیش";
+ return "منتشر شده در ${interval.floor()} ساعت پیش";
}
interval = seconds / 60;
if (interval > 1) {
- return "${interval.floor()} دقیقه پیش";
+ return "منتشر شده در ${interval.floor()} دقیقه پیش";
}
return 'هم اکنون';
// return seconds.floor().toString() + " ثانیه پیش";
}
-}
+}
\ No newline at end of file
diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart
index f0c6a7d..c8dd299 100644
--- a/lib/views/ai/ai.dart
+++ b/lib/views/ai/ai.dart
@@ -1,5 +1,3 @@
-// ignore_for_file: library_private_types_in_public_api
-
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
@@ -10,22 +8,20 @@ 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/home/home.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:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
class Ai extends StatefulWidget {
const Ai({Key? key}) : super(key: key);
@override
+ // ignore: library_private_types_in_public_api
_AiState createState() => _AiState();
}
@@ -100,81 +96,7 @@ class _AiState extends State {
);
}
- return GridView.builder(
- gridDelegate:
- const SliverGridDelegateWithFixedCrossAxisCount(
- childAspectRatio: 1 / 1,
- crossAxisCount: 2,
- mainAxisSpacing: 8,
- crossAxisSpacing: 8),
- itemCount: tools.length,
- shrinkWrap: true,
- padding: const EdgeInsets.all(16),
- physics:
- const NeverScrollableScrollPhysics(),
- itemBuilder: (context, index) {
- final tool = tools[index];
- return InkWell(
- onTap: () => context
- .read()
- .goToToolBox(tool: tool),
- child: Container(
- padding: const EdgeInsets.all(16),
- decoration: BoxDecoration(
- borderRadius: DesignConfig
- .lowBorderRadius,
- color: Theme.of(context)
- .colorScheme
- .surface,
- border: Border.all(
- color: Theme.of(context)
- .colorScheme
- .border,
- ),
- ),
- child: Column(
- mainAxisAlignment:
- MainAxisAlignment.center,
- children: [
- SvgPicture.network(
- tool.image!,
- width: 48,
- height: 48,
- ),
- const SizedBox(
- width: 8,
- ),
- DidvanText(
- tool.name ?? '',
- style: Theme.of(context)
- .textTheme
- .titleSmall,
- maxLines: 2,
- overflow:
- TextOverflow.ellipsis,
- ),
- Flexible(
- child: DidvanText(
- tool.description ?? '',
- style: Theme.of(context)
- .textTheme
- .bodySmall,
- overflow:
- TextOverflow.ellipsis,
- color: Theme.of(context)
- .colorScheme
- .caption,
- maxLines: 2,
- textAlign:
- TextAlign.center,
- ),
- )
- ],
- ),
- ),
- );
- },
- );
+ return const SizedBox();
},
)
],
@@ -189,45 +111,55 @@ class _AiState extends State {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
InkWell(
- onTap: () => ActionSheetUtils(context)
- .botsDialogSelect(context: context),
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment:
- CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Icon(
- DidvanIcons.caret_down_solid,
- color:
- Theme.of(context).colorScheme.title,
- size: 16,
- ),
- const SizedBox(
- width: 12,
- ),
- 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),
- ),
- ],
- ),
- ),
+ 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)
@@ -242,17 +174,12 @@ class _AiState extends State {
Expanded(
child: Container(
decoration: BoxDecoration(
- boxShadow: DesignConfig
- .defaultShadow,
color: Theme.of(context)
.colorScheme
.surface,
border: Border.all(
- color: Theme.of(context)
- .colorScheme
- .border),
- borderRadius: DesignConfig
- .highBorderRadius),
+ color: const Color.fromARGB(255, 0, 126, 167),width: 1.5),
+ borderRadius: BorderRadius.circular(50)),
child: Row(
children: [
Expanded(
@@ -291,7 +218,7 @@ class _AiState extends State {
InputBorder
.none,
hintText:
- 'بنویسید...',
+ 'بنویسید یا پیام صوتی بگذارید...',
hintStyle: Theme.of(
context)
.textTheme
@@ -318,7 +245,7 @@ class _AiState extends State {
},
enable: false,
icon: Icons
- .attach_file_rounded),
+ .add),
],
))))
],
@@ -332,7 +259,7 @@ class _AiState extends State {
EdgeInsets.fromLTRB(8, 8, 8, 4),
child: FittedBox(
child: DidvanText(
- 'مدلهای هوش مصنوعی میتوانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.',
+ 'مدلهای هوش مصنوعی میتوانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.',style: TextStyle(fontWeight: FontWeight.w600),
),
),
)
@@ -354,7 +281,7 @@ class _AiState extends State {
top: 32,
right: 0,
child: InkWell(
- onTap: () => homeScaffKey.currentState!.openDrawer(),
+ onTap: () => Scaffold.of(context).openDrawer(),
child: Container(
width: 46,
height: 46,
@@ -445,4 +372,4 @@ class _AiState extends State {
},
);
}
-}
+}
\ 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 5171316..a294bed 100644
--- a/lib/views/ai/ai_chat_page.dart
+++ b/lib/views/ai/ai_chat_page.dart
@@ -59,7 +59,6 @@ class _AiChatPageState extends State {
state.chatId = widget.args.chat!.id!;
state.chat = widget.args.chat;
}
- // JsInteropService().showAlert();
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (state.chatId != null) {
state.getAllMessages(state.chatId!).then((value) => Future.delayed(
@@ -429,103 +428,104 @@ 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: 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') &&
diff --git a/lib/views/ai/ai_chat_state.dart b/lib/views/ai/ai_chat_state.dart
index a131527..aed21fc 100644
--- a/lib/views/ai/ai_chat_state.dart
+++ b/lib/views/ai/ai_chat_state.dart
@@ -57,6 +57,7 @@ class AiChatState extends CoreProvier {
await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData(
message: 'خطا در برقراری ارتباط', aLertType: ALertType.error));
update();
+ print("error is ${e.toString()}");
}
Future getChatId() async {
@@ -195,7 +196,6 @@ class AiChatState extends CoreProvier {
if (kIsWeb) {
try {
int startIndex = responseMessgae.indexOf('{{{');
-// + 3 to include the }}} characters
String slicedText =
responseMessgae.substring(startIndex, responseMessgae.length);
diff --git a/lib/views/ai/ai_state.dart b/lib/views/ai/ai_state.dart
index ea6a029..5bc2a02 100644
--- a/lib/views/ai/ai_state.dart
+++ b/lib/views/ai/ai_state.dart
@@ -10,6 +10,7 @@ class AiState extends CoreProvier {
bool loading = true;
List? tools;
+
void getTools() async {
final service = RequestService(
diff --git a/lib/views/ai/create_bot_assistants_page.dart b/lib/views/ai/create_bot_assistants_page.dart
index c9ede74..b0bdd62 100644
--- a/lib/views/ai/create_bot_assistants_page.dart
+++ b/lib/views/ai/create_bot_assistants_page.dart
@@ -61,6 +61,14 @@ class _CreateBotAssistantsPageState extends State {
@override
void initState() {
super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final state = context.read();
+ if (widget.id != null) {
+ state.getAnAssistant(id: widget.id!);
+ } else {
+ state.getImageToolsBots();
+ }
+ });
}
void onConfirm(CreateBotAssistantsState state) async {
diff --git a/lib/views/ai/tool_screen.dart b/lib/views/ai/tool_screen.dart
index 95c547e..09c08bf 100644
--- a/lib/views/ai/tool_screen.dart
+++ b/lib/views/ai/tool_screen.dart
@@ -1,123 +1,19 @@
-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/views/ai/ai_state.dart';
-import 'package:didvan/views/widgets/didvan/text.dart';
-import 'package:didvan/views/widgets/skeleton_image.dart';
+import 'package:didvan/views/ai/widgets/tool_category_view_widget.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
-class ToolScreen extends StatefulWidget {
+class ToolScreen extends StatelessWidget {
const ToolScreen({Key? key}) : super(key: key);
- @override
- State createState() => _ToolScreenState();
-}
-
-class _ToolScreenState extends State {
- late Tools tool = context.read().tool!;
-
- get itemBuilder => null;
-
@override
Widget build(BuildContext context) {
- return SingleChildScrollView(
- physics: const BouncingScrollPhysics(),
- child: Column(
- children: [
- const SizedBox(height: 32),
- SvgPicture.network(
- tool.image!,
- width: 64,
- height: 64,
- ),
- const SizedBox(
- height: 4,
- ),
- DidvanText(
- tool.name!,
- fontSize: 20,
- fontWeight: FontWeight.bold,
- color: Theme.of(context).colorScheme.checkFav,
- ),
- const SizedBox(height: 8),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 32),
- child: DidvanText(
- tool.guide!,
- fontSize: 12,
- color: Theme.of(context).colorScheme.caption,
- textAlign: TextAlign.justify,
- ),
- ),
- const SizedBox(height: 24),
- GridView.builder(
- shrinkWrap: true,
- itemCount: tool.bots!.length,
- physics: const NeverScrollableScrollPhysics(),
- padding: const EdgeInsets.symmetric(horizontal: 32),
- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- childAspectRatio: 1 / 1,
- crossAxisSpacing: 18,
- mainAxisSpacing: 18),
- itemBuilder: (context, index) {
- final bot = tool.bots![index];
- return InkWell(
- onTap: () => Navigator.of(context).pushNamed(Routes.aiChat,
- arguments: AiChatArgs(bot: bot, isTool: tool.bots)),
- child: Container(
- padding: const EdgeInsets.all(12),
- decoration: BoxDecoration(
- borderRadius: DesignConfig.lowBorderRadius,
- border: Border.all(color: const Color(0xffbbbbbb))),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- SkeletonImage(
- imageUrl: bot.image!,
- width: 72,
- height: 72,
- borderRadius: BorderRadius.circular(360),
- ),
- const SizedBox(
- height: 4,
- ),
- DidvanText(
- bot.name ?? '',
- fontSize: 16,
- fontWeight: FontWeight.bold,
- color: Theme.of(context).colorScheme.text,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- if (bot.short != null)
- Column(
- children: [
- const SizedBox(
- height: 4,
- ),
- DidvanText(
- bot.short!,
- fontSize: 12,
- fontWeight: FontWeight.bold,
- color: const Color(0xffA8A6AC),
- // maxLines: 2,
- overflow: TextOverflow.ellipsis,
- ),
- ],
- )
- ],
- ),
- ),
- );
- },
- )
- ],
- ),
- );
+ final Tools? tool = context.watch().tool;
+
+ if (tool == null) {
+ return const Center(child: Text("ابزاری انتخاب نشده است."));
+ }
+ return ToolCategoryViewWidget(tool: tool);
}
-}
+}
\ 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 d8cd8b4..09c391e 100644
--- a/lib/views/ai/widgets/ai_message_bar.dart
+++ b/lib/views/ai/widgets/ai_message_bar.dart
@@ -245,11 +245,13 @@ class _AiMessageBarState extends State {
children: [
Container(
decoration: BoxDecoration(
- boxShadow: DesignConfig.defaultShadow,
- color: Theme.of(context).colorScheme.surface,
- border: Border.all(
- color: Theme.of(context).colorScheme.border),
- borderRadius: DesignConfig.highBorderRadius),
+ boxShadow: DesignConfig.defaultShadow,
+ 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(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
@@ -293,11 +295,12 @@ class _AiMessageBarState extends State {
],
),
MediaQuery.of(context).viewInsets.bottom == 0
- ? const Padding(
- padding: EdgeInsets.fromLTRB(8, 8, 8, 4),
+ ? const Padding(
+ padding: EdgeInsets.fromLTRB(3, 8, 3, 4),
child: DidvanText(
'مدلهای هوش مصنوعی میتوانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.',
- fontSize: 12,
+ fontSize: 11,
+ fontWeight: FontWeight.bold,
),
)
: const SizedBox(
@@ -406,7 +409,7 @@ class _AiMessageBarState extends State {
enable: false,
icon: openAttach || state.file != null
? DidvanIcons.close_regular
- : Icons.attach_file_outlined,
+ : Icons.add,
click: () {
if (_mPlayer!.isPlaying) {
stopPlayer();
@@ -557,11 +560,10 @@ class _AiMessageBarState extends State {
if (kIsWeb) {
Uint8List? bytes = result
- .files.first.bytes; // Access the bytes property
+ .files.first.bytes;
- // 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,
@@ -663,7 +665,7 @@ class _AiMessageBarState extends State {
final blobUrl = html.Url.createObjectUrlFromBlob(blob);
state.file = FilesModel(
- blobUrl, // No need for a file path on web
+ blobUrl,
name: name,
bytes: bytes,
audio: true,
diff --git a/lib/views/ai/widgets/ai_message_bar_ios.dart b/lib/views/ai/widgets/ai_message_bar_ios.dart
index bd7729a..1086e88 100644
--- a/lib/views/ai/widgets/ai_message_bar_ios.dart
+++ b/lib/views/ai/widgets/ai_message_bar_ios.dart
@@ -552,7 +552,7 @@ class _AiMessageBarIOSState extends State {
},
enable: false,
icon: Icons
- .attach_file_rounded,
+ .add,
)
: const SizedBox(),
),
diff --git a/lib/views/ai/widgets/hoshan_drawer.dart b/lib/views/ai/widgets/hoshan_drawer.dart
index 2f13fab..e141bd6 100644
--- a/lib/views/ai/widgets/hoshan_drawer.dart
+++ b/lib/views/ai/widgets/hoshan_drawer.dart
@@ -28,10 +28,15 @@ class HoshanDrawer extends StatefulWidget {
class _HoshanDrawerState extends State {
@override
- initState() {
+ void 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(
@@ -150,7 +155,6 @@ class _HoshanDrawerState extends State {
state: state,
centerEmptyState: false,
emptyState: const EmptyList(),
- // enableEmptyState: state.chats.isEmpty,
placeholder: chatRowPlaceholder(),
placeholderCount: 10,
builder: (context, state, index) {
@@ -286,7 +290,6 @@ class _HoshanDrawerState extends State {
color: enable
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.disabledText),
- // if (!enable) Text('در حال توسعه ...')
],
)
],
diff --git a/lib/views/ai/widgets/tool_category_view_widget.dart b/lib/views/ai/widgets/tool_category_view_widget.dart
new file mode 100644
index 0000000..1d7e867
--- /dev/null
+++ b/lib/views/ai/widgets/tool_category_view_widget.dart
@@ -0,0 +1,178 @@
+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/widgets/didvan/text.dart';
+import 'package:didvan/views/widgets/skeleton_image.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class ToolCategoryViewWidget extends StatelessWidget {
+ final Tools tool;
+ const ToolCategoryViewWidget({Key? key, required this.tool}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ const Color customBackgroundColor = Color.fromRGBO(0, 126, 167, 1.0);
+
+ return SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ padding: const EdgeInsets.only(bottom: 24),
+ child: Column(
+ children: [
+ Container(
+ width: double.infinity,
+ padding: const EdgeInsets.only(top: 32, bottom: 16),
+ child: Column(
+ children: [
+ if (tool.image != null)
+ SvgPicture.network(
+ tool.image!,
+ width: 64,
+ height: 64,
+ placeholderBuilder: (BuildContext context) => const SizedBox(
+ width: 64,
+ height: 64,
+ child: Center(child: CircularProgressIndicator(strokeWidth: 2.0)),
+ ),
+ ),
+ const SizedBox(height: 8),
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
+ margin: const EdgeInsets.symmetric(horizontal: 16),
+ child: DidvanText(
+ tool.name ?? "ابزار هوش مصنوعی",
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ color: Theme.of(context).colorScheme.checkFav,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ],
+ ),
+ ),
+ if (tool.guide != null && tool.guide!.isNotEmpty) ...[
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32),
+ child: DidvanText(
+ tool.guide!,
+ fontSize: 12,
+ color: Theme.of(context).colorScheme.caption,
+ textAlign: TextAlign.justify,
+ ),
+ ),
+ ],
+ const SizedBox(height: 24),
+ if (tool.bots != null && tool.bots!.isNotEmpty)
+ GridView.builder(
+ shrinkWrap: true,
+ itemCount: tool.bots!.length,
+ physics: const NeverScrollableScrollPhysics(),
+ padding: const EdgeInsets.symmetric(horizontal: 32),
+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: 2,
+ childAspectRatio: 1 / 1.1,
+ crossAxisSpacing: 18,
+ mainAxisSpacing: 18),
+ itemBuilder: (context, index) {
+ final bot = tool.bots![index];
+ return InkWell(
+ onTap: () {
+ Navigator.of(context).pushNamed(Routes.aiChat,
+ arguments: AiChatArgs(bot: bot, isTool: tool.bots));
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.only(
+ 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)),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Container(
+ height: 20,
+ decoration: BoxDecoration(
+ color: customBackgroundColor,
+ borderRadius: BorderRadius.only(
+ topLeft: DesignConfig.highBorderRadius.topLeft,
+ topRight: DesignConfig.highBorderRadius.topRight,
+ ),
+ ),
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(12, 8, 12, 12),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ if (bot.image != null)
+ SkeletonImage(
+ imageUrl: bot.image!,
+ width: 70,
+ height: 70,
+ borderRadius: BorderRadius.circular(360),
+ )
+ else
+ 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)),
+ color: customBackgroundColor,
+ ),
+
+ 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,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ),
+ 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,
+ overflow: TextOverflow.ellipsis,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ ] else const Spacer(),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ )
+ else
+ Padding(
+ padding: const EdgeInsets.all(32.0),
+ child: Center(child: DidvanText("هیچ رباتی برای ابزار '${tool.name ?? "انتخابی"}' موجود نیست.")),
+ ),
+ ],
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/ai_section/ai_section_page.dart b/lib/views/ai_section/ai_section_page.dart
new file mode 100644
index 0000000..4342152
--- /dev/null
+++ b/lib/views/ai_section/ai_section_page.dart
@@ -0,0 +1,182 @@
+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/widgets/hoshan_drawer.dart';
+import 'package:didvan/views/ai_section/widgets/ai_section_bnb.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:didvan/views/widgets/hoshan_app_bar.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:didvan/views/ai/history_ai_chat_state.dart';
+import 'package:didvan/views/ai/ai_state.dart';
+import 'package:didvan/views/ai/widgets/tool_category_view_widget.dart';
+
+class ImageGenerationScreen extends StatelessWidget {
+ const ImageGenerationScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final aiState = context.watch();
+
+ if (aiState.tools == null || aiState.tools!.isEmpty) {
+ if (aiState.loading || aiState.appState == AppState.busy) {
+ return const Center(child: CircularProgressIndicator());
+ }
+ return const Center(
+ child: DidvanText('لیست ابزارها هنوز بارگذاری نشده یا خالی است.'));
+ }
+ final Tools imageGenToolCategory = aiState.tools![0];
+ return ToolCategoryViewWidget(tool: imageGenToolCategory);
+ }
+}
+
+class VoiceChatScreen extends StatelessWidget {
+ const VoiceChatScreen({super.key});
+ @override
+ Widget build(BuildContext context) {
+ final aiState = context.watch();
+
+ if (aiState.tools == null || aiState.tools!.isEmpty) {
+ if (aiState.loading || aiState.appState == AppState.busy) {
+ return const Center(child: CircularProgressIndicator());
+ }
+ return const Center(
+ child: DidvanText('لیست ابزارها هنوز بارگذاری نشده یا خالی است.'));
+ }
+ final Tools voiceChatToolCategory = aiState.tools!.last;
+
+ return ToolCategoryViewWidget(tool: voiceChatToolCategory);
+ }
+}
+
+class ChartAnalysisScreen extends StatelessWidget {
+ const ChartAnalysisScreen({super.key});
+ @override
+ Widget build(BuildContext context) {
+ final aiState = context.watch();
+
+ if (aiState.tools == null || aiState.tools!.isEmpty) {
+ if (aiState.loading || aiState.appState == AppState.busy) {
+ return const Center(child: CircularProgressIndicator());
+ }
+ return const Center(
+ child: DidvanText('لیست ابزارها هنوز بارگذاری نشده یا خالی است.'));
+ }
+
+ if (aiState.tools!.length < 2) {
+ return const Center(
+ child:
+ DidvanText('ابزار کافی برای "تحلیل نمودار" وجود ندارد.'));
+ }
+ final Tools chartAnalysisToolCategory = aiState.tools![1];
+
+ return ToolCategoryViewWidget(tool: chartAnalysisToolCategory);
+ }
+}
+
+class TranslationScreen extends StatelessWidget {
+ const TranslationScreen({super.key});
+ @override
+ Widget build(BuildContext context) {
+ final aiState = context.watch();
+
+ if (aiState.tools == null || aiState.tools!.isEmpty) {
+ if (aiState.loading || aiState.appState == AppState.busy) {
+ return const Center(child: CircularProgressIndicator());
+ }
+ return const Center(
+ child: DidvanText('لیست ابزارها هنوز بارگذاری نشده یا خالی است.'));
+ }
+
+ if (aiState.tools!.length < 3) {
+ return const Center(
+ child: DidvanText('ابزار کافی برای "ترجمه" وجود ندارد.'));
+ }
+ final Tools translationToolCategory = aiState.tools![2];
+
+ return ToolCategoryViewWidget(tool: translationToolCategory);
+ }
+}
+
+class AiSectionPage extends StatefulWidget {
+ const AiSectionPage({super.key});
+
+ @override
+ State createState() => _AiSectionPageState();
+}
+
+class _AiSectionPageState extends State
+ with SingleTickerProviderStateMixin {
+ late TabController _tabController;
+ int _currentTabIndex = 2;
+ final GlobalKey _aiSectionScaffoldKey =
+ GlobalKey();
+
+ @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) {
+ historyAiChatState.getBots();
+ }
+
+ final aiState = context.read();
+ aiState.page = 0;
+ if (aiState.tools == null) {
+ aiState.getTools();
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ _tabController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ key: _aiSectionScaffoldKey,
+ appBar: HoshanAppBar(
+ onBack: () {
+ final aiState = context.read();
+ if (aiState.page != 0) {
+ aiState.goToAi();
+ } else {
+ Navigator.of(context).pop();
+ }
+ },
+ withActions: true,
+ ),
+ drawer: HoshanDrawer(scaffKey: _aiSectionScaffoldKey),
+ body: TabBarView(
+ controller: _tabController,
+ physics: const NeverScrollableScrollPhysics(),
+ children: const [
+ ImageGenerationScreen(),
+ VoiceChatScreen(),
+ Ai(),
+ ChartAnalysisScreen(),
+ TranslationScreen(),
+ ],
+ ),
+ bottomNavigationBar: AiSectionBNB(
+ currentTabIndex: _currentTabIndex,
+ onTabChanged: (index) {
+ _tabController.animateTo(index);
+ },
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/ai_section/widgets/ai_section_bnb.dart b/lib/views/ai_section/widgets/ai_section_bnb.dart
new file mode 100644
index 0000000..8fde39f
--- /dev/null
+++ b/lib/views/ai_section/widgets/ai_section_bnb.dart
@@ -0,0 +1,176 @@
+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;
+ final void Function(int index) onTabChanged;
+
+ const AiSectionBNB(
+ {Key? key, required this.currentTabIndex, required this.onTabChanged})
+ : super(key: key);
+
+ @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 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';
+
+ return Container(
+ height: 72,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surface,
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
+ boxShadow: [
+ BoxShadow(
+ color: const Color(0XFF1B3C59).withValues(alpha: 0.15),
+ blurRadius: 8,
+ spreadRadius: 0,
+ offset: const Offset(0, -8),
+ )
+ ],
+ ),
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Row(
+ children: [
+ _AiNavBarItem(
+ isSelected: currentTabIndex == 0,
+ title: 'عکس ساز',
+ selectedIcon: cameraSolid,
+ unselectedIcon: cameraRegular,
+ onTap: () => onTabChanged(0),
+ ),
+ _AiNavBarItem(
+ isSelected: currentTabIndex == 1,
+ title: 'چت صوتی',
+ selectedIcon: micSolid,
+ unselectedIcon: micRegular,
+ onTap: () {
+ NativeWebViewLauncher.openWebView(
+ 'https://www.aisada.ir/app/page1.html');
+ },
+ ),
+ _AiNavBarItem(
+ isSelected: currentTabIndex == 2,
+ title: '',
+ selectedIcon: aiSolid,
+ unselectedIcon: aiRegular,
+ onTap: () => onTabChanged(2),
+ ),
+ _AiNavBarItem(
+ isSelected: currentTabIndex == 3,
+ title: 'جست و جو',
+ selectedIcon: searchSolid,
+ unselectedIcon: searchRegular,
+ 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('ابزار ترجمه در حال بارگذاری است...')),
+ );
+ }
+ },
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+
+class _AiNavBarItem extends StatelessWidget {
+ final VoidCallback onTap;
+ final bool isSelected;
+ final String title;
+ final String selectedIcon;
+ final String unselectedIcon;
+
+ const _AiNavBarItem({
+ Key? key,
+ required this.isSelected,
+ required this.title,
+ required this.selectedIcon,
+ required this.unselectedIcon,
+ required this.onTap,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+
+ final double iconSize = title.isEmpty ? 50.0 : 32.0;
+
+ return Expanded(
+ child: Tooltip(
+ message: title,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.title,
+ borderRadius: DesignConfig.highBorderRadius,
+ boxShadow: DesignConfig.defaultShadow,
+ ),
+ child: GestureDetector(
+ onTap: onTap,
+ child: Container(
+ color: Colors.transparent,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ AnimatedContainer(
+ padding: const EdgeInsets.all(4),
+ duration: DesignConfig.lowAnimationDuration,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle,
+ ),
+ child: SvgPicture.asset(
+ isSelected ? selectedIcon : unselectedIcon,
+ width: iconSize,
+ height: iconSize,
+ ),
+ ),
+ if (title.isNotEmpty)
+ Padding(
+ padding: const EdgeInsets.only(top: 2.0),
+ child: DidvanText(
+ title,
+ style: Theme.of(context).textTheme.bodySmall,
+ color: Theme.of(context).colorScheme.title,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/authentication/authentication_state.dart b/lib/views/authentication/authentication_state.dart
index 5a483ba..013740b 100644
--- a/lib/views/authentication/authentication_state.dart
+++ b/lib/views/authentication/authentication_state.dart
@@ -24,22 +24,30 @@ class AuthenticationState extends CoreProvier {
int get currentPageIndex => _currentPageIndex;
Future confirmUsername() async {
- appState = AppState.isolatedBusy;
- final RequestService service = RequestService(
- RequestHelper.confirmUsername,
- useAutherization: false,
- body: {'username': username},
- );
- await service.post();
- if (service.isSuccess && service.result['confirmed']) {
- appState = AppState.idle;
- currentPageIndex++;
+ appState = AppState.isolatedBusy;
+ final RequestService service = RequestService(
+ RequestHelper.checkHasPassword,
+ useAutherization: false,
+ body: {'username': username},
+ );
+ await service.post();
+
+ if (service.isSuccess) {
+ appState = AppState.idle;
+
+ final bool hasPassword = service.result['hasPassword'];
+
+ if (hasPassword) {
+ currentPageIndex = 1;
} else {
- appState = AppState.failed;
- ActionSheetUtils(navigatorKey.currentContext!)
- .showAlert(AlertData(message: service.errorMessage));
+ currentPageIndex = 2;
}
+ } else {
+ appState = AppState.failed;
+ ActionSheetUtils(navigatorKey.currentContext!)
+ .showAlert(AlertData(message: service.errorMessage));
}
+}
Future login(UserProvider userProvider) async {
appState = AppState.isolatedBusy;
diff --git a/lib/views/authentication/screens/reset_password.dart b/lib/views/authentication/screens/reset_password.dart
index 2e34f7f..134ef53 100644
--- a/lib/views/authentication/screens/reset_password.dart
+++ b/lib/views/authentication/screens/reset_password.dart
@@ -1,3 +1,4 @@
+import 'package:didvan/providers/server_data.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/authentication/authentication_state.dart';
@@ -49,25 +50,29 @@ class _ResetPasswordState extends State {
const Spacer(),
DidvanButton(
onPressed: () async {
- if (!_formKey.currentState!.validate()) return;
- final result = await context
- .read()
- .resetPassword(_password);
- if (!mounted) return;
- Future.delayed(
- Duration.zero,
- () {
- if (result && context.read().isAuthenticated) {
- Navigator.of(context).pop();
- Navigator.of(context).pop();
- return;
- }
- Navigator.of(context).pushNamed(
- Routes.authenticaion,
- arguments: false,
+ if (!_formKey.currentState!.validate()) {
+ return;
+ }
+
+ final authState = context.read();
+ final userProvider = context.read();
+
+ final bool resetSuccess =
+ await authState.resetPassword(_password);
+
+ if (resetSuccess && mounted) {
+ authState.password = _password;
+ final String? token = await authState.login(userProvider);
+
+ if (token != null && mounted) {
+ await ServerDataProvider.getData();
+ Navigator.of(context).pushNamedAndRemoveUntil(
+ Routes.home,
+ (_) => false,
+ arguments: true,
);
- },
- );
+ }
+ }
},
title: 'تغییر رمز عبور',
),
diff --git a/lib/views/authentication/screens/username.dart b/lib/views/authentication/screens/username.dart
index 9058371..3f21d04 100644
--- a/lib/views/authentication/screens/username.dart
+++ b/lib/views/authentication/screens/username.dart
@@ -1,3 +1,4 @@
+import 'package:didvan/utils/extension.dart';
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';
@@ -44,7 +45,7 @@ class _UsernameInputState extends State {
return null;
},
onChanged: (value) {
- state.username = value;
+ state.username = value.convertToEnglishNumber();
},
),
),
diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart
index 586764a..baebb6c 100644
--- a/lib/views/home/home.dart
+++ b/lib/views/home/home.dart
@@ -1,264 +1,276 @@
- // ignore_for_file: deprecated_member_use
+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/notification_message.dart';
+import 'package:didvan/models/view/action_sheet_data.dart';
+import 'package:didvan/providers/theme.dart';
+import 'package:didvan/routes/routes.dart';
+import 'package:didvan/services/app_initalizer.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/widgets/hoshan_drawer.dart';
+import 'package:didvan/views/home/categories/categories_page.dart';
+import 'package:didvan/views/home/main/main_page.dart';
+import 'package:didvan/views/home/home_state.dart';
+import 'package:didvan/views/home/new_statistic/new_statistic.dart';
+import 'package:didvan/views/home/search/search.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:didvan/views/widgets/hoshan_app_bar.dart';
+import 'package:didvan/views/widgets/ink_wrapper.dart';
+import 'package:didvan/views/widgets/logo_app_bar.dart';
+import 'package:didvan/views/widgets/didvan/bnb.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:provider/provider.dart';
+import '../../services/app_home_widget/home_widget_repository.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/main.dart';
- import 'package:didvan/models/notification_message.dart';
- import 'package:didvan/models/view/action_sheet_data.dart';
- import 'package:didvan/providers/theme.dart';
- import 'package:didvan/routes/routes.dart';
- import 'package:didvan/services/app_initalizer.dart';
- import 'package:didvan/utils/action_sheet.dart';
- import 'package:didvan/views/ai/ai.dart';
- import 'package:didvan/views/ai/ai_state.dart';
- import 'package:didvan/views/ai/history_ai_chat_state.dart';
- import 'package:didvan/views/ai/widgets/hoshan_drawer.dart';
- import 'package:didvan/views/home/categories/categories_page.dart';
- import 'package:didvan/views/home/main/main_page.dart';
- import 'package:didvan/views/home/home_state.dart';
- import 'package:didvan/views/home/new_statistic/new_statistic.dart';
- import 'package:didvan/views/home/search/search.dart';
- import 'package:didvan/views/widgets/didvan/text.dart';
- import 'package:didvan/views/widgets/hoshan_app_bar.dart';
- import 'package:didvan/views/widgets/ink_wrapper.dart';
- import 'package:didvan/views/widgets/logo_app_bar.dart';
- import 'package:didvan/views/widgets/didvan/bnb.dart';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:provider/provider.dart';
- import '../../services/app_home_widget/home_widget_repository.dart';
+final GlobalKey homeScaffKey = GlobalKey();
- final GlobalKey homeScaffKey = GlobalKey();
+class Home extends StatefulWidget {
+ final bool? showDialogs;
+ const Home({Key? key, this.showDialogs}) : super(key: key);
- class Home extends StatefulWidget {
- final bool? showDialogs;
- const Home({Key? key, this.showDialogs}) : super(key: key);
+ @override
+ State createState() => _HomeState();
+}
- @override
- State createState() => _HomeState();
+class _HomeState extends State
+ with SingleTickerProviderStateMixin, WidgetsBindingObserver {
+ late final TabController _tabController;
+
+ Future _showDialog(BuildContext context) async {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ ActionSheetUtils(context)
+ .openDialog(
+ data: ActionSheetData(
+ hasDismissButton: false,
+ hasConfirmButton: false,
+ withoutButtonMode: true,
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ InkWrapper(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Icon(DidvanIcons.close_solid, size: 24),
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+
+ SvgPicture.asset(Assets.horizontalLogoWithText, height: 80),
+ const SizedBox(height: 24),
+
+ DidvanText(
+ 'به دیدوان، چشم همیشه باز مدیران خوش آمدید',
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ const SizedBox(height: 24),
+ ],
+ ),
+ ),
+ )
+ .then((value) => ActionSheetUtils(context).openDialog(
+ data: ActionSheetData(
+ backgroundColor: Theme.of(context).colorScheme.background,
+ isBackgroundDropBlur: true,
+ content: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ InkWrapper(
+ onPressed: () {
+ Future.delayed(
+ Duration.zero,
+ () => Navigator.of(context).pop(),
+ );
+ },
+ child: const Icon(
+ DidvanIcons.close_solid,
+ size: 24,
+ ),
+ ),
+ 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لطفا جهت شخصیسازی و استفاده بهتر از برنامه، دستهبندیهای مورد علاقه خود و زمان دریافت اعلانات را انتخاب نمایید.")
+ ],
+ ),
+ onConfirmed: () {
+ Future.delayed(
+ Duration.zero,
+ () =>
+ Navigator.of(navigatorKey.currentContext!).pushNamed(
+ Routes.favouritesStep,
+ arguments: {"toTimer": true},
+ ),
+ );
+ },
+ confrimTitle: 'تایید',
+ ),
+ ));
+ });
}
- class _HomeState extends State
- with SingleTickerProviderStateMixin, WidgetsBindingObserver {
- late final TabController _tabController;
-
- Future _showDialog(BuildContext context) async {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- ActionSheetUtils(context)
- .openDialog(
- data: ActionSheetData(
- content: const DidvanText(
- 'خوش آمدید!\nبرای امنیت بیشتر، رمز عبور خود را تغییر دهید.',
- ),
- onConfirmed: () {
- Future.delayed(
- Duration.zero,
- () => Navigator.of(context)
- .pushNamed(Routes.authenticaion, arguments: true),
- );
- },
- isBackgroundDropBlur: false,
- confrimTitle: 'تغییر رمز عبور',
- dismissTitle: 'بعدا',
- ),
- )
- .then((value) => ActionSheetUtils(context).openDialog(
- data: ActionSheetData(
- backgroundColor: Theme.of(context).colorScheme.background,
- isBackgroundDropBlur: true,
- content: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- InkWrapper(
- onPressed: () {
- Future.delayed(
- Duration.zero,
- () => Navigator.of(context).pop(),
- );
- },
- child: const Icon(
- DidvanIcons.close_solid,
- size: 24,
- ),
- ),
- 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لطفا جهت شخصیسازی و استفاده بهتر از برنامه، دستهبندیهای مورد علاقه خود و زمان دریافت اعلانات را انتخاب نمایید.")
- ],
- ),
- // hasDismissButton: false,
- onConfirmed: () {
- Future.delayed(
- Duration.zero,
- () =>
- Navigator.of(navigatorKey.currentContext!).pushNamed(
- Routes.favouritesStep,
- arguments: {"toTimer": true},
- ),
- );
- },
- confrimTitle: 'تایید',
- ),
- ));
- });
+ @override
+ void initState() {
+ if (widget.showDialogs ?? false) {
+ _showDialog(context);
}
+ // if (!kIsWeb) {
+ // NotificationService.startListeningNotificationEvents();
+ // }
- @override
- void initState() {
- if (widget.showDialogs ?? false) {
- _showDialog(context);
+ 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();
}
- // 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();
+ });
+ if (!kIsWeb) {
+ Future.delayed(Duration.zero, () {
+ HomeWidgetRepository.fetchWidget();
+ HomeWidgetRepository.decideWhereToGo();
+ NotificationMessage? data = HomeWidgetRepository.data;
+ if (data != null) {
+ HomeWidgetRepository.decideWhereToGoNotif();
}
+ AppInitializer.handleCLick(state, _tabController);
});
- if (!kIsWeb) {
- Future.delayed(Duration.zero, () {
- HomeWidgetRepository.fetchWidget();
- HomeWidgetRepository.decideWhereToGo();
- NotificationMessage? data = HomeWidgetRepository.data;
- if (data != null) {
- HomeWidgetRepository.decideWhereToGoNotif();
- }
- AppInitializer.handleCLick(state, _tabController);
- });
- }
+ }
+ state.refresh();
+ context.read().addListener(() {
state.refresh();
- context.read().addListener(() {
- state.refresh();
- });
+ });
- super.initState();
+ super.initState();
+ }
+
+ PreferredSizeWidget getAppBar() {
+ PreferredSizeWidget result = const LogoAppBar();
+ if (context.watch().tabController.index == 2) {
+ result = HoshanAppBar(
+ onBack: () {
+ final state = context.read();
+ if (state.page == 1) {
+ state.goToAi();
+ }
+ },
+ );
}
- PreferredSizeWidget getAppBar() {
- PreferredSizeWidget result = const LogoAppBar();
- if (context.watch().tabController.index == 2) {
- result = HoshanAppBar(
- onBack: () {
- final state = context.read();
- if (state.page == 1) {
- state.goToAi();
+ return result;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ key: homeScaffKey,
+ appBar: getAppBar(),
+ resizeToAvoidBottomInset: false,
+ drawer: context.watch().tabController.index == 2
+ ? HoshanDrawer(
+ scaffKey: homeScaffKey,
+ )
+ : null,
+ body: WillPopScope(
+ onWillPop: () async {
+ if (context.read().tabController.index == 0) {
+ if (kIsWeb) {
+ return true;
}
- },
- );
- }
-
- return result;
- }
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- key: homeScaffKey,
- appBar: getAppBar(),
- resizeToAvoidBottomInset: false,
- drawer: context.watch().tabController.index == 2
- ? HoshanDrawer(
- scaffKey: homeScaffKey,
- )
- : null,
- body: WillPopScope(
- onWillPop: () async {
- if (context.read().tabController.index == 0) {
- if (kIsWeb) {
- return true;
- }
- ActionSheetUtils(context).openDialog(
- data: ActionSheetData(
- content: const DidvanText(
- 'آیا قصد خروج از برنامه را دارید؟',
- ),
- onConfirmed: () {
- SystemChannels.platform.invokeMethod('SystemNavigator.pop');
- },
- isBackgroundDropBlur: true,
- confrimTitle: 'بله',
- dismissTitle: 'خیر',
- ));
- } else if (context.read().tabController.index == 2) {
- switch (context.read().page) {
- case 1:
- context.read().goToAi();
- break;
-
- default:
- _tabController.animateTo(0);
- }
- } else {
- _tabController.animateTo(0);
- }
- return false;
- },
- child: Consumer(
- builder: (context, state, child) => AnimatedCrossFade(
- duration: DesignConfig.lowAnimationDuration,
- crossFadeState: state.filtering
- ? CrossFadeState.showSecond
- : CrossFadeState.showFirst,
- firstChild: SizedBox(
- height: MediaQuery.of(context).size.height,
- width: MediaQuery.of(context).size.width,
- child: TabBarView(
- physics: const NeverScrollableScrollPhysics(),
- controller: _tabController,
- children: const [
- MainPage(),
- CategoriesPage(),
- Ai(),
- NewStatistic(),
- //Statistic(),
- // Bookmarks(),
- ],
- ),
+ ActionSheetUtils(context).openDialog(
+ data: ActionSheetData(
+ content: const DidvanText(
+ 'آیا قصد خروج از برنامه را دارید؟',
+ ),
+ onConfirmed: () {
+ SystemChannels.platform.invokeMethod('SystemNavigator.pop');
+ },
+ isBackgroundDropBlur: true,
+ confrimTitle: 'بله',
+ dismissTitle: 'خیر',
+ ));
+ } else if (context.read().tabController.index == 2) {
+ switch (context.read().page) {
+ case 1:
+ context.read().goToAi();
+ break;
+
+ default:
+ _tabController.animateTo(0);
+ }
+ } else {
+ _tabController.animateTo(0);
+ }
+ return false;
+ },
+ child: Consumer(
+ builder: (context, state, child) => AnimatedCrossFade(
+ duration: DesignConfig.lowAnimationDuration,
+ crossFadeState: state.filtering
+ ? CrossFadeState.showSecond
+ : CrossFadeState.showFirst,
+ firstChild: SizedBox(
+ height: MediaQuery.of(context).size.height,
+ width: MediaQuery.of(context).size.width,
+ child: TabBarView(
+ physics: const NeverScrollableScrollPhysics(),
+ controller: _tabController,
+ children: const [
+ MainPage(),
+ CategoriesPage(),
+ NewStatistic(),
+ ],
),
- secondChild: const SearchPage(),
),
+ secondChild: const SearchPage(),
),
),
- bottomNavigationBar: Consumer(
- builder: (context, state, child) => DidvanBNB(
- currentTabIndex: state.currentPageIndex,
- onTabChanged: (index) {
+ ),
+ bottomNavigationBar: Consumer(
+ builder: (context, state, child) => DidvanBNB(
+ currentTabIndex: state.currentPageIndex,
+ onTabChanged: (index) {
+ if (index < _tabController.length) {
state.currentPageIndex = index;
FocusScope.of(context).unfocus();
state.resetFilters(false);
_tabController.animateTo(index);
- },
- ),
+ }
+ },
),
- );
- }
+ ),
+ );
}
+}
diff --git a/lib/views/home/home_state.dart b/lib/views/home/home_state.dart
index dac6a4f..5214bf4 100644
--- a/lib/views/home/home_state.dart
+++ b/lib/views/home/home_state.dart
@@ -200,7 +200,7 @@ class HomeState extends CoreProvier {
MenuItemType(
label: 'هوشان',
asset: Assets.ai,
- link: 'tab-2',
+ link: Routes.aiSection,
),
MenuItemType(
label: 'فرصت و تهدید',
diff --git a/lib/views/home/main/main_page.dart b/lib/views/home/main/main_page.dart
index f59757d..4f85f62 100644
--- a/lib/views/home/main/main_page.dart
+++ b/lib/views/home/main/main_page.dart
@@ -11,6 +11,7 @@ import 'package:didvan/views/home/main/widgets/banner.dart';
import 'package:didvan/views/home/main/widgets/general_item.dart';
import 'package:didvan/views/home/main/widgets/main_content.dart';
import 'package:didvan/views/home/main/widgets/podcast_item.dart';
+import 'package:didvan/views/home/main/widgets/story_section.dart';
import 'package:didvan/views/widgets/didvan/slider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
@@ -19,7 +20,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:didvan/services/network/request.dart';
import 'package:url_launcher/url_launcher_string.dart';
-import 'package:didvan/views/home/main/widgets/swot_item_card.dart';
+import 'package:didvan/views/home/main/widgets/swot_item_card.dart';
class MainPage extends StatefulWidget {
const MainPage({
@@ -45,161 +46,183 @@ class _MainPageState extends State {
builder: (context, state) => ListView(
padding: const EdgeInsets.symmetric(vertical: 16),
children: [
+ if (state.stories.isNotEmpty) StorySection(stories: state.stories),
+ const SizedBox(height: 12),
// محتوای اصلی صفحه
const MainPageMainContent(),
- // آیتمهای لیستها
- ...List.generate(state.content.lists.length + 1, (index) {
- if (index == 4) {
- return Padding(
- padding: const EdgeInsets.only(top: 32),
- child: Column(
- children: [
+ Builder(builder: (context) {
+ final List pageContent = [];
+ if (state.content != null && state.content!.lists.isNotEmpty) {
+ final lists = state.content!.lists;
+
+ for (int i = 0; i < lists.length; i++) {
+ final currentList = lists[i];
+
+ if (i == 4) {
+ pageContent.add(
Padding(
- padding: const EdgeInsets.only(
- left: 16,
- right: 16,
- bottom: 16,
- top: 28,
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ padding: const EdgeInsets.only(top: 32),
+ child: Column(
children: [
- const InfoTitle(),
- GestureDetector(
- onTap: () => {
- Navigator.of(context).pushNamed(Routes.infography)
- },
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 16,
+ right: 16,
+ bottom: 16,
+ top: 28,
+ ),
child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- DidvanText(
- "همه",
- color: Theme.of(context).colorScheme.primary,
- ),
- Icon(
- DidvanIcons.angle_left_light,
- color: Theme.of(context).colorScheme.primary,
+ const InfoTitle(),
+ GestureDetector(
+ onTap: () => {
+ Navigator.of(context)
+ .pushNamed(Routes.infography)
+ },
+ child: Row(
+ children: [
+ DidvanText(
+ "همه",
+ color: Theme.of(context)
+ .colorScheme
+ .primary,
+ ),
+ Icon(
+ DidvanIcons.angle_left_light,
+ color: Theme.of(context)
+ .colorScheme
+ .primary,
+ )
+ ],
+ ),
)
],
),
- )
+ ),
+ const MainPageBanner(
+ isFirst: false,
+ ),
],
),
),
- const MainPageBanner(
- isFirst: false,
- ),
- ],
- ),
- );
- }
+ );
+ }
- int listIndex = index > 4 ? index - 1 : index;
- if (listIndex >= state.content.lists.length) {
- return const SizedBox.shrink();
- }
- return _MainPageSection(
- list: state.content.lists[listIndex],
- isLast: listIndex == state.content.lists.length - 1,
- );
- }),
+ pageContent.add(_MainPageSection(
+ list: currentList,
+ isLast: i == lists.length - 1,
+ ));
- // کانتینر زیر کل لیستها
- FutureBuilder>(
- future: SwotService.fetchSwotItems(),
- builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.waiting) {
- return const SizedBox(
- height: 10,
- );
- } else if (snapshot.hasError) {
- return const SizedBox(
- height: 10,
- );
- } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
- return const SizedBox(
- height: 10,
- );
+ if (currentList.type == 'startup') {
+ pageContent.add(const _SwotSection());
+ }
}
+ }
+ return Column(children: pageContent);
+ }),
+ ],
+ ),
+ );
+ }
+}
- final items = snapshot.data!;
+class _SwotSection extends StatelessWidget {
+ const _SwotSection();
- return Padding(
- padding: const EdgeInsets.all(0.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder>(
+ future: SwotService.fetchSwotItems(),
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.waiting) {
+ return const SizedBox(
+ height: 10,
+ );
+ } else if (snapshot.hasError) {
+ return const SizedBox(
+ height: 10,
+ );
+ } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
+ return const SizedBox(
+ height: 10,
+ );
+ }
+
+ final items = snapshot.data!;
+
+ return Padding(
+ padding: const EdgeInsets.all(0.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ /// Title Row
+ Padding(
+ padding: const EdgeInsets.only(right: 20, top: 30),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- /// Title Row
- Padding(
- padding: const EdgeInsets.only(right: 20, top: 30),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Row(
- children: [
- SvgPicture.asset(
- "lib/assets/images/features/Saha Solid.svg",
- ),
- const SizedBox(width: 5),
- DidvanText(
- "ماژول فرصت و تهدید",
- style: Theme.of(context).textTheme.titleMedium,
- color: Theme.of(context).colorScheme.title,
- ),
- ],
- ),
- GestureDetector(
- onTap: () {
- AppInitializer.openWebLink(
- navigatorKey.currentContext!,
- 'http://opportunity-threat.didvan.com/?accessToken=${RequestService.token}',
- mode: LaunchMode.inAppWebView,
- );
- },
- child: Padding(
- padding: const EdgeInsets.only(left: 20),
- child: Row(
- children: [
- DidvanText(
- "همه",
- color:
- Theme.of(context).colorScheme.primary,
- ),
- Icon(
- DidvanIcons.angle_left_light,
- color:
- Theme.of(context).colorScheme.primary,
- ),
- ],
- ),
- ),
- ),
- ],
- ),
+ Row(
+ children: [
+ SvgPicture.asset(
+ "lib/assets/images/features/Saha Solid.svg",
+ ),
+ const SizedBox(width: 5),
+ DidvanText(
+ "ماژول فرصت و تهدید",
+ style: Theme.of(context).textTheme.titleMedium,
+ color: Theme.of(context).colorScheme.title,
+ ),
+ ],
),
-
- const SizedBox(height: 16),
-
- /// Swot Items Slider
- DidvanSlider(
- height: 300,
- itemCount: items.length,
- viewportFraction: 0.65,
- itemBuilder: (context, index, realIndex) => Padding(
- padding: const EdgeInsets.symmetric(horizontal: 0.0),
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: SwotItemCard(item: items[index]),
+ GestureDetector(
+ onTap: () {
+ AppInitializer.openWebLink(
+ navigatorKey.currentContext!,
+ 'http://opportunity-threat.didvan.com/?accessToken=${RequestService.token}',
+ mode: LaunchMode.inAppWebView,
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.only(left: 20),
+ child: Row(
+ children: [
+ DidvanText(
+ "همه",
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ Icon(
+ DidvanIcons.angle_left_light,
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ ],
),
),
),
],
),
- );
- },
- )
- ],
- ),
+ ),
+
+ const SizedBox(height: 16),
+
+ /// Swot Items Slider
+ DidvanSlider(
+ height: 300,
+ itemCount: items.length,
+ viewportFraction: 0.65,
+ itemBuilder: (context, index, realIndex) => Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 0.0),
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: SwotItemCard(item: items[index]),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
);
}
}
@@ -362,4 +385,4 @@ class _MainPageSection extends StatelessWidget {
],
);
}
-}
+}
\ No newline at end of file
diff --git a/lib/views/home/main/main_page_state.dart b/lib/views/home/main/main_page_state.dart
index 3582d34..727bb19 100644
--- a/lib/views/home/main/main_page_state.dart
+++ b/lib/views/home/main/main_page_state.dart
@@ -4,40 +4,61 @@ import 'package:didvan/models/home_page_content/home_page_content.dart';
import 'package:didvan/models/requests/infography.dart';
import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/requests/radar.dart';
+import 'package:didvan/models/story_model.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
+import 'package:didvan/services/story_service.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
-// import 'package:url_launcher/url_launcher_string.dart';
class MainPageState extends CoreProvier {
- late MainPageContent content;
+ MainPageContent? content;
int unread = 0;
+ List stories = [];
Future _getMainPageContent() async {
final service = RequestService(RequestHelper.mainPageContent);
-
await service.httpGet();
if (service.isSuccess) {
content = MainPageContent.fromJson(service.result);
unread = service.result['unread'];
- appState = AppState.idle;
- return;
+ } else {
+ throw Exception('Failed to load main page content');
+ }
+ }
+
+ Future _fetchStories() async {
+ try {
+ stories = await StoryService.getStories();
+ // [اضافه شود] تعداد استوری های دریافت شده را چاپ کنید
+ print("Fetched ${stories.length} stories.");
+ } catch (e) {
+ stories = [];
+ // [اضافه شود] خطای رخ داده را چاپ کنید
+ debugPrint("Could not fetch stories: $e");
}
- appState = AppState.failed;
}
void init() {
- Future.delayed(Duration.zero, () {
- _getMainPageContent();
+ Future.delayed(Duration.zero, () async {
+ appState = AppState.busy;
+ try {
+ await Future.wait([
+ _getMainPageContent(),
+ _fetchStories(),
+ ]);
+ appState = AppState.idle;
+ } catch (e) {
+ appState = AppState.failed;
+ }
});
}
void markChangeHandler(String type, int id, bool value) {
- content.lists
+ content?.lists
.firstWhere((element) => element.type == type)
.contents
.firstWhere((element) => element.id == id)
@@ -109,4 +130,4 @@ class MainPageState extends CoreProvier {
}
Navigator.of(navigatorKey.currentContext!).pushNamed(link, arguments: args);
}
-}
+}
\ No newline at end of file
diff --git a/lib/views/home/main/widgets/banner.dart b/lib/views/home/main/widgets/banner.dart
index 79c946e..545f287 100644
--- a/lib/views/home/main/widgets/banner.dart
+++ b/lib/views/home/main/widgets/banner.dart
@@ -5,7 +5,6 @@ import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
-// import 'package:url_launcher/url_launcher_string.dart';
class MainPageBanner extends StatelessWidget {
final bool isFirst;
@@ -16,7 +15,7 @@ class MainPageBanner extends StatelessWidget {
final state = context.read();
return DidvanSlider(
itemBuilder: (context, index, realIndex) {
- final item = state.content.banners[isFirst ? 0 : 1][index];
+ final item = state.content!.banners[isFirst ? 0 : 1][index];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: GestureDetector(
@@ -30,7 +29,7 @@ class MainPageBanner extends StatelessWidget {
),
);
},
- itemCount: state.content.banners[isFirst ? 0 : 1].length,
+ itemCount: state.content!.banners[isFirst ? 0 : 1].length,
viewportFraction: 1,
enableIndicator: true,
height: (MediaQuery.of(context).size.width - 8) * 9.0 / 16.0,
diff --git a/lib/views/home/main/widgets/story_section.dart b/lib/views/home/main/widgets/story_section.dart
new file mode 100644
index 0000000..d001061
--- /dev/null
+++ b/lib/views/home/main/widgets/story_section.dart
@@ -0,0 +1,124 @@
+import 'package:cached_network_image/cached_network_image.dart';
+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;
+
+ const StorySection({super.key, required this.stories});
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 110.0,
+ child: ListView.builder(
+ padding: const EdgeInsets.symmetric(horizontal: 8.0),
+ scrollDirection: Axis.horizontal,
+ reverse: true,
+ itemCount: stories.length,
+ itemBuilder: (BuildContext context, int index) {
+ final userStories = stories[index];
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 4.0),
+ child: _StoryCircle(
+ userStories: userStories,
+ onTap: () {
+ Navigator.of(context).pushNamed(
+ Routes.storyViewer,
+ arguments: {
+ 'stories': stories,
+ 'tappedIndex': index,
+ },
+ );
+ },
+ ),
+ );
+ },
+ ),
+ );
+ }
+}
+
+// lib/views/home/main/widgets/story_section.dart
+
+class _StoryCircle extends StatelessWidget {
+ final UserStories userStories;
+ final VoidCallback onTap;
+
+ const _StoryCircle({required this.userStories, required this.onTap});
+
+ @override
+ Widget build(BuildContext context) {
+ final allStoriesViewed = ValueNotifier(
+ userStories.stories.every((story) => story.isViewed.value));
+
+ for (var story in userStories.stories) {
+ story.isViewed.addListener(() {
+ allStoriesViewed.value =
+ userStories.stories.every((s) => s.isViewed.value);
+ });
+ }
+
+ return InkWell(
+ onTap: onTap,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ValueListenableBuilder(
+ valueListenable: allStoriesViewed,
+ builder: (context, isViewed, child) {
+ return Container(
+ width: 80.0,
+ height: 80.0,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ gradient: isViewed
+ ? const LinearGradient(
+ colors: [Color.fromARGB(255, 184, 184, 184),Color.fromARGB(255, 184, 184, 184)],
+ )
+ : const LinearGradient(
+ colors: [ Color.fromARGB(255, 27, 60, 79), Color.fromARGB(255, 27, 60, 79)],
+ begin: Alignment.topRight,
+ end: Alignment.bottomLeft,
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(4.0),
+ child: Container(
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ shape: BoxShape.circle,
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(4.0),
+ child: CircleAvatar(
+ backgroundColor: const Color.fromARGB(255, 230, 242, 246),
+ child: ClipOval(
+ child: SvgPicture.asset(
+ "lib/assets/icons/MACRO_TRENDS.svg",
+ fit: BoxFit.cover,
+ width: 50.0,
+ height: 50.0,
+ placeholderBuilder: (context) => const CircularProgressIndicator(strokeWidth: 2.0),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ const SizedBox(height: 6.0),
+ Text(
+ userStories.user.name,
+ style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.w500),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ ),
+ );
+ }
+}
\ 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
new file mode 100644
index 0000000..7e388b3
--- /dev/null
+++ b/lib/views/story_viewer/story_viewer_page.dart
@@ -0,0 +1,406 @@
+import 'package:cached_network_image/cached_network_image.dart';
+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: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;
+
+ const StoryViewerPage({
+ super.key,
+ required this.stories,
+ required this.tappedIndex,
+ });
+
+ @override
+ State createState() => _StoryViewerPageState();
+}
+
+class _StoryViewerPageState extends State {
+ late PageController _pageController;
+
+ @override
+ void initState() {
+ super.initState();
+ _pageController = PageController(initialPage: widget.tappedIndex);
+ }
+
+ @override
+ void dispose() {
+ _pageController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Directionality(
+ textDirection: TextDirection.ltr,
+ child: PageView.builder(
+ controller: _pageController,
+ itemCount: widget.stories.length,
+ itemBuilder: (context, index) {
+ final userStories = widget.stories[index];
+ return UserStoryViewer(
+ key: ValueKey(userStories.user.name + index.toString()),
+ userStories: userStories,
+ onComplete: () {
+ if (index < widget.stories.length - 1) {
+ _pageController.nextPage(
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeIn,
+ );
+ } else {
+ Navigator.of(context).pop();
+ }
+ },
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
+
+class UserStoryViewer extends StatefulWidget {
+ final UserStories userStories;
+ final VoidCallback onComplete;
+
+ const UserStoryViewer({
+ super.key,
+ required this.userStories,
+ required this.onComplete,
+ });
+
+ @override
+ State createState() => _UserStoryViewerState();
+}
+
+class _UserStoryViewerState extends State
+ with SingleTickerProviderStateMixin {
+ late AnimationController _animationController;
+ VideoPlayerController? _videoController;
+ int _currentStoryIndex = 0;
+ bool _isLongPressing = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _animationController = AnimationController(vsync: this);
+ _loadStory(story: widget.userStories.stories.first);
+
+ _animationController.addStatusListener((status) {
+ if (status == AnimationStatus.completed) {
+ _nextStory();
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ _animationController.dispose();
+ _videoController?.dispose();
+ super.dispose();
+ }
+
+ void _loadStory({required StoryItem story}) {
+ if (!story.isViewed.value) {
+ StoryService.markStoryAsViewed(widget.userStories.id, story.id);
+ story.isViewed.value = true;
+ }
+
+ _animationController.stop();
+ _animationController.reset();
+ _videoController?.dispose();
+ _videoController = null;
+
+ switch (story.media) {
+ case MediaType.image:
+ case MediaType.gif:
+ _animationController.duration = story.duration;
+ _animationController.forward();
+ break;
+ case MediaType.video:
+ _videoController =
+ VideoPlayerController.networkUrl(Uri.parse(story.url))
+ ..initialize().then((_) {
+ if (mounted) {
+ setState(() {
+ if (_videoController!.value.isInitialized) {
+ // Check for a valid duration
+ if (_videoController!.value.duration > Duration.zero) {
+ _animationController.duration =
+ _videoController!.value.duration;
+ } else {
+ // Fallback to default duration if video duration is invalid
+ _animationController.duration = story.duration;
+ }
+ _videoController!.play();
+ _animationController.forward();
+ } else {
+ // Fallback for failed initialization
+ _animationController.duration = story.duration;
+ _animationController.forward();
+ }
+ });
+ }
+ });
+ break;
+ }
+ setState(() {});
+ }
+
+ void _nextStory() {
+ if (_currentStoryIndex < widget.userStories.stories.length - 1) {
+ setState(() {
+ _currentStoryIndex++;
+ });
+ _loadStory(story: widget.userStories.stories[_currentStoryIndex]);
+ } else {
+ widget.onComplete();
+ }
+ }
+
+ void _previousStory() {
+ if (_currentStoryIndex > 0) {
+ setState(() {
+ _currentStoryIndex--;
+ });
+ _loadStory(story: widget.userStories.stories[_currentStoryIndex]);
+ }
+ }
+
+ void _pauseStory() {
+ _animationController.stop();
+ _videoController?.pause();
+ }
+
+ void _resumeStory() {
+ _animationController.forward();
+ _videoController?.play();
+ }
+
+ void _handleTap(TapUpDetails details) {
+ if (_isLongPressing) {
+ _isLongPressing = false;
+ _resumeStory();
+ return;
+ }
+ final double screenWidth = MediaQuery.of(context).size.width;
+ final double dx = details.globalPosition.dx;
+
+ if (dx > screenWidth / 2) {
+ _nextStory();
+ } else {
+ _previousStory();
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final story = widget.userStories.stories[_currentStoryIndex];
+ return Scaffold(
+ backgroundColor: Colors.black,
+ body: GestureDetector(
+ onTapUp: _handleTap,
+ onLongPressStart: (_) {
+ _isLongPressing = true;
+ _pauseStory();
+ },
+ onLongPressEnd: (_) {
+ _isLongPressing = false;
+ _resumeStory();
+ },
+ child: Stack(
+ fit: StackFit.expand,
+ children: [
+ _buildMediaViewer(story),
+ _buildStoryHeader(),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildMediaViewer(StoryItem story) {
+ switch (story.media) {
+ case MediaType.image:
+ return CachedNetworkImage(
+ imageUrl: story.url,
+ fit: BoxFit.cover,
+ width: double.infinity,
+ height: double.infinity);
+ case MediaType.gif:
+ return Image.network(story.url,
+ fit: BoxFit.cover, width: double.infinity, height: double.infinity);
+ case MediaType.video:
+ if (_videoController?.value.isInitialized ?? false) {
+ return FittedBox(
+ fit: BoxFit.cover,
+ child: SizedBox(
+ width: _videoController!.value.size.width,
+ height: _videoController!.value.size.height,
+ child: VideoPlayer(_videoController!)));
+ }
+ return const Center(child: CircularProgressIndicator());
+ }
+ }
+
+ Widget _buildStoryHeader() {
+ return Positioned(
+ top: 35.0,
+ left: 10.0,
+ right: 10.0,
+ child: Column(
+ children: [
+ Directionality(
+ textDirection: TextDirection.ltr,
+ child: Row(
+ children: widget.userStories.stories
+ .asMap()
+ .map((i, e) {
+ return MapEntry(
+ i,
+ _AnimatedBar(
+ animationController: _animationController,
+ position: i,
+ currentIndex: _currentStoryIndex,
+ ),
+ );
+ })
+ .values
+ .toList(),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10.0),
+ child: _UserInfo(user: widget.userStories.user),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class _AnimatedBar extends StatelessWidget {
+ final AnimationController animationController;
+ final int position;
+ final int currentIndex;
+
+ const _AnimatedBar({
+ required this.animationController,
+ required this.position,
+ required this.currentIndex,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Flexible(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 1.5),
+ child: LayoutBuilder(
+ builder: (context, constraints) {
+ return Stack(
+ children: [
+ Container(
+ height: 6.0,
+ width: double.infinity,
+ decoration: BoxDecoration(
+ color: position < currentIndex
+ ? Colors.white
+ : Colors.white.withOpacity(0.5),
+ border: Border.all(color: Colors.black26, width: 0.8),
+ borderRadius: BorderRadius.circular(30.0),
+ ),
+ ),
+ if (position == currentIndex)
+ Align(
+ alignment: Alignment.centerLeft,
+ child: AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Container(
+ height: 6.0,
+ width:
+ constraints.maxWidth * animationController.value,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(30.0),
+ ),
+ );
+ },
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
+
+class _UserInfo extends StatelessWidget {
+ final User user;
+
+ const _UserInfo({required this.user});
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ children: [
+ CircleAvatar(
+ radius: 20.0,
+ backgroundColor: Colors.grey[300],
+ child: ClipOval(
+ child: Padding(
+ padding: const EdgeInsets.all(5.0),
+ child: SvgPicture.asset(
+ user.profileImageUrl,
+ width: 40.0,
+ height: 40.0,
+ fit: BoxFit.cover,
+ placeholderBuilder: (context) => const CircularProgressIndicator(
+ strokeWidth: 2.0,
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 10.0),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ user.name,
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 18.0,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ DidvanText(
+ DateTimeUtils.momentGenerator(user.createdAt),
+ style: const TextStyle(
+ color: Colors.white70,
+ fontSize: 14.0,
+ ),
+ ),
+ ],
+ ),
+ ),
+ IconButton(
+ icon: const Icon(Icons.close, size: 30.0, color: Colors.white),
+ onPressed: () => Navigator.of(context).pop(),
+ ),
+ ],
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/views/widgets/ai_banner.dart b/lib/views/widgets/ai_banner.dart
index 2efb873..063087a 100644
--- a/lib/views/widgets/ai_banner.dart
+++ b/lib/views/widgets/ai_banner.dart
@@ -15,8 +15,8 @@ class AiBanner extends StatelessWidget {
NativeWebViewLauncher.openWebView(
'https://www.aisada.ir/app/page1.html');
},
- child: Padding(
- padding: const EdgeInsets.only(top: 20),
+ child: const Padding(
+ padding: EdgeInsets.only(top: 20),
child: Stack(
children: [
Icon(Icons.insert_comment_sharp),
diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart
index 9569ef5..9f557a0 100644
--- a/lib/views/widgets/didvan/bnb.dart
+++ b/lib/views/widgets/didvan/bnb.dart
@@ -1,6 +1,7 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
+import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
@@ -58,18 +59,18 @@ class DidvanBNB extends StatelessWidget {
onTap: () => onTabChanged(1),
),
_NavBarItem(
- isSelected: currentTabIndex == 2,
+ isSelected: false,
title: 'هوشان',
selectedIcon: DidvanIcons.ai_solid,
unselectedIcon: DidvanIcons.ai_regular,
- onTap: () => onTabChanged(2),
+ onTap: () => Navigator.of(context).pushNamed(Routes.aiSection),
),
_NavBarItem(
- isSelected: currentTabIndex == 3,
+ isSelected: currentTabIndex == 2,
title: 'آمار و داده',
selectedIcon: DidvanIcons.stats__solid,
unselectedIcon: DidvanIcons.stats__light,
- onTap: () => onTabChanged(3),
+ onTap: () => onTabChanged(2),
),
],
),
@@ -145,4 +146,4 @@ class _NavBarItem extends StatelessWidget {
),
);
}
-}
+}
\ No newline at end of file
diff --git a/lib/views/widgets/didvan/text_field.dart b/lib/views/widgets/didvan/text_field.dart
index 632e7e2..78c3a86 100644
--- a/lib/views/widgets/didvan/text_field.dart
+++ b/lib/views/widgets/didvan/text_field.dart
@@ -20,7 +20,7 @@ class DidvanTextField extends StatefulWidget {
final bool acceptSpace;
final String? Function(String value)? validator;
final TextInputType? textInputType;
- final TextInputAction? textInputAction; // پارامتر جدید
+ final TextInputAction? textInputAction;
final bool disableBorders;
final bool isSmall;
final int? maxLine;
@@ -38,7 +38,7 @@ class DidvanTextField extends StatefulWidget {
this.initialValue,
this.validator,
this.textInputType,
- this.textInputAction, // اضافه کردن به سازنده
+ this.textInputAction,
this.textAlign,
this.obsecureText = false,
this.autoFocus = false,
@@ -111,13 +111,13 @@ class _DidvanTextFieldState extends State {
inputFormatters: [
if (!widget.acceptSpace)
FilteringTextInputFormatter.allow(
- RegExp("[0-9a-zA-Z]")),
+ RegExp("[0-9a-zA-Z\u0600-\u06FF]")),
],
autofocus: widget.autoFocus,
obscureText: _hideContent,
textAlign: widget.textAlign ?? TextAlign.start,
keyboardType: widget.textInputType,
- textInputAction: widget.textInputAction, // پاس دادن به TextFormField
+ textInputAction: widget.textInputAction,
focusNode: _focusNode,
controller: _controller,
onFieldSubmitted: widget.onSubmitted,
diff --git a/pubspec.yaml b/pubspec.yaml
index b7a373a..fc543d0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -143,6 +143,7 @@ flutter:
- lib/assets/icons/
- lib/assets/animations/
- lib/assets/js/
+ - lib/assets/icons/houshanNav/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.