fixed news

This commit is contained in:
mohamadmahdi jebeli 2025-07-14 15:46:38 +03:30
parent 941b482e82
commit c88324da81
4 changed files with 131 additions and 162 deletions

View File

@ -1,3 +1,5 @@
// lib/main.dart
// ignore_for_file: deprecated_member_use // ignore_for_file: deprecated_member_use
import 'dart:async'; import 'dart:async';
@ -7,6 +9,7 @@ import 'package:bot_toast/bot_toast.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/firebase_options.dart'; import 'package:didvan/firebase_options.dart';
import 'package:didvan/models/notification_message.dart'; import 'package:didvan/models/notification_message.dart';
import 'package:didvan/models/requests/news.dart';
import 'package:didvan/providers/media.dart'; import 'package:didvan/providers/media.dart';
import 'package:didvan/providers/theme.dart'; import 'package:didvan/providers/theme.dart';
import 'package:didvan/providers/user.dart'; import 'package:didvan/providers/user.dart';
@ -32,56 +35,54 @@ import 'package:home_widget/home_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
// پکیج جدید برای Deep Linking
import 'package:app_links/app_links.dart'; import 'package:app_links/app_links.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// متغیر استاتیک برای نگهداری لینک اولیه
Uri? initialURI; Uri? initialURI;
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> _backgroundCallbackHomeWidget(Uri? uri) async { Future<void> _backgroundCallbackHomeWidget(Uri? uri) async {
if (uri != null) { if (uri != null) {
await HomeWidget.saveWidgetData("uri", uri.host); await HomeWidget.saveWidgetData("uri", uri.host);
} }
} }
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); runZonedGuarded(
try { () async {
if (!kIsWeb) { WidgetsFlutterBinding.ensureInitialized();
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
await NotificationService.initializeNotification();
try { try {
if (Platform.isAndroid) { if (!kIsWeb) {
await FlutterDownloader.initialize( HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
debug: true, HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
ignoreSsl: true await NotificationService.initializeNotification();
); try {
if (Platform.isAndroid) {
await FlutterDownloader.initialize(debug: true, ignoreSsl: true);
}
} catch (e) {
e.printError();
}
} }
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await FirebaseApi().initNotification();
} catch (e) { } catch (e) {
e.printError(); debugPrint(e.toString());
} }
}
await Firebase.initializeApp( await SentryFlutter.init(
options: DefaultFirebaseOptions.currentPlatform); (options) {
await FirebaseApi().initNotification(); options.dsn = 'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048';
} catch (e) { options.tracesSampleRate = 1.0;
debugPrint(e.toString()); options.profilesSampleRate = 1.0;
} },
appRunner: () => runApp(const Didvan()),
await SentryFlutter.init( );
(options) { },
options.dsn = (error, stack) {
'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048'; Sentry.captureException(error, stackTrace: stack);
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
}, },
appRunner: () => runApp(const Didvan()),
); );
} }
@ -100,54 +101,39 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
// مقداردهی اولیه و گوش دادن به لینکها در اینجا انجام میشود
_initDeepLinks(); _initDeepLinks();
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_linkSubscription?.cancel(); // لغو کردن subscription _linkSubscription?.cancel();
if (MediaService.currentPodcast != null) { if (MediaService.currentPodcast != null) {
MediaService.audioPlayer.dispose(); MediaService.audioPlayer.dispose();
} }
super.dispose(); super.dispose();
} }
/// منطق جدید برای مدیریت لینکها با app_links
Future<void> _initDeepLinks() async { Future<void> _initDeepLinks() async {
_appLinks = AppLinks(); _appLinks = AppLinks();
// لینک اولیه را فقط در متغیر ذخیره میکنیم
initialURI = await _appLinks.getInitialLink(); initialURI = await _appLinks.getInitialLink();
// به لینکهای جدید زمانی که اپلیکیشن باز است گوش میدهیم
_linkSubscription = _appLinks.uriLinkStream.listen((uri) { _linkSubscription = _appLinks.uriLinkStream.listen((uri) {
_navigateTo(uri); _navigateTo(uri);
}); });
} }
/// تابع کمکی برای ناوبری
void _navigateTo(Uri uri) { void _navigateTo(Uri uri) {
if (mounted) { if (mounted) {
String path = uri.path; String path = uri.path;
print("path: $path, uri: $uri"); if (path.startsWith('/news/')) {
final id = path.split('/news/').last;
if (path.contains("news")) { if (id.isNotEmpty) {
final regex = RegExp(r'^/news/(\d+)$'); navigatorKey.currentState?.pushNamed(
final match = regex.firstMatch(path); Routes.newsDetails,
arguments: {'id': int.parse(id), 'args': const NewsRequestArgs(page: 0)},
if (match != null) { );
final id = match.group(1); // This will be '1234' as a string
print('Extracted ID: $id');
navigatorKey.currentState?.pushNamed(Routes.newsDetails, arguments: { 'id': int.parse(id!) ,'hasUnmarkConfirmation':true, 'isForward': true });
} else {
print('No match found');
} }
} else { } else {
print("no fragments identified. opening home path, uri: $uri");
navigatorKey.currentState?.pushNamed(path); navigatorKey.currentState?.pushNamed(path);
} }
} }
@ -158,11 +144,9 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
if (!kIsWeb) { if (!kIsWeb) {
if (state == AppLifecycleState.resumed) { if (state == AppLifecycleState.resumed) {
var r = await HomeWidget.getWidgetData("cRoute", defaultValue: ''); var r = await HomeWidget.getWidgetData("cRoute", defaultValue: '');
if (r!.toString() != Routes.splash) { if (r.toString() != Routes.splash) {
await HomeWidgetRepository.decideWhereToGo(); await HomeWidgetRepository.decideWhereToGo();
NotificationMessage? data = HomeWidgetRepository.data; NotificationMessage? data = HomeWidgetRepository.data;
if (data != null) { if (data != null) {
await HomeWidgetRepository.decideWhereToGoNotif(); await HomeWidgetRepository.decideWhereToGoNotif();
} }
@ -190,41 +174,41 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: SafeArea( child: SafeArea(
child: MaterialApp( child: MaterialApp(
scrollBehavior: MyCustomScrollBehavior(), scrollBehavior: MyCustomScrollBehavior(),
navigatorKey: navigatorKey, navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: 'Didvan', title: 'Didvan',
theme: LightThemeConfig.themeData.copyWith( theme: LightThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData( bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent), backgroundColor: Colors.transparent),
textTheme: LightThemeConfig.themeData.textTheme.apply( textTheme: LightThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily, fontFamily: themeProvider.fontFamily,
)), )),
darkTheme: DarkThemeConfig.themeData.copyWith( darkTheme: DarkThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData( bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent), backgroundColor: Colors.transparent),
textTheme: DarkThemeConfig.themeData.textTheme.apply( textTheme: DarkThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily, fontFamily: themeProvider.fontFamily,
)), )),
color: LightThemeConfig.themeData.primaryColor, color: LightThemeConfig.themeData.primaryColor,
themeMode: themeProvider.themeMode, themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) => onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings), RouteGenerator.generateRoute(settings),
builder: BotToastInit(), builder: BotToastInit(),
navigatorObservers: [BotToastNavigatorObserver()], navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: "/", initialRoute: "/",
localizationsDelegates: const [ localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
], ],
supportedLocales: const [ supportedLocales: const [
Locale("fa", "IR"), Locale("fa", "IR"),
], ],
locale: const Locale("fa", "IR"), locale: const Locale("fa", "IR"),
)), )),
), ),
), ),
); );

View File

@ -1,3 +1,5 @@
// lib/services/network/request.dart
// ignore_for_file: empty_catches // ignore_for_file: empty_catches
import 'dart:convert'; import 'dart:convert';
@ -15,7 +17,7 @@ import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
class RequestService { class RequestService {
static late String token; static String? token; // Made token nullable
int? statusCode; int? statusCode;
dynamic data(String s) { dynamic data(String s) {
@ -48,12 +50,9 @@ class RequestService {
}) { }) {
if (body != null) _requestBody = body; if (body != null) _requestBody = body;
if (requestHeaders != null) _headers.addAll(requestHeaders); if (requestHeaders != null) _headers.addAll(requestHeaders);
if (useAutherization) _headers.addAll({'Authorization': 'Bearer $token'}); if (useAutherization && token != null) { // Check if token is not null
// if (kDebugMode) { _headers.addAll({'Authorization': 'Bearer $token'});
// try { }
// print('Authorization : Bearer $token');
// } catch (e) {}
// }
if (body != null) _requestBody = body; if (body != null) _requestBody = body;
} }

View File

@ -1,3 +1,5 @@
// lib/views/news/news_details/news_details_state.dart
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
@ -8,6 +10,8 @@ import 'package:didvan/models/requests/news.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/services/storage/storage.dart';
import 'package:flutter/material.dart';
class NewsDetailsState extends CoreProvier { class NewsDetailsState extends CoreProvier {
final List<NewsDetailsData?> news = []; final List<NewsDetailsData?> news = [];
@ -25,26 +29,35 @@ class NewsDetailsState extends CoreProvier {
NewsDetailsData get currentNews => news[_currentIndex]!; NewsDetailsData get currentNews => news[_currentIndex]!;
Future<void> getNewsDetails(int id, {bool? isForward}) async { Future<void> getNewsDetails(int id, {bool? isForward}) async {
debugPrint('requset token ${RequestService.token}');
// **FIX: Ensure the token is available before making a request.**
if (RequestService.token == null) {
final token = await StorageService.getValue(key: 'token');
if (token != null) {
RequestService.token = token;
} else {
// If no token, fail gracefully. You might want to navigate
// to the login screen here in a real-world scenario.
appState = AppState.failed;
notifyListeners();
return;
}
}
isFetchingNewItem = true;
notifyListeners();
print("isForward: $isForward");
// if (isForward == null) {
// appState = AppState.busy;
// } else {
isFetchingNewItem = true;
notifyListeners();
// }
final service = RequestService(RequestHelper.newsDetails(id, args)); final service = RequestService(RequestHelper.newsDetails(id, args));
await service.httpGet(); await service.httpGet();
print("making request to news endpoint $id");
_handleTracking(sendRequest: isForward != null); _handleTracking(sendRequest: isForward != null);
if (service.isSuccess) { if (service.isSuccess) {
print("success in receiving news detail for id $id");
final result = service.result; final result = service.result;
final newsItem = NewsDetailsData.fromJson(result['news']); final newsItem = NewsDetailsData.fromJson(result['news']);
if (args.page == 0) { if (args.page == 0) {
news.add(newsItem); news.add(newsItem);
initialIndex = 0; initialIndex = 0;
appState = AppState.idle; appState = AppState.idle;
notifyListeners(); // Notify listeners after state change
return; return;
} }
NewsDetailsData? prevNews; NewsDetailsData? prevNews;
@ -81,12 +94,12 @@ class NewsDetailsState extends CoreProvier {
getRelatedContents(); getRelatedContents();
} }
appState = AppState.idle; appState = AppState.idle;
notifyListeners(); // Notify listeners after state change
return; return;
} }
print("failed to receive news detail for id $id");
// if (isForward == null) { appState = AppState.failed;
// appState = AppState.failed; notifyListeners(); // Notify listeners on failure
// }
} }
bool exists(NewsDetailsData? newsItem) => bool exists(NewsDetailsData? newsItem) =>

View File

@ -108,14 +108,11 @@ class _SplashState extends State<Splash> {
print("detected token as $token"); print("detected token as $token");
if (token != null) { if (token != null) {
print("detected token"); RequestService.token = token;
if (!kIsWeb) { if (!kIsWeb) {
await mediaProvider.getDownloadsList(); await mediaProvider.getDownloadsList();
} }
RequestService.token = token;
print("fetching user info...");
final result = await userProvider.getUserInfo(); final result = await userProvider.getUserInfo();
if (!result) { if (!result) {
@ -138,49 +135,25 @@ class _SplashState extends State<Splash> {
await ServerDataProvider.getData(); await ServerDataProvider.getData();
} }
// --- بخش اصلاح شده --- final String destinationRoute =
try { token == null ? Routes.authenticaion : Routes.home;
// Determine destination and arguments based on authentication status dynamic routeArguments =
if (token == null) { token == null ? {'isResetPassword': false} : {'showDialogs': true};
// User is not authenticated, go to the authentication screen
print("User not authenticated. Navigating to authentication.");
await navigatorKey.currentState!.pushReplacementNamed(
Routes.authenticaion,
arguments: false, // Pass the expected boolean argument
);
} else {
// User is authenticated, go to the home screen
print("User authenticated. Navigating to home.");
// Prepare arguments for the home screen if (destinationRoute == Routes.home && initialURI != null) {
final Map<String, dynamic> routeArguments = {'showDialogs': true}; (routeArguments as Map)['deepLinkUri'] = initialURI;
initialURI = null;
// If a deep link exists, add it to the arguments
if (initialURI != null) {
print("Deep link found: $initialURI. Adding to arguments.");
routeArguments['deepLinkUri'] = initialURI;
initialURI = null; // Consume the link so it's not used again
}
// Navigate to the home screen with the correct arguments
await navigatorKey.currentState!.pushReplacementNamed(
Routes.home,
arguments: routeArguments,
);
}
// --- پایان بخش اصلاح شده ---
} catch (e) {
print("An error occurred during initialization: $e");
if (mounted) {
setState(() {
_errorOccured = true;
});
}
} }
if(destinationRoute == Routes.authenticaion){
routeArguments = false;
}
await navigatorKey.currentState!.pushReplacementNamed(
destinationRoute,
arguments: routeArguments,
);
} catch (e) { } catch (e) {
print("error in splash screen: $e");
setState(() { setState(() {
_errorOccured = true; _errorOccured = true;
}); });