chitfund/luckychit/lib/interfaces/manager/draw_animation_page.dart

350 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../core/services/chit_group_service.dart';
import '../../core/models/chit_group.dart';
import '../../shared/widgets/draw_animation_selector.dart';
class DrawAnimationPage extends StatefulWidget {
final ChitGroup group;
final int month;
final int year;
final String serverSeed;
final int nonce;
final List<Map<String, dynamic>> eligibleMembers;
const DrawAnimationPage({
super.key,
required this.group,
required this.month,
required this.year,
required this.serverSeed,
required this.nonce,
required this.eligibleMembers,
});
@override
State<DrawAnimationPage> createState() => _DrawAnimationPageState();
}
class _DrawAnimationPageState extends State<DrawAnimationPage>
with SingleTickerProviderStateMixin {
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
bool _isComplete = false;
@override
void initState() {
super.initState();
_fadeController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _fadeController,
curve: Curves.easeInOut,
));
_fadeController.forward();
}
@override
void dispose() {
_fadeController.dispose();
super.dispose();
}
void _onDrawComplete(String winnerId) async {
if (_isComplete) return;
setState(() {
_isComplete = true;
});
// Wait a moment to show the winner
await Future.delayed(const Duration(seconds: 3));
// Save the draw result
final chitGroupService = Get.find<ChitGroupService>();
try {
await chitGroupService.createMonthlyDraw(
widget.group.id,
widget.month,
widget.year,
clientSeed: 'DRAW_${DateTime.now().millisecondsSinceEpoch}',
);
// Navigate back with success
Get.back(result: true);
Get.snackbar(
'Draw Complete! 🎉',
'Winner has been selected successfully',
backgroundColor: Colors.green,
colorText: Colors.white,
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
} catch (e) {
Get.back(result: false);
Get.snackbar(
'Error',
'Failed to save draw result: ${e.toString()}',
backgroundColor: Colors.red,
colorText: Colors.white,
duration: const Duration(seconds: 4),
);
}
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// Prevent back button during animation
if (_isComplete) return true;
final result = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Cancel Draw?'),
content: const Text('Are you sure you want to cancel the draw? This action cannot be undone.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Continue Draw'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Cancel Draw'),
),
],
),
);
return result ?? false;
},
child: Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: FadeTransition(
opacity: _fadeAnimation,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.purple.shade900,
Colors.blue.shade900,
Colors.indigo.shade900,
],
),
),
child: Column(
children: [
// Header
Padding(
padding: EdgeInsets.all(20.w),
child: Column(
children: [
Row(
children: [
if (!_isComplete)
IconButton(
icon: Icon(
Icons.close,
color: Colors.white.withOpacity(0.8),
size: 28.w,
),
onPressed: () async {
final result = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Cancel Draw?'),
content: const Text('Are you sure you want to cancel the draw?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Continue'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Cancel'),
),
],
),
);
if (result == true) {
Get.back(result: false);
}
},
)
else
SizedBox(width: 48.w),
Expanded(
child: Column(
children: [
Text(
widget.group.name,
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
textAlign: TextAlign.center,
),
SizedBox(height: 8.h),
Container(
padding: EdgeInsets.symmetric(
horizontal: 16.w,
vertical: 6.h,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20.r),
border: Border.all(
color: Colors.white.withOpacity(0.3),
),
),
child: Text(
'Month ${widget.month}/${widget.year}',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
],
),
),
SizedBox(width: 48.w),
],
),
],
),
),
// Animation Area
Expanded(
child: Center(
child: DrawAnimationSelector(
members: widget.eligibleMembers,
onDrawComplete: _onDrawComplete,
serverSeed: widget.serverSeed,
clientSeed: 'DRAW_${DateTime.now().millisecondsSinceEpoch}',
nonce: widget.nonce,
animationDuration: const Duration(seconds: 8),
),
),
),
// Footer
Padding(
padding: EdgeInsets.all(20.w),
child: Column(
children: [
Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.white.withOpacity(0.2),
),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInfoItem(
'Eligible Members',
'${widget.eligibleMembers.length}',
Icons.people,
),
Container(
width: 1,
height: 30.h,
color: Colors.white.withOpacity(0.3),
),
_buildInfoItem(
'Total Members',
'${widget.group.maxMembers}',
Icons.group,
),
],
),
],
),
),
SizedBox(height: 12.h),
Text(
'🎲 Provably Fair Draw',
style: TextStyle(
fontSize: 14.sp,
color: Colors.white.withOpacity(0.7),
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
),
),
),
),
);
}
Widget _buildInfoItem(String label, String value, IconData icon) {
return Column(
children: [
Icon(
icon,
color: Colors.white.withOpacity(0.8),
size: 24.w,
),
SizedBox(height: 6.h),
Text(
value,
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withOpacity(0.7),
),
),
],
);
}
}