diff --git a/lib/core/config/api_config.dart b/lib/core/config/api_config.dart index 201f843..8a56d7d 100644 --- a/lib/core/config/api_config.dart +++ b/lib/core/config/api_config.dart @@ -6,5 +6,5 @@ class ApiConfig { static const String updateCategories = "/user/favoriteCategory"; static const String getFavoriteCategories = "/user/getfavoriteCategory"; static const String addReservation = "/reservation/add"; - static const String getReservations = "/reservation/get"; // <-- این خط اضافه شد + static const String getReservations = "/reservation/get"; } \ No newline at end of file diff --git a/lib/core/config/http_overrides.dart b/lib/core/config/http_overrides.dart index b4e6b32..8d34d3f 100644 --- a/lib/core/config/http_overrides.dart +++ b/lib/core/config/http_overrides.dart @@ -1,5 +1,3 @@ -// lib/core/config/http_overrides.dart - import 'dart:io'; class MyHttpOverrides extends HttpOverrides { diff --git a/lib/data/models/offer_model.dart b/lib/data/models/offer_model.dart index 7a446be..68db1ac 100644 --- a/lib/data/models/offer_model.dart +++ b/lib/data/models/offer_model.dart @@ -110,17 +110,15 @@ class OfferModel extends Equatable { final originalPriceValue = (json['Price'] as num?)?.toDouble() ?? 0.0; final finalPriceValue = (json['NPrice'] as num?)?.toDouble() ?? 0.0; - // ******** شروع تغییر ۱ ******** - // مدیریت هوشمند فاصله و عکس‌ها از منابع مختلف final distanceFromServer = (json['distance'] as num?)?.round() ?? (json['Distance'] as num?)?.round() ?? 0; List images = []; - if (json['imageData'] is List) { // برای سازگاری با پاسخ MQTT + if (json['imageData'] is List) { images = (json['imageData'] as List) .map((imageData) => imageData['Url']?.toString() ?? '') .where((url) => url.isNotEmpty) .toList(); - } else if (json['Images'] is List) { // برای پاسخ جدید API رزرو + } else if (json['Images'] is List) { images = (json['Images'] as List) .map((imageData) => (imageData is Map) ? imageData['Url']?.toString() ?? '' : '') .where((url) => url.isNotEmpty) @@ -129,13 +127,12 @@ class OfferModel extends Equatable { final typeData = json['typeData'] ?? json['Type']; final discountTypeName = (typeData is Map) ? typeData['Name']?.toString() : typeData?.toString(); - // ******** پایان تغییر ۱ ******** return OfferModel( id: json['ID'] ?? '', title: json['Name'] ?? 'بدون عنوان', discount: json['Description'] ?? '', - imageUrls: images, // <-- استفاده از لیست جدید + imageUrls: images, category: json['categoryData']?['Name']?.toString() ?? '', expiryTime: DateTime.tryParse(json['EndDate'] ?? '') ?? DateTime.now().add(const Duration(days: 1)), discountType: discountTypeName ?? '', @@ -146,7 +143,7 @@ class OfferModel extends Equatable { latitude: shopData.latitude, longitude: shopData.longitude, features: shopData.properties, - distanceInMeters: distanceFromServer, // <-- استفاده از متغیر جدید + distanceInMeters: distanceFromServer, isOpen: checkIsOpen, workingHours: [], rating: 0.0, diff --git a/lib/main.dart b/lib/main.dart index 5461181..de78257 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,7 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:proxibuy/core/config/http_overrides.dart'; // این خط را اضافه کنید +import 'package:proxibuy/core/config/http_overrides.dart'; import 'package:proxibuy/firebase_options.dart'; import 'package:proxibuy/presentation/auth/bloc/auth_bloc.dart'; import 'package:proxibuy/presentation/notification_preferences/bloc/notification_preferences_bloc.dart'; diff --git a/lib/presentation/auth/bloc/auth_bloc.dart b/lib/presentation/auth/bloc/auth_bloc.dart index 1696767..fb9ab81 100644 --- a/lib/presentation/auth/bloc/auth_bloc.dart +++ b/lib/presentation/auth/bloc/auth_bloc.dart @@ -77,7 +77,7 @@ class AuthBloc extends Bloc { }, ); - if (isClosed) return; // FIX: Add check here + if (isClosed) return; if (response.statusCode == 200) { final accessToken = response.data['data']['accessToken']; @@ -91,26 +91,23 @@ class AuthBloc extends Bloc { emit(AuthFailure(response.data['message'] ?? 'کد صحیح نیست')); } } on DioException catch (e) { - if (isClosed) return; // FIX: Add check here + if (isClosed) return; emit(AuthFailure(e.response?.data['message'] ?? 'خطایی در سرور رخ داد')); } } Future _onUpdateUserInfo( UpdateUserInfoEvent event, Emitter emit) async { - // ******** لاگ ۱: شروع پردازش ******** debugPrint("AuthBloc: 🔵 ایونت UpdateUserInfoEvent دریافت شد با نام: ${event.name}"); emit(AuthLoading()); try { final token = await _storage.read(key: 'accessToken'); if (token == null) { - // ******** لاگ خطا: توکن وجود ندارد ******** debugPrint("AuthBloc: 🔴 خطا: توکن کاربر یافت نشد."); emit(const AuthFailure("شما وارد نشده‌اید.")); return; } - // ******** لاگ ۲: ارسال درخواست به سرور ******** debugPrint("AuthBloc: 🟡 در حال ارسال درخواست آپدیت به سرور..."); final response = await _dio.post( ApiConfig.baseUrl + ApiConfig.updateUser, @@ -118,7 +115,6 @@ class AuthBloc extends Bloc { options: Options(headers: {'Authorization': 'Bearer $token'}), ); - // ******** لاگ ۳: پاسخ سرور ******** debugPrint("AuthBloc: 🟠 پاسخ سرور دریافت شد. StatusCode: ${response.statusCode}"); if (isClosed) { @@ -127,16 +123,13 @@ class AuthBloc extends Bloc { } if (response.statusCode == 200) { - // ******** لاگ ۴: موفقیت‌آمیز بودن عملیات ******** debugPrint("AuthBloc: ✅ درخواست موفق بود. در حال emit کردن AuthSuccess..."); emit(AuthSuccess()); } else { - // ******** لاگ خطا: پاسخ ناموفق از سرور ******** debugPrint("AuthBloc: 🔴 سرور پاسخ ناموفق داد: ${response.data['message']}"); emit(AuthFailure(response.data['message'] ?? 'خطا در ثبت اطلاعات')); } } on DioException catch (e) { - // ******** لاگ خطا: خطای Dio ******** debugPrint("AuthBloc: 🔴 خطای DioException رخ داد: ${e.response?.data['message']}"); if (isClosed) return; emit(AuthFailure(e.response?.data['message'] ?? 'خطا در ارتباط با سرور')); diff --git a/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart b/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart index 9756857..deff79c 100644 --- a/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart +++ b/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart @@ -17,6 +17,7 @@ class NotificationPreferencesBloc on(_onToggleCategorySelection); on(_onSubmitPreferences); on(_onLoadFavoriteCategories); + on(_onResetSubmissionStatus); add(LoadCategories()); } @@ -55,7 +56,7 @@ class NotificationPreferencesBloc try { final token = await _storage.read(key: 'accessToken'); if (token == null) { - if (isClosed) return; // بررسی قبل از emit + if (isClosed) return; emit(state.copyWith(isLoading: false, errorMessage: "شما وارد نشده‌اید.")); return; } @@ -66,7 +67,7 @@ class NotificationPreferencesBloc options: Options(headers: {'Authorization': 'Bearer $token'}), ); - if (isClosed) return; // بررسی قبل از emit + if (isClosed) return; if (response.statusCode == 200) { emit(state.copyWith(isLoading: false, submissionSuccess: true)); @@ -76,7 +77,7 @@ class NotificationPreferencesBloc errorMessage: response.data['message'] ?? 'خطا در ثبت اطلاعات')); } } on DioException catch (e) { - if (isClosed) return; // بررسی قبل از emit + if (isClosed) return; emit(state.copyWith( isLoading: false, errorMessage: e.response?.data['message'] ?? 'خطا در ارتباط با سرور')); @@ -89,7 +90,7 @@ class NotificationPreferencesBloc try { final token = await _storage.read(key: 'accessToken'); if (token == null) { - if (isClosed) return; // بررسی قبل از emit + if (isClosed) return; emit(state.copyWith(isLoading: false, errorMessage: "شما وارد نشده‌اید.")); return; } @@ -99,7 +100,7 @@ class NotificationPreferencesBloc options: Options(headers: {'Authorization': 'Bearer $token'}), ); - if (isClosed) return; // بررسی قبل از emit + if (isClosed) return; if (response.statusCode == 200) { final List fCategory = response.data['data']['FCategory']; @@ -115,10 +116,16 @@ class NotificationPreferencesBloc errorMessage: response.data['message'] ?? 'خطا در دریافت اطلاعات')); } } on DioException catch (e) { - if (isClosed) return; // بررسی قبل از emit + if (isClosed) return; emit(state.copyWith( isLoading: false, errorMessage: e.response?.data['message'] ?? 'خطا در ارتباط با سرور')); } } -} \ No newline at end of file + void _onResetSubmissionStatus( + ResetSubmissionStatus event, Emitter emit) { + emit(state.copyWith(submissionSuccess: false)); + } +} + + diff --git a/lib/presentation/notification_preferences/bloc/notification_preferences_event.dart b/lib/presentation/notification_preferences/bloc/notification_preferences_event.dart index 6dfde45..a231fe8 100644 --- a/lib/presentation/notification_preferences/bloc/notification_preferences_event.dart +++ b/lib/presentation/notification_preferences/bloc/notification_preferences_event.dart @@ -9,7 +9,7 @@ abstract class NotificationPreferencesEvent extends Equatable { class LoadCategories extends NotificationPreferencesEvent {} -class LoadFavoriteCategories extends NotificationPreferencesEvent {} // این کلاس اضافه شد +class LoadFavoriteCategories extends NotificationPreferencesEvent {} class ToggleCategorySelection extends NotificationPreferencesEvent { final String categoryId; @@ -20,4 +20,6 @@ class ToggleCategorySelection extends NotificationPreferencesEvent { List get props => [categoryId]; } -class SubmitPreferences extends NotificationPreferencesEvent {} \ No newline at end of file +class SubmitPreferences extends NotificationPreferencesEvent {} + +class ResetSubmissionStatus extends NotificationPreferencesEvent {} \ No newline at end of file diff --git a/lib/presentation/offer/bloc/offer_bloc.dart b/lib/presentation/offer/bloc/offer_bloc.dart index 36ec583..b2c7fbc 100644 --- a/lib/presentation/offer/bloc/offer_bloc.dart +++ b/lib/presentation/offer/bloc/offer_bloc.dart @@ -1,3 +1,4 @@ +// ignore: depend_on_referenced_packages import 'package:bloc/bloc.dart'; import 'package:proxibuy/presentation/offer/bloc/offer_event.dart'; import 'package:proxibuy/presentation/offer/bloc/offer_state.dart'; @@ -12,13 +13,8 @@ class OffersBloc extends Bloc { OffersReceivedFromMqtt event, Emitter emit, ) { - // همیشه حالت موفقیت را با لیست دریافتی منتشر کن (حتی اگر خالی باشد). - // این کار تضمین می‌کند که صفحه از حالت لودینگ خارج می‌شود و رابط کاربری - // آخرین وضعیت (وجود یا عدم وجود تخفیف) را نمایش می‌دهد. emit(OffersLoadSuccess(event.offers)); } - - // برای زمانی که مثلا کاربر GPS را خاموش می‌کند void _onClearOffers(ClearOffers event, Emitter emit) { emit(OffersInitial()); } diff --git a/lib/presentation/offer/bloc/widgets/offer_card.dart b/lib/presentation/offer/bloc/widgets/offer_card.dart index ef382c9..9a44a3e 100644 --- a/lib/presentation/offer/bloc/widgets/offer_card.dart +++ b/lib/presentation/offer/bloc/widgets/offer_card.dart @@ -33,7 +33,6 @@ class _OfferCardState extends State { padding: const EdgeInsets.only(bottom: 10), child: Column( children: [ - // Hero ویجت به اینجا اضافه شد Hero( tag: 'offer_image_${widget.offer.id}', child: _buildOfferImage(), diff --git a/lib/presentation/pages/login_page.dart b/lib/presentation/pages/login_page.dart index f131f0e..425a8e3 100644 --- a/lib/presentation/pages/login_page.dart +++ b/lib/presentation/pages/login_page.dart @@ -1,4 +1,3 @@ -// lib/presentation/pages/login_page.dart import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -114,8 +113,6 @@ class _LoginPageState extends State { ); } if (state is AuthCodeSentSuccess) { - // ******** شروع تغییر ۱ ******** - // BlocProvider.value حذف شد Navigator.push( context, MaterialPageRoute( @@ -127,7 +124,6 @@ class _LoginPageState extends State { ), ), ); - // ******** پایان تغییر ۱ ******** } }, builder: (context, state) { diff --git a/lib/presentation/pages/offers_page.dart b/lib/presentation/pages/offers_page.dart index 8295f2d..dc62343 100644 --- a/lib/presentation/pages/offers_page.dart +++ b/lib/presentation/pages/offers_page.dart @@ -12,6 +12,8 @@ import 'package:proxibuy/core/config/api_config.dart'; import 'package:proxibuy/core/config/app_colors.dart'; import 'package:proxibuy/core/gen/assets.gen.dart'; import 'package:proxibuy/data/models/offer_model.dart'; +import 'package:proxibuy/presentation/notification_preferences/bloc/notification_preferences_bloc.dart'; +import 'package:proxibuy/presentation/notification_preferences/bloc/notification_preferences_event.dart'; import 'package:proxibuy/presentation/offer/bloc/offer_bloc.dart'; import 'package:proxibuy/presentation/offer/bloc/offer_event.dart'; import 'package:proxibuy/presentation/offer/bloc/offer_state.dart'; @@ -47,16 +49,14 @@ class _OffersPageState extends State { super.initState(); _initializePage(); _initConnectivityListener(); - _fetchInitialReservations(); // <-- فراخوانی متد جدید + _fetchInitialReservations(); } - // ******** شروع متد جدید ******** - // این متد تعداد اولیه رزروها را برای نمایش روی آیکون دریافت می‌کند Future _fetchInitialReservations() async { try { const storage = FlutterSecureStorage(); final token = await storage.read(key: 'accessToken'); - if (token == null) return; // کاربر لاگین نکرده است + if (token == null) return; final dio = Dio(); final response = await dio.get( @@ -75,15 +75,12 @@ class _OffersPageState extends State { .where((id) => id.isNotEmpty) .toList(); - // به‌روزرسانی Cubit با لیست شناسه‌ها context.read().setReservedIds(reservedIds); } } catch (e) { - // اگر خطایی رخ دهد، مشکلی نیست. فقط عدد نمایش داده نمی‌شود debugPrint("Error fetching initial reservations: $e"); } } - // ******** پایان متد جدید ******** Future _initializePage() async { WidgetsBinding.instance.addPostFrameCallback((_) async { @@ -282,49 +279,52 @@ class _OffersPageState extends State { } Widget _buildFavoriteCategoriesSection() { - return Padding( - padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - 'دسته‌بندی‌های مورد علاقه شما', - style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold), - ), - TextButton( - onPressed: () async { - final result = await Navigator.of(context).push( - MaterialPageRoute( - builder: - (context) => const NotificationPreferencesPage( - loadFavoritesOnStart: true, - ), + return Padding( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'دسته‌بندی‌های مورد علاقه شما', + style: TextStyle(fontSize: 19, fontWeight: FontWeight.bold), + ), + TextButton( + onPressed: () async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const NotificationPreferencesPage( + loadFavoritesOnStart: true, ), - ); + ), + ); - if (result == true && mounted) { - _loadPreferences(); - } - }, - child: Row( - children: [ - SvgPicture.asset(Assets.icons.edit.path), - const SizedBox(width: 4), - const Text( - 'ویرایش', - style: TextStyle(color: AppColors.active), - ), - ], - ), + if (!mounted) return; + + context + .read() + .add(ResetSubmissionStatus()); + + _loadPreferences(); + }, + child: Row( + children: [ + SvgPicture.asset(Assets.icons.edit.path), + const SizedBox(width: 4), + const Text( + 'ویرایش', + style: TextStyle(color: AppColors.active), + ), + ], ), - ], - ), - const Divider(height: 1), - const SizedBox(height: 12), - if (_selectedCategories.isEmpty) + ), + ], + ), + const Divider(height: 1), + const SizedBox(height: 12), + if (_selectedCategories.isEmpty) const Padding( padding: EdgeInsets.only(bottom: 8.0), child: Text( diff --git a/lib/presentation/pages/onboarding_page.dart b/lib/presentation/pages/onboarding_page.dart index e88bda7..0728ff1 100644 --- a/lib/presentation/pages/onboarding_page.dart +++ b/lib/presentation/pages/onboarding_page.dart @@ -74,7 +74,6 @@ class _OnboardingPageState extends State { MaterialPageRoute( builder: (context) => BlocProvider( - // This creates a NEW AuthBloc create: (context) => AuthBloc(), child: const LoginPage(), ), diff --git a/lib/presentation/pages/otp_page.dart b/lib/presentation/pages/otp_page.dart index 54ffbba..be58d8b 100644 --- a/lib/presentation/pages/otp_page.dart +++ b/lib/presentation/pages/otp_page.dart @@ -148,13 +148,10 @@ class _OtpPageState extends State { }); } if (state is AuthNeedsInfo) { - // ******** شروع تغییر ۲ ******** - // BlocProvider.value حذف شد Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => const UserInfoPage()), (route) => false, ); - // ******** پایان تغییر ۲ ******** } }, builder: (context, state) { diff --git a/lib/presentation/pages/product_detail_page.dart b/lib/presentation/pages/product_detail_page.dart index fedea5c..b43c76e 100644 --- a/lib/presentation/pages/product_detail_page.dart +++ b/lib/presentation/pages/product_detail_page.dart @@ -191,7 +191,6 @@ class ProductDetailPage extends StatelessWidget { } } -// ... بقیه کدهای این فایل بدون تغییر باقی می‌ماند ... class ProductDetailView extends StatefulWidget { final OfferModel offer; @@ -589,7 +588,7 @@ class _ProductDetailViewState extends State { children: [ Container( padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 11), + const EdgeInsets.symmetric(horizontal: 8, vertical: 8), decoration: BoxDecoration( color: AppColors.singleOfferType, borderRadius: BorderRadius.circular(20), diff --git a/lib/presentation/pages/reservation_details_screen.dart b/lib/presentation/pages/reservation_details_screen.dart index 4cd1d3b..0a2ed25 100644 --- a/lib/presentation/pages/reservation_details_screen.dart +++ b/lib/presentation/pages/reservation_details_screen.dart @@ -36,7 +36,6 @@ class _ReservationConfirmationPageState super.initState(); _playSound(); - // ******** پایان تغییر اصلی ******** _calculateRemainingTime(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { @@ -44,10 +43,8 @@ class _ReservationConfirmationPageState }); } - // متد جدید برای پخش صدا void _playSound() async { try { - // فایل صدایی که در پوشه assets قرار داده‌اید await _audioPlayer.play(AssetSource('sounds/positive-notification-alert-351299.mp3')); } catch (e) { debugPrint("Error playing sound: $e"); @@ -130,7 +127,7 @@ class _ReservationConfirmationPageState curve: Curves.easeOutBack, ), const SizedBox(height: 18), - _buildQrCodeCard() // نمایش QR Code با استفاده از توکن + _buildQrCodeCard() .animate() .fadeIn(delay: 1000.ms, duration: 500.ms) .flipV(begin: -0.5, end: 0, curve: Curves.easeOut), @@ -359,7 +356,7 @@ class _ReservationConfirmationPageState Text( value, style: const TextStyle( - fontSize: 20, // فونت کمی کوچک‌تر شد + fontSize: 20, fontFamily: 'Dana', fontWeight: FontWeight.bold, color: Colors.black, diff --git a/lib/presentation/pages/reserved_list_page.dart b/lib/presentation/pages/reserved_list_page.dart index 5acf14d..76cccc4 100644 --- a/lib/presentation/pages/reserved_list_page.dart +++ b/lib/presentation/pages/reserved_list_page.dart @@ -41,22 +41,18 @@ class _ReservedListPageState extends State { if (response.statusCode == 200) { final List reserves = response.data['reserves']; - // ******** شروع تغییر ۲ ******** - // تبدیل ساختار جدید JSON به فرمت مورد انتظار OfferModel return reserves.map((reserveData) { final discountData = reserveData['Discount'] as Map; final shopData = reserveData['Shop'] as Map; - // ترکیب اطلاعات برای سازگاری با مدل final fullOfferData = { ...discountData, 'shopData': shopData, - 'Distance': reserveData['Distance'], // پاس دادن فاصله به مدل + 'Distance': reserveData['Distance'], }; return OfferModel.fromJson(fullOfferData); }).toList(); - // ******** پایان تغییر ۲ ******** } else { throw Exception('خطا در دریافت اطلاعات از سرور'); } diff --git a/lib/presentation/pages/user_info_page.dart b/lib/presentation/pages/user_info_page.dart index 3781097..8e75e79 100644 --- a/lib/presentation/pages/user_info_page.dart +++ b/lib/presentation/pages/user_info_page.dart @@ -1,4 +1,3 @@ -// lib/presentation/pages/user_info_page.dart import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -24,9 +23,6 @@ class _UserInfoPageState extends State { super.dispose(); } - // ******** شروع تغییر ۱ ******** - // متد ناوبری را ساده می‌کنیم. - // این متد دیگر Provider جدیدی نمی‌سازد و از Provider های سراسری استفاده می‌کند. void _navigateToNextPage() { Navigator.of(context).pushReplacement( MaterialPageRoute( @@ -34,7 +30,6 @@ class _UserInfoPageState extends State { ), ); } - // ******** پایان تغییر ۱ ******** Widget _buildGenderRadio(String title, String value) { return InkWell( @@ -146,7 +141,6 @@ class _UserInfoPageState extends State { const SizedBox(height: 55), BlocConsumer( listener: (context, state) { - // ******** لاگ: state جدید دریافت شد ******** debugPrint( "UserInfoPage: 🟠 Listener یک State جدید دریافت کرد: ${state.runtimeType}", ); @@ -160,7 +154,6 @@ class _UserInfoPageState extends State { ); } if (state is AuthSuccess) { - // ******** لاگ: state موفقیت آمیز بود ******** debugPrint( "UserInfoPage: ✅ State از نوع AuthSuccess است. فراخوانی _navigateToNextPage...", ); diff --git a/lib/presentation/reservation/cubit/reservation_cubit.dart b/lib/presentation/reservation/cubit/reservation_cubit.dart index e73f739..a73570b 100644 --- a/lib/presentation/reservation/cubit/reservation_cubit.dart +++ b/lib/presentation/reservation/cubit/reservation_cubit.dart @@ -1,4 +1,3 @@ -// lib/presentation/reservation/cubit/reservation_cubit.dart // ignore: depend_on_referenced_packages import 'package:bloc/bloc.dart'; @@ -15,7 +14,6 @@ class ReservationCubit extends Cubit { emit(state.copyWith(reservedProductIds: updatedList)); } - // متد جدید برای تنظیم کردن همه شناسه‌های رزرو شده void setReservedIds(List productIds) { emit(state.copyWith(reservedProductIds: productIds)); } diff --git a/lib/presentation/widgets/reserved_list_item_card.dart b/lib/presentation/widgets/reserved_list_item_card.dart index f5a34a0..b7589fc 100644 --- a/lib/presentation/widgets/reserved_list_item_card.dart +++ b/lib/presentation/widgets/reserved_list_item_card.dart @@ -1,4 +1,4 @@ - import 'dart:async'; +import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'; import 'package:flutter/material.dart'; @@ -58,7 +58,6 @@ class _ReservedListItemCardState extends State { void _toggleExpansion() { setState(() { _isExpanded = !_isExpanded; - // توکن فقط زمانی ساخته می‌شود که کاربر پنل را باز می‌کند if (_isExpanded && _qrTokenFuture == null) { _qrTokenFuture = _generateQrToken(); } @@ -90,7 +89,9 @@ class _ReservedListItemCardState extends State { @override Widget build(BuildContext context) { - return Column( + final isExpired = _remaining <= Duration.zero; + + final cardContent = Column( children: [ Card( color: Colors.white, @@ -107,6 +108,20 @@ class _ReservedListItemCardState extends State { _buildExpansionPanel(), ], ); + + if (isExpired) { + return ColorFiltered( + colorFilter: const ColorFilter.matrix([ + 0.2126, 0.7152, 0.0722, 0, 0, + 0.2126, 0.7152, 0.0722, 0, 0, + 0.2126, 0.7152, 0.0722, 0, 0, + 0, 0, 0, 1, 0, + ]), + child: cardContent, + ); + } + + return cardContent; } Widget _buildOfferPrimaryDetails() { @@ -214,6 +229,15 @@ class _ReservedListItemCardState extends State { const SizedBox(height: 4), _buildTimerLabels(_remaining), ], + ) + else + const Text( + 'منقضی شده', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + fontSize: 16, + ), ), SizedBox(width: 10), TextButton( diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart index b7f6e27..f53b2a7 100644 --- a/lib/services/mqtt_service.dart +++ b/lib/services/mqtt_service.dart @@ -1,5 +1,3 @@ -// lib/services/mqtt_service.dart - import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -10,7 +8,7 @@ import 'package:mqtt_client/mqtt_server_client.dart'; class MqttService { late MqttServerClient client; - final String server = '5.75.200.241'; + final String server = '5.75.197.180'; final int port = 1883; final StreamController> _messageStreamController = StreamController.broadcast(); @@ -28,7 +26,7 @@ class MqttService { client = MqttServerClient.withPort(server, clientId, port); client.logging(on: true); client.keepAlivePeriod = 60; - client.autoReconnect = true; // ✅ فعال‌سازی اتصال مجدد خودکار + client.autoReconnect = true; client.setProtocolV311(); debugPrint('--- [MQTT] Attempting to connect...'); @@ -64,17 +62,14 @@ class MqttService { }); }; - // این callback زمانی فراخوانی می‌شود که اتصال قطع شود client.onDisconnected = () { debugPrint('❌ [MQTT] Disconnected.'); }; - // این callback زمانی فراخوانی می‌شود که کلاینت در حال تلاش برای اتصال مجدد خودکار است client.onAutoReconnect = () { debugPrint('↪️ [MQTT] Auto-reconnecting...'); }; - // این callback زمانی فراخوانی می‌شود که اتصال مجدد خودکار موفقیت‌آمیز باشد client.onAutoReconnected = () { debugPrint('✅ [MQTT] Auto-reconnected successfully.'); };