import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:arcx/app/app_theme.dart'; import 'package:arcx/data/local/models/models.dart'; import 'package:arcx/features/config/application/app_config_controller.dart'; import 'package:arcx/features/home/presentation/widgets/heatmap_card.dart'; import 'package:arcx/features/home/presentation/widgets/profile_card.dart'; import 'package:arcx/features/home/presentation/widgets/score_distribution_card.dart'; import 'package:arcx/features/home/presentation/widgets/stats_card.dart'; import 'package:arcx/features/iap/presentation/iap_unlock_dialog.dart'; import 'package:arcx/features/profile/presentation/profile_edit_dialog.dart'; import 'package:arcx/features/records/presentation/point_book_list_page.dart'; import 'package:arcx/features/scoring/application/scoring_service.dart'; import 'package:arcx/features/scoring/presentation/point_book_create_page.dart'; import 'package:arcx/features/scoring/presentation/point_book_edit_page.dart'; import 'package:arcx/features/stats/application/stats.dart'; class PointBookHomeScreen extends ConsumerWidget { const PointBookHomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final statsAsync = ref.watch(statsProvider); final configAsync = ref.watch(appConfigProvider); return Scaffold( backgroundColor: AppTheme.surfaceMuted, appBar: AppBar(title: const Text('记分本')), body: statsAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => Center(child: Text('数据加载失败:$e')), data: (stats) => RefreshIndicator( onRefresh: () async => ref.invalidate(statsProvider), child: ListView( padding: const EdgeInsets.fromLTRB(16, 16, 16, 32), children: [ ProfileCard(onEdit: () => _editProfile(context)), const SizedBox(height: 12), _EntitlementBanner(configAsync: configAsync), const SizedBox(height: 12), StatsCard(stats: stats), const SizedBox(height: 12), HeatmapCard(stats: stats), const SizedBox(height: 12), ScoreDistributionCard(stats: stats), const SizedBox(height: 20), _EntryButtons( onOpenRecords: () => _openRecords(context), onStartScoring: () => _startScoring(context, ref), ), ], ), ), ), ); } Future _editProfile(BuildContext context) async { await showDialog( context: context, builder: (_) => const ProfileEditDialog(), ); } Future _openRecords(BuildContext context) async { await Navigator.of( context, ).push(MaterialPageRoute(builder: (_) => const PointBookListPage())); } Future _startScoring(BuildContext context, WidgetRef ref) async { final config = ref.read(appConfigProvider).value; if (config == null) return; // IAP / trial gate. // if (!config.canStartScoring) { // await showDialog( // context: context, // builder: (_) => const IapUnlockDialog(), // ); // return; // } // Draft check. final notifier = ref.read(pointRecordsProvider.notifier); final draft = await notifier.currentDraft(); if (draft != null && context.mounted) { final choice = await _showDraftConfirm(context); if (choice == null) return; if (choice == true) { await _goToEdit(context, draft); return; } await notifier.clearDrafts(); } if (!context.mounted) return; await _goToCreate(context); } Future _goToCreate(BuildContext context) async { final created = await Navigator.of(context).push( MaterialPageRoute( builder: (_) => const PointBookCreatePage(), ), ); // Create already popped itself before returning, so the stack is [Home] // here. A plain push yields [Home, Edit] and back/popUntil lands on Home. if (created != null && context.mounted) { await _goToEdit(context, created); } } Future _goToEdit(BuildContext context, PointRecord record) async { await Navigator.of(context).push( MaterialPageRoute( builder: (_) => PointBookEditPage(recordId: record.id), ), ); } Future _showDraftConfirm(BuildContext context) { return showDialog( context: context, builder: (_) => AlertDialog( title: const Text('存在未完成草稿'), content: const Text('检测到上次有未完成的记分草稿,是否继续编辑?选择“重新计分”将删除该草稿。'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('重新计分'), ), FilledButton( onPressed: () => Navigator.pop(context, true), child: const Text('继续编辑'), ), ], ), ); } } class _EntitlementBanner extends StatelessWidget { const _EntitlementBanner({required this.configAsync}); final AsyncValue configAsync; @override Widget build(BuildContext context) { return configAsync.when( loading: () => const SizedBox.shrink(), error: (_, __) => const SizedBox.shrink(), data: (config) { final unlocked = config.isVipUnlocked; return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: unlocked ? AppTheme.primary.withValues(alpha: 0.12) : AppTheme.accent.withValues(alpha: 0.18), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon( unlocked ? Icons.verified_outlined : Icons.timer_outlined, color: unlocked ? AppTheme.primary : AppTheme.accent, size: 20, ), const SizedBox(width: 8), Expanded( child: Text( unlocked ? '已解锁无限记分权益' : '免费试用剩余 ${config.remainingTrialCount}/${config.freeTrialLimit} 次', style: TextStyle( fontSize: 13, color: unlocked ? AppTheme.primaryDark : AppTheme.accent, fontWeight: FontWeight.w600, ), ), ), ], ), ); }, ); } } class _EntryButtons extends StatelessWidget { const _EntryButtons({ required this.onOpenRecords, required this.onStartScoring, }); final VoidCallback onOpenRecords; final VoidCallback onStartScoring; @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: onOpenRecords, icon: const Icon(Icons.list_alt_outlined), label: const Text('计分记录'), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton.icon( onPressed: onStartScoring, icon: const Icon(Icons.play_arrow), label: const Text('开始记分'), ), ), ], ); } }