301 lines
9.5 KiB
Dart
301 lines
9.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../models/app_notification.dart';
|
|
import '../services/api_service.dart';
|
|
import '../services/auth_service.dart';
|
|
|
|
class NotificationsScreen extends StatefulWidget {
|
|
const NotificationsScreen({super.key});
|
|
|
|
@override
|
|
State<NotificationsScreen> createState() => _NotificationsScreenState();
|
|
}
|
|
|
|
class _NotificationsScreenState extends State<NotificationsScreen> {
|
|
late final ApiService _api;
|
|
List<AppNotification> _notifications = [];
|
|
bool _loading = true;
|
|
final Set<String> _processing = {};
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_api = ApiService(AuthService());
|
|
_load();
|
|
}
|
|
|
|
Future<void> _load() async {
|
|
setState(() => _loading = true);
|
|
final list = await _api.getNotifications();
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_notifications = list;
|
|
_loading = false;
|
|
});
|
|
}
|
|
|
|
Future<void> _respond(AppNotification n, bool accepted) async {
|
|
setState(() => _processing.add(n.id));
|
|
final err = await _api.respondToNotification(n.id, accepted);
|
|
if (!mounted) return;
|
|
setState(() => _processing.remove(n.id));
|
|
|
|
if (err != null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
err,
|
|
style: const TextStyle(fontSize: 11),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
backgroundColor: const Color(0xFF333333),
|
|
behavior: SnackBarBehavior.floating,
|
|
duration: const Duration(seconds: 2),
|
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 40),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
// refresh
|
|
_load();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: Colors.black,
|
|
body: SafeArea(
|
|
child: Column(
|
|
children: [
|
|
// Header ساده و وسطچین (بدون دکمههای کناری)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(
|
|
Icons.notifications_outlined,
|
|
color: Color(0xFF00C853),
|
|
size: 16,
|
|
),
|
|
const SizedBox(width: 6),
|
|
const Text(
|
|
'اعلانها',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const Divider(height: 1, color: Color(0xFF333333)),
|
|
|
|
// Content
|
|
Expanded(
|
|
child: _loading
|
|
? const Center(
|
|
child: CircularProgressIndicator(
|
|
color: Color(0xFF00C853),
|
|
strokeWidth: 2,
|
|
),
|
|
)
|
|
: _notifications.isEmpty
|
|
? const Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
Icons.notifications_off_outlined,
|
|
color: Colors.white24,
|
|
size: 32,
|
|
),
|
|
SizedBox(height: 8),
|
|
Text(
|
|
'اعلانی وجود ندارد',
|
|
style: TextStyle(
|
|
color: Colors.white38,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: ListView.builder(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 8,
|
|
),
|
|
itemCount: _notifications.length,
|
|
itemBuilder: (ctx, i) => _NotifTile(
|
|
notif: _notifications[i],
|
|
isProcessing: _processing.contains(
|
|
_notifications[i].id,
|
|
),
|
|
onAccept: () => _respond(_notifications[i], true),
|
|
onReject: () => _respond(_notifications[i], false),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _NotifTile extends StatelessWidget {
|
|
final AppNotification notif;
|
|
final bool isProcessing;
|
|
final VoidCallback onAccept;
|
|
final VoidCallback onReject;
|
|
|
|
const _NotifTile({
|
|
required this.notif,
|
|
required this.isProcessing,
|
|
required this.onAccept,
|
|
required this.onReject,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isPending = notif.isPending;
|
|
final isJoin = notif.isJoinRequest;
|
|
|
|
Color statusColor;
|
|
if (notif.isAccepted == true) {
|
|
statusColor = const Color(0xFF00C853);
|
|
} else if (notif.isAccepted == false) {
|
|
statusColor = Colors.red;
|
|
} else {
|
|
statusColor = const Color(0xFFFFAB00);
|
|
}
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 8), // فاصله عمودی بین آیتمها
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF1C1C1E),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: isPending ? statusColor.withOpacity(0.4) : Colors.transparent,
|
|
width: 1,
|
|
),
|
|
),
|
|
padding: const EdgeInsets.all(10),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
isJoin ? Icons.group_add_outlined : Icons.campaign_outlined,
|
|
color: statusColor,
|
|
size: 14,
|
|
),
|
|
const SizedBox(width: 6),
|
|
Expanded(
|
|
child: Text(
|
|
notif.title,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
if (notif.description != null && notif.description!.isNotEmpty) ...[
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
notif.description!,
|
|
style: const TextStyle(color: Colors.white70, fontSize: 11),
|
|
maxLines: 3,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
if (isJoin && isPending) ...[
|
|
const SizedBox(height: 8),
|
|
if (isProcessing)
|
|
const Center(
|
|
child: SizedBox(
|
|
width: 16,
|
|
height: 16,
|
|
child: CircularProgressIndicator(
|
|
color: Color(0xFF00C853),
|
|
strokeWidth: 2,
|
|
),
|
|
),
|
|
)
|
|
else
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end, // دکمهها سمت راست
|
|
children: [
|
|
// Reject
|
|
GestureDetector(
|
|
onTap: onReject,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.red.withOpacity(0.15),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: const Text(
|
|
'رد',
|
|
style: TextStyle(
|
|
color: Colors.red,
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
// Accept
|
|
GestureDetector(
|
|
onTap: onAccept,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF00C853).withOpacity(0.15),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: const Text(
|
|
'قبول',
|
|
style: TextStyle(
|
|
color: Color(0xFF00C853),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
if (!isPending) ...[
|
|
const SizedBox(height: 4),
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: Text(
|
|
notif.isAccepted == true ? 'پذیرفته شد' : 'رد شد',
|
|
style: TextStyle(color: statusColor, fontSize: 10),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|