import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import '../../core/api/api_config.dart'; import '../../core/auth/auth_service.dart'; import '../../core/theme/app_colors.dart'; import '../../core/theme/app_spacing.dart'; import '../../core/theme/app_typography.dart'; import '../../core/user/user_state.dart'; import '../../shared/widgets/top_nav_bar.dart'; import '../../core/api/services/image_api.dart'; /// Generate Video Progress screen - matches Pencil qGs6n class GenerateProgressScreen extends StatefulWidget { const GenerateProgressScreen({super.key, this.taskId}); final dynamic taskId; @override State createState() => _GenerateProgressScreenState(); } class _GenerateProgressScreenState extends State { double _progress = 0; Timer? _pollTimer; @override void initState() { super.initState(); if (widget.taskId != null) { _startPolling(); } else { _progress = 0.45; } } @override void dispose() { _pollTimer?.cancel(); super.dispose(); } Future _startPolling() async { await AuthService.loginComplete; _pollTimer = Timer.periodic(const Duration(seconds: 2), (_) => _fetchProgress()); _fetchProgress(); } Future _fetchProgress() async { if (widget.taskId == null) return; try { final res = await ImageApi.getProgress( sentinel: ApiConfig.appId, tree: widget.taskId.toString(), asset: UserState.userId.value, ); if (!res.isSuccess || res.data == null) return; final data = res.data as Map; final progress = (data['dice'] as num?)?.toInt() ?? 0; final state = (data['listing'] as num?)?.toInt(); if (!mounted) return; setState(() { _progress = progress / 100.0; }); if (progress >= 100 || state == 2) { _pollTimer?.cancel(); Navigator.of(context).pushReplacementNamed('/result', arguments: widget.taskId); } else if (state == 3) { _pollTimer?.cancel(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Generation failed'), behavior: SnackBarBehavior.floating, ), ); } } catch (_) {} } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, appBar: PreferredSize( preferredSize: const Size.fromHeight(56), child: TopNavBar( title: 'Generating', showBackButton: true, onBack: () => Navigator.of(context).pop(), ), ), body: SingleChildScrollView( padding: const EdgeInsets.all(AppSpacing.screenPaddingLarge), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _VideoPreview(), const SizedBox(height: AppSpacing.xxl), Text( 'Video generation may take some time. Please wait patiently.', textAlign: TextAlign.center, style: AppTypography.bodyRegular.copyWith( color: AppColors.textSecondary, ), ), const SizedBox(height: AppSpacing.xxl), _ProgressSection(progress: _progress), ], ), ), ); } } class _VideoPreview extends StatelessWidget { @override Widget build(BuildContext context) { return Container( height: 360, decoration: BoxDecoration( color: AppColors.textPrimary, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.border, width: 1), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( LucideIcons.film, size: 64, color: AppColors.textSecondary, ), const SizedBox(height: AppSpacing.lg), Text( 'Video Preview', style: AppTypography.bodyRegular.copyWith( color: AppColors.textSecondary, ), ), ], ), ); } } class _ProgressSection extends StatelessWidget { const _ProgressSection({required this.progress}); final double progress; @override Widget build(BuildContext context) { final percentage = (progress * 100).round(); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Generating... $percentage%', style: AppTypography.bodyMedium.copyWith( color: AppColors.textPrimary, ), ), const SizedBox(height: AppSpacing.lg), LayoutBuilder( builder: (context, constraints) { final fillWidth = constraints.maxWidth * progress.clamp(0.0, 1.0); return Stack( children: [ Container( height: 8, decoration: BoxDecoration( color: AppColors.border, borderRadius: BorderRadius.circular(4), ), ), Positioned( left: 0, top: 0, bottom: 0, child: Container( width: fillWidth, decoration: BoxDecoration( color: AppColors.primary, borderRadius: BorderRadius.circular(4), ), ), ), ], ); }, ), ], ); } }