dev/fix-news #2

Merged
Mr.Jebelli merged 8 commits from dev/fix-news into master 2025-07-16 11:59:01 +00:00
4 changed files with 131 additions and 162 deletions
Showing only changes of commit c88324da81 - Show all commits

View File

@ -1,3 +1,5 @@
// lib/main.dart
// ignore_for_file: deprecated_member_use
import 'dart:async';
@ -7,6 +9,7 @@ import 'package:bot_toast/bot_toast.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/firebase_options.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/theme.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:flutter_downloader/flutter_downloader.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
// پکیج جدید برای Deep Linking
import 'package:app_links/app_links.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// متغیر استاتیک برای نگهداری لینک اولیه
Uri? initialURI;
@pragma('vm:entry-point')
Future<void> _backgroundCallbackHomeWidget(Uri? uri) async {
if (uri != null) {
await HomeWidget.saveWidgetData("uri", uri.host);
await HomeWidget.saveWidgetData("uri", uri.host);
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
try {
if (!kIsWeb) {
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
await NotificationService.initializeNotification();
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
try {
if (Platform.isAndroid) {
await FlutterDownloader.initialize(
debug: true,
ignoreSsl: true
);
if (!kIsWeb) {
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
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) {
e.printError();
debugPrint(e.toString());
}
}
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
await FirebaseApi().initNotification();
} catch (e) {
debugPrint(e.toString());
}
await SentryFlutter.init(
(options) {
options.dsn =
'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048';
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
await SentryFlutter.init(
(options) {
options.dsn = 'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048';
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
},
appRunner: () => runApp(const Didvan()),
);
},
(error, stack) {
Sentry.captureException(error, stackTrace: stack);
},
appRunner: () => runApp(const Didvan()),
);
}
@ -100,55 +101,40 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// مقداردهی اولیه و گوش دادن به لینکها در اینجا انجام میشود
_initDeepLinks();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_linkSubscription?.cancel(); // لغو کردن subscription
_linkSubscription?.cancel();
if (MediaService.currentPodcast != null) {
MediaService.audioPlayer.dispose();
}
super.dispose();
}
/// منطق جدید برای مدیریت لینکها با app_links
Future<void> _initDeepLinks() async {
_appLinks = AppLinks();
// لینک اولیه را فقط در متغیر ذخیره میکنیم
initialURI = await _appLinks.getInitialLink();
// به لینکهای جدید زمانی که اپلیکیشن باز است گوش میدهیم
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
_navigateTo(uri);
});
}
/// تابع کمکی برای ناوبری
void _navigateTo(Uri uri) {
if (mounted) {
String path = uri.path;
print("path: $path, uri: $uri");
if (path.contains("news")) {
final regex = RegExp(r'^/news/(\d+)$');
final match = regex.firstMatch(path);
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');
if (path.startsWith('/news/')) {
final id = path.split('/news/').last;
if (id.isNotEmpty) {
navigatorKey.currentState?.pushNamed(
Routes.newsDetails,
arguments: {'id': int.parse(id), 'args': const NewsRequestArgs(page: 0)},
);
}
} 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 (state == AppLifecycleState.resumed) {
var r = await HomeWidget.getWidgetData("cRoute", defaultValue: '');
if (r!.toString() != Routes.splash) {
await HomeWidgetRepository.decideWhereToGo();
if (r.toString() != Routes.splash) {
await HomeWidgetRepository.decideWhereToGo();
NotificationMessage? data = HomeWidgetRepository.data;
if (data != null) {
await HomeWidgetRepository.decideWhereToGoNotif();
}
@ -190,43 +174,43 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
child: MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: 'Didvan',
theme: LightThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent),
textTheme: LightThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
)),
darkTheme: DarkThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent),
textTheme: DarkThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
)),
color: LightThemeConfig.themeData.primaryColor,
themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings),
builder: BotToastInit(),
navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: "/",
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale("fa", "IR"),
],
locale: const Locale("fa", "IR"),
)),
scrollBehavior: MyCustomScrollBehavior(),
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: 'Didvan',
theme: LightThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent),
textTheme: LightThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
)),
darkTheme: DarkThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent),
textTheme: DarkThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
)),
color: LightThemeConfig.themeData.primaryColor,
themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings),
builder: BotToastInit(),
navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: "/",
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: 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
import 'dart:convert';
@ -15,7 +17,7 @@ import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart';
class RequestService {
static late String token;
static String? token; // Made token nullable
int? statusCode;
dynamic data(String s) {
@ -48,12 +50,9 @@ class RequestService {
}) {
if (body != null) _requestBody = body;
if (requestHeaders != null) _headers.addAll(requestHeaders);
if (useAutherization) _headers.addAll({'Authorization': 'Bearer $token'});
// if (kDebugMode) {
// try {
// print('Authorization : Bearer $token');
// } catch (e) {}
// }
if (useAutherization && token != null) { // Check if token is not null
_headers.addAll({'Authorization': 'Bearer $token'});
}
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:math';
@ -8,6 +10,8 @@ import 'package:didvan/models/requests/news.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.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 {
final List<NewsDetailsData?> news = [];
@ -25,26 +29,35 @@ class NewsDetailsState extends CoreProvier {
NewsDetailsData get currentNews => news[_currentIndex]!;
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;
}
}
print("isForward: $isForward");
// if (isForward == null) {
// appState = AppState.busy;
// } else {
isFetchingNewItem = true;
notifyListeners();
// }
isFetchingNewItem = true;
notifyListeners();
final service = RequestService(RequestHelper.newsDetails(id, args));
await service.httpGet();
print("making request to news endpoint $id");
_handleTracking(sendRequest: isForward != null);
if (service.isSuccess) {
print("success in receiving news detail for id $id");
final result = service.result;
final newsItem = NewsDetailsData.fromJson(result['news']);
if (args.page == 0) {
news.add(newsItem);
initialIndex = 0;
appState = AppState.idle;
notifyListeners(); // Notify listeners after state change
return;
}
NewsDetailsData? prevNews;
@ -81,12 +94,12 @@ class NewsDetailsState extends CoreProvier {
getRelatedContents();
}
appState = AppState.idle;
notifyListeners(); // Notify listeners after state change
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) =>

View File

@ -108,14 +108,11 @@ class _SplashState extends State<Splash> {
print("detected token as $token");
if (token != null) {
print("detected token");
RequestService.token = token;
if (!kIsWeb) {
await mediaProvider.getDownloadsList();
}
RequestService.token = token;
print("fetching user info...");
final result = await userProvider.getUserInfo();
if (!result) {
@ -138,49 +135,25 @@ class _SplashState extends State<Splash> {
await ServerDataProvider.getData();
}
// --- بخش اصلاح شده ---
try {
// Determine destination and arguments based on authentication status
if (token == null) {
// 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.");
final String destinationRoute =
token == null ? Routes.authenticaion : Routes.home;
dynamic routeArguments =
token == null ? {'isResetPassword': false} : {'showDialogs': true};
// Prepare arguments for the home screen
final Map<String, dynamic> routeArguments = {'showDialogs': true};
// 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.home && initialURI != null) {
(routeArguments as Map)['deepLinkUri'] = initialURI;
initialURI = null;
}
if(destinationRoute == Routes.authenticaion){
routeArguments = false;
}
await navigatorKey.currentState!.pushReplacementNamed(
destinationRoute,
arguments: routeArguments,
);
} catch (e) {
print("error in splash screen: $e");
setState(() {
_errorOccured = true;
});