diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 791d5a1..c7c4c6a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,7 +17,6 @@ android:icon="@mipmap/ic_launcher" android:label="Didvan" android:requestLegacyExternalStorage="true" - android:usesCleartextTraffic="true"> - - - - - - - - - - - - @@ -110,19 +92,23 @@ android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> - - - + + + + + + + + + + + + + with WidgetsBindingObserver { + late AppLinks _appLinks; + StreamSubscription? _linkSubscription; @override void didChangeDependencies() { super.didChangeDependencies(); @@ -104,17 +107,46 @@ class _DidvanState extends State with WidgetsBindingObserver { void initState() { WidgetsBinding.instance.addObserver(this); super.initState(); + _initDeepLinks(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); + _linkSubscription?.cancel(); if (MediaService.currentPodcast != null) { MediaService.audioPlayer.dispose(); } super.dispose(); } + Future _initDeepLinks() async { + _appLinks = AppLinks(); + + // بررسی لینک اولیه در زمان باز شدن اپلیکیشن + final initialUri = await _appLinks.getInitialLink(); + if (initialUri != null) { + _navigateTo(initialUri); + } + + // گوش دادن به لینک‌های جدید زمانی که اپلیکیشن در حال اجراست + _linkSubscription = _appLinks.uriLinkStream.listen((uri) { + _navigateTo(uri); + }); + } + + /// تابع کمکی برای ناوبری + void _navigateTo(Uri uri) { + if (mounted) { + String path = uri.path; + if (uri.fragment.isNotEmpty) { + path = "/${uri.fragment}"; + } + navigatorKey.currentState?.pushNamed(path); + } + } + + bool b = true; @override diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 617a4dd..7ba82ad 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -1,6 +1,8 @@ // ignore_for_file: avoid_print import 'package:didvan/models/ai/ai_chat_args.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/views/ai/ai_chat_page.dart'; import 'package:didvan/views/ai/ai_chat_state.dart'; @@ -74,6 +76,35 @@ import '../views/notification_time/notification_time.dart'; class RouteGenerator { static Route generateRoute(RouteSettings settings) { + final uri = Uri.parse(settings.name ?? ''); + if (uri.pathSegments.isNotEmpty) { + if (uri.pathSegments.first == 'news' && uri.pathSegments.length > 1) { + final id = int.tryParse(uri.pathSegments[1]); + if (id != null) { + return _createRoute( + ChangeNotifierProvider( + create: (context) => NewsDetailsState(), + child: NewsDetails( + pageData: {'id': id, 'args': const NewsRequestArgs(page: 0)}, + ), + ), + ); + } + } + if (uri.pathSegments.first == 'radar' && uri.pathSegments.length > 1) { + final id = int.tryParse(uri.pathSegments[1]); + if (id != null) { + return _createRoute( + ChangeNotifierProvider( + create: (context) => RadarDetailsState(), + child: RadarDetails( + pageData: {'id': id, 'args': const RadarRequestArgs(page: 0)}, + ), + ), + ); + } + } + } if (!kIsWeb) { HomeWidget.saveWidgetData("cRoute", settings.name!); } diff --git a/lib/services/network/request.dart b/lib/services/network/request.dart index 3443318..bad60ff 100644 --- a/lib/services/network/request.dart +++ b/lib/services/network/request.dart @@ -61,6 +61,7 @@ class RequestService { _requestBody == null ? null : jsonEncode(_requestBody); Future httpGet() async { + log('req is: $url', name: 'RequestService'); try { final response = await http .get( diff --git a/lib/views/authentication/authentication.dart b/lib/views/authentication/authentication.dart index 46e0afe..9f72ffb 100644 --- a/lib/views/authentication/authentication.dart +++ b/lib/views/authentication/authentication.dart @@ -46,9 +46,14 @@ class _AuthenticationState extends State { body: Consumer( builder: (context, state, child) => WillPopScope( onWillPop: () async { - if (state.currentPageIndex == 0) { + if (state.currentPageIndex == 0) { return true; } + // Check if on OTP screen and no password exists + if (state.currentPageIndex == 2 && !state.hasPassword) { + state.currentPageIndex = 0; // Go back to username screen + return false; + } state.currentPageIndex--; return false; }, diff --git a/lib/views/authentication/authentication_state.dart b/lib/views/authentication/authentication_state.dart index 00de5d3..0ad37e7 100644 --- a/lib/views/authentication/authentication_state.dart +++ b/lib/views/authentication/authentication_state.dart @@ -15,6 +15,7 @@ class AuthenticationState extends CoreProvier { String password = ''; String _verificationCode = ''; bool isShowCustomDialog = true; + bool hasPassword = true; set currentPageIndex(int value) { _currentPageIndex = value; @@ -36,6 +37,7 @@ class AuthenticationState extends CoreProvier { appState = AppState.idle; final bool hasPassword = service.result['hasPassword']; + this.hasPassword = hasPassword; if (hasPassword) { currentPageIndex = 1; diff --git a/lib/views/authentication/screens/reset_password.dart b/lib/views/authentication/screens/reset_password.dart index 134ef53..4a655c0 100644 --- a/lib/views/authentication/screens/reset_password.dart +++ b/lib/views/authentication/screens/reset_password.dart @@ -22,6 +22,7 @@ class _ResetPasswordState extends State { @override Widget build(BuildContext context) { + final authState = context.watch(); return Form( key: _formKey, child: AuthenticationLayout( @@ -74,7 +75,7 @@ class _ResetPasswordState extends State { } } }, - title: 'تغییر رمز عبور', + title: authState.hasPassword ? 'تغییر رمز عبور' : 'تایید رمز عبور', ), const SizedBox( height: 48, diff --git a/lib/views/authentication/widgets/authentication_app_bar.dart b/lib/views/authentication/widgets/authentication_app_bar.dart index eccc62a..fb50158 100644 --- a/lib/views/authentication/widgets/authentication_app_bar.dart +++ b/lib/views/authentication/widgets/authentication_app_bar.dart @@ -1,3 +1,5 @@ +// lib/views/authentication/widgets/authentication_app_bar.dart + import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/views/authentication/authentication_state.dart'; @@ -16,14 +18,26 @@ class AuthenticationAppBar extends StatelessWidget { return Row( children: [ DidvanIconButton( - icon: DidvanIcons.back_regular, - onPressed: () { - if (state.currentPageIndex == 0) { - Navigator.of(context).pop(); - return; - } - state.currentPageIndex--; - }), + icon: DidvanIcons.back_regular, + onPressed: () { + // اگر در صفحه اول باشیم، از صفحه احراز هویت خارج می‌شویم + if (state.currentPageIndex == 0) { + Navigator.of(context).pop(); + return; + } + + // ** منطق جدید برای بازگشت از صفحه OTP ** + // اگر کاربر رمز عبور نداشته و در صفحه OTP باشد + if (state.currentPageIndex == 2 && !state.hasPassword) { + // او را به صفحه اول (ورود شماره) برمی‌گردانیم + state.currentPageIndex = 0; + return; + } + + // در غیر این صورت، به صفحه قبلی برمی‌گردیم + state.currentPageIndex--; + }, + ), const SizedBox( width: 4, ), @@ -36,4 +50,4 @@ class AuthenticationAppBar 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 95e1f4a..08aefbf 100644 --- a/lib/views/home/main/main_page_state.dart +++ b/lib/views/home/main/main_page_state.dart @@ -43,14 +43,13 @@ class MainPageState extends CoreProvier { try { swotItems = await SwotService.fetchSwotItems(); } catch (e) { - // در صورت بروز خطا، می‌توانید اینجا آن را مدیریت کنید + } } Future _fetchStories() async { try { stories = await StoryService.getStories(); - // ignore: avoid_print print("Fetched ${stories.length} stories."); } catch (e) { stories = []; diff --git a/lib/views/home/main/widgets/story_section.dart b/lib/views/home/main/widgets/story_section.dart index e6d84a5..9d26a91 100644 --- a/lib/views/home/main/widgets/story_section.dart +++ b/lib/views/home/main/widgets/story_section.dart @@ -23,14 +23,11 @@ class StorySection extends StatelessWidget { child: _StoryCircle( userStories: userStories, onTap: () { - bool allStoriesViewed = stories.every((userStories) => - userStories.stories.every((story) => story.isViewed.value)); - Navigator.of(context).pushNamed( Routes.storyViewer, arguments: { 'stories': stories, - 'tappedIndex': allStoriesViewed ? 0 : index, + 'tappedIndex': index, }, ); }, @@ -50,9 +47,11 @@ class _StoryCircle extends StatelessWidget { @override Widget build(BuildContext context) { + // ValueNotifier برای پیگیری وضعیت مشاهده همه استوری‌ها final allStoriesViewed = ValueNotifier( userStories.stories.every((story) => story.isViewed.value)); + // افزودن Listener به هر استوری for (var story in userStories.stories) { story.isViewed.addListener(() { allStoriesViewed.value = @@ -65,6 +64,7 @@ class _StoryCircle extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + // استفاده از ValueListenableBuilder برای تغییر رنگ حاشیه ValueListenableBuilder( valueListenable: allStoriesViewed, builder: (context, isViewed, child) { @@ -104,7 +104,7 @@ class _StoryCircle extends StatelessWidget { child: ClipOval( child: Image.asset( userStories.user - .profileImageUrl, + .profileImageUrl, // Assuming this is a local asset fit: BoxFit.cover, width: 50.0, height: 50.0, @@ -131,4 +131,4 @@ class _StoryCircle extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/views/story_viewer/story_viewer_page.dart b/lib/views/story_viewer/story_viewer_page.dart index 33bd89e..7fe14e5 100644 --- a/lib/views/story_viewer/story_viewer_page.dart +++ b/lib/views/story_viewer/story_viewer_page.dart @@ -92,14 +92,14 @@ class _UserStoryViewerState extends State super.initState(); _animationController = AnimationController(vsync: this); - final allStoriesInGroupViewed = - widget.userStories.stories.every((story) => story.isViewed.value); + // final allStoriesInGroupViewed = + // widget.userStories.stories.every((story) => story.isViewed.value); - if (allStoriesInGroupViewed) { - for (final story in widget.userStories.stories) { - story.isViewed.value = false; - } - } + // if (allStoriesInGroupViewed) { + // for (final story in widget.userStories.stories) { + // story.isViewed.value = false; + // } + // } _currentStoryIndex = widget.userStories.stories.indexWhere((story) => !story.isViewed.value); @@ -244,12 +244,12 @@ class _UserStoryViewerState extends State return CachedNetworkImage( placeholder: (context, url) => const ShimmerPlaceholder(), imageUrl: story.url, - fit: BoxFit.cover, + fit: BoxFit.fill, width: double.infinity, height: double.infinity); case MediaType.gif: return Image.network(story.url, - fit: BoxFit.cover, width: double.infinity, height: double.infinity); + fit: BoxFit.fill, width: double.infinity, height: double.infinity); case MediaType.video: if (_videoController?.value.isInitialized ?? false) { return FittedBox( diff --git a/lib/views/widgets/didvan/text_field.dart b/lib/views/widgets/didvan/text_field.dart index 33f739e..b538666 100644 --- a/lib/views/widgets/didvan/text_field.dart +++ b/lib/views/widgets/didvan/text_field.dart @@ -4,6 +4,7 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/utils/extension.dart'; import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -108,49 +109,51 @@ class _DidvanTextFieldState extends State { ? TextDirection.ltr : TextDirection.rtl, child: Padding( - padding: const EdgeInsets.fromLTRB(8,8,0,15), - child: TextFormField( - inputFormatters: [ - if (!widget.acceptSpace) - FilteringTextInputFormatter.allow( - RegExp("[0-9a-zA-Z\u0600-\u06FF]")), - ], - autofocus: widget.autoFocus, - obscureText: _hideContent, - textAlign: widget.textAlign ?? TextAlign.start, - keyboardType: widget.textInputType, - textInputAction: widget.textInputAction, - focusNode: _focusNode, - controller: _controller, - onFieldSubmitted: widget.onSubmitted, - onChanged: _onChanged, - validator: _validator, - maxLines: _hideContent ? 1 : widget.maxLine, - minLines: widget.minLine, - maxLength: widget.maxLength, - obscuringCharacter: '*', - buildCounter: widget.showLen - ? null - : (context, - {required currentLength, - required isFocused, - required maxLength}) => - const SizedBox(), - style: (widget.isSmall - ? Theme.of(context).textTheme.bodySmall! - : Theme.of(context).textTheme.bodyMedium!) - .copyWith( - fontFamily: DesignConfig.fontFamily.padRight(3)), - decoration: InputDecoration( - suffixIcon: _suffixBuilder(), - enabled: widget.enabled, - border: InputBorder.none, - hintText: widget.hintText, - errorStyle: const TextStyle(height: 0.01), - hintStyle: (widget.isSmall + padding: const EdgeInsets.fromLTRB(kIsWeb?8:8,kIsWeb?4:8,kIsWeb?8:0,kIsWeb?0:8), + child: Center( + child: TextFormField( + inputFormatters: [ + if (!widget.acceptSpace) + FilteringTextInputFormatter.allow( + RegExp("[0-9a-zA-Z\u0600-\u06FF]")), + ], + autofocus: widget.autoFocus, + obscureText: _hideContent, + textAlign: widget.textAlign ?? TextAlign.start, + keyboardType: widget.textInputType, + textInputAction: widget.textInputAction, + focusNode: _focusNode, + controller: _controller, + onFieldSubmitted: widget.onSubmitted, + onChanged: _onChanged, + validator: _validator, + maxLines: _hideContent ? 1 : widget.maxLine, + minLines: widget.minLine, + maxLength: widget.maxLength, + obscuringCharacter: '*', + buildCounter: widget.showLen + ? null + : (context, + {required currentLength, + required isFocused, + required maxLength}) => + const SizedBox(), + style: (widget.isSmall ? Theme.of(context).textTheme.bodySmall! : Theme.of(context).textTheme.bodyMedium!) - .copyWith(color: Theme.of(context).colorScheme.hint), + .copyWith( + fontFamily: DesignConfig.fontFamily.padRight(3)), + decoration: InputDecoration( + suffixIcon: _suffixBuilder(), + enabled: widget.enabled, + border: InputBorder.none, + hintText: widget.hintText, + errorStyle: const TextStyle(height: 0.01), + hintStyle: (widget.isSmall + ? Theme.of(context).textTheme.bodySmall! + : Theme.of(context).textTheme.bodyMedium!) + .copyWith(color: Theme.of(context).colorScheme.hint), + ), ), ), ), diff --git a/pubspec.lock b/pubspec.lock index a079ef9..7d7cc0f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.4" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + url: "https://pub.dev" + source: hosted + version: "6.4.0" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" args: dependency: transitive description: @@ -669,6 +701,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" highlight: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 08b2548..3a1e4ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 4.0.1+4380 +version: 4.0.3+5000 environment: sdk: ">=2.19.0 <3.0.0" @@ -113,6 +113,7 @@ dependencies: sms_autofill: ^2.4.1 shimmer: ^3.0.0 device_info_plus: ^11.5.0 + app_links: ^6.4.0 # image_gallery_saver: ^2.0.3 # fading_edge_scrollview: ^4.1.1 dev_dependencies: