proxibuy/lib/presentation/widgets/notification_panel.dart

262 lines
8.6 KiB
Dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:intl/intl.dart';
import 'package:proxibuy/core/config/app_colors.dart';
import 'package:proxibuy/data/models/notification_model.dart';
import 'package:proxibuy/presentation/offer/bloc/offer_bloc.dart';
import 'package:proxibuy/presentation/offer/bloc/offer_state.dart';
import 'package:proxibuy/presentation/pages/product_detail_page.dart';
class NotificationPanel extends StatefulWidget {
final VoidCallback onClose;
final VoidCallback? onListChanged;
const NotificationPanel({super.key, required this.onClose, this.onListChanged});
@override
State<NotificationPanel> createState() => _NotificationPanelState();
}
class _NotificationPanelState extends State<NotificationPanel> {
List<NotificationModel> _notifications = [];
bool _isLoading = true;
String _errorMessage = '';
final Dio _dio = Dio();
final FlutterSecureStorage _storage = const FlutterSecureStorage();
@override
void initState() {
super.initState();
_fetchNotifications();
}
Future<void> _fetchNotifications() async {
final token = await _storage.read(key: 'accessToken');
if (token == null) {
if (mounted) {
setState(() {
_isLoading = false;
_errorMessage = 'برای مشاهده اعلان‌ها، لطفا ابتدا وارد شوید.';
});
widget.onListChanged?.call();
}
return;
}
try {
final response = await _dio.get(
'https://proxybuy.liara.run/notify/get',
options: Options(headers: {'Authorization': 'Bearer $token'}),
);
if (response.statusCode == 200 && mounted) {
final List<dynamic> data = response.data['data'] ?? [];
setState(() {
_notifications = data.map((json) => NotificationModel.fromJson(json)).toList();
_isLoading = false;
});
widget.onListChanged?.call();
} else if (response.statusCode == 201) {
if (mounted) {
setState(() {
_isLoading = false;
_errorMessage = 'اعلانی وجود ندارد';
});
widget.onListChanged?.call();
}
}
} catch (e) {
if (mounted) {
setState(() {
_isLoading = false;
_errorMessage = 'اتصال به سرور برقرار نشد.';
});
widget.onListChanged?.call();
}
}
}
Future<void> _ignoreNotification(String notificationId) async {
final token = await _storage.read(key: 'accessToken');
if (token == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('خطا: شما وارد حساب کاربری نشده‌اید.')),
);
return;
}
try {
final response = await _dio.get(
'https://proxybuy.liara.run/notify/ignore/$notificationId',
options: Options(headers: {'Authorization': 'Bearer $token'}),
);
if (response.statusCode == 200 && mounted) {
setState(() {
_notifications.removeWhere((n) => n.id == notificationId);
});
widget.onListChanged?.call();
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(response.data?['message'] ?? 'خطا در حذف اعلان.')),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('خطا در ارتباط با سرور.')),
);
debugPrint('Error ignoring notification: $e');
}
}
}
String _formatTimeAgo(DateTime dateTime) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inSeconds < 60) {
return 'همین الان';
} else if (difference.inMinutes < 60) {
return '${difference.inMinutes} دقیقه قبل';
} else if (difference.inHours < 24) {
return '${difference.inHours} ساعت قبل';
} else if (difference.inDays < 7) {
return '${difference.inDays} روز قبل';
} else {
return DateFormat('yyyy/MM/dd', 'fa').format(dateTime);
}
}
void _navigateToOffer(NotificationModel notification) {
if (!notification.status) return;
final offersState = context.read<OffersBloc>().state;
if (offersState is OffersLoadSuccess) {
try {
final offer = offersState.offers.firstWhere((o) => o.id == notification.discountId);
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ProductDetailPage(offer: offer),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('این تخفیف در حال حاضر در دسترس نیست.')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('اطلاعات تخفیف‌ها هنوز بارگذاری نشده است.')),
);
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: _buildBody(),
),
],
);
}
Widget _buildBody() {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_errorMessage.isNotEmpty) {
return Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(_errorMessage, textAlign: TextAlign.center),
),
);
}
if (_notifications.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('اعلانی وجود ندارد.', textAlign: TextAlign.center),
),
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 0.0),
itemCount: _notifications.length,
itemBuilder: (context, index) {
return _buildNotificationCard(_notifications[index]);
},
);
}
Widget _buildNotificationCard(NotificationModel notification) {
final bool isExpired = !notification.status;
final Color textColor = isExpired ? Colors.grey.shade600 : Colors.black;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
notification.description,
style: TextStyle(fontSize: 15, height: 1.6, color: textColor),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
_formatTimeAgo(notification.createdAt.toLocal()),
style: TextStyle(color: Colors.grey.shade600, fontSize: 12),
),
if (isExpired)
ElevatedButton(
onPressed: null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.shade300,
foregroundColor: Colors.grey.shade700,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(horizontal: 16),
),
child: const Text('تخفیف تمام شد'),
)
else
Row(
children: [
TextButton(
onPressed: () => _ignoreNotification(notification.id),
style: TextButton.styleFrom(foregroundColor: Colors.red.shade700),
child: const Text('بیخیال'),
),
const SizedBox(width: 4),
ElevatedButton(
onPressed: () => _navigateToOffer(notification),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.backgroundConfirm,
foregroundColor: AppColors.selectedImg,
side: BorderSide(color: Colors.green.shade200),
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(horizontal: 16),
),
child: const Text('بزن بریم'),
),
],
),
],
),
],
),
);
}
}