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