import 'package:flutter/material.dart'; import 'planet_data.dart'; import '../../core/i18n/app_locale.dart'; import '../../core/ui/bottom_inset.dart'; class PlanetHistoryScreen extends StatefulWidget { const PlanetHistoryScreen({ super.key, required this.repository, required this.fandomName, }); final PlanetRepository repository; final String fandomName; @override State createState() => _PlanetHistoryScreenState(); } class _PlanetHistoryScreenState extends State { final _items = []; String? _cursor; bool _loading = false; bool _loaded = false; bool _failed = false; bool _personal = false; @override void initState() { super.initState(); _load(); } Future _load() async { if (_loading) return; setState(() { _loading = true; _failed = false; }); try { final page = await widget.repository.history( cursor: _cursor, personal: _personal, ); if (!mounted) return; setState(() { final ids = _items.map((item) => item.id).toSet(); _items.addAll(page.items.where((item) => ids.add(item.id))); _cursor = page.nextCursor; _loaded = true; }); } catch (_) { if (mounted) setState(() => _failed = true); } finally { if (mounted) setState(() => _loading = false); } } @override Widget build(BuildContext context) => Scaffold( appBar: AppBar(title: Text(context.l10n.historyTitle)), body: ListView( padding: withBottomInset(context, const EdgeInsets.all(16)), children: [ Text(widget.fandomName, style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 16), SegmentedButton( segments: [ ButtonSegment( value: false, label: Text(context.l10n.homeFandomHistory), ), ButtonSegment(value: true, label: Text(context.l10n.historyMine)), ], selected: {_personal}, onSelectionChanged: _loading ? null : (selection) { if (selection.single == _personal) return; setState(() { _personal = selection.single; _items.clear(); _cursor = null; _loaded = false; }); _load(); }, ), const SizedBox(height: 16), for (final item in _items) _Stamp( historic: item.kind.isHistoric, child: ListTile( contentPadding: EdgeInsets.zero, leading: Icon(switch (item.kind) { PlanetHistoryKind.evolution => Icons.auto_awesome, PlanetHistoryKind.hibernation => Icons.bedtime_outlined, PlanetHistoryKind.wakeUp => Icons.wb_sunny_outlined, PlanetHistoryKind.zap => Icons.bolt, PlanetHistoryKind.questReward => Icons.task_alt, PlanetHistoryKind.companyAdBonus => Icons.play_circle_outline, PlanetHistoryKind.funding => Icons.favorite_border, PlanetHistoryKind.eclipseSuccess => Icons.shield_outlined, PlanetHistoryKind.emblemEvolution => Icons.military_tech_outlined, PlanetHistoryKind.founderBadge => Icons.workspace_premium_outlined, }), title: Text(switch (item.kind) { PlanetHistoryKind.evolution => context.l10n.historyEvolution( '${item.fromStage}', '${item.toStage}', ), PlanetHistoryKind.hibernation => context.l10n.historyHibernation, PlanetHistoryKind.wakeUp => context.l10n.historyWakeUp, PlanetHistoryKind.zap => context.l10n.historyZap, PlanetHistoryKind.questReward => context.l10n.historyQuest, PlanetHistoryKind.companyAdBonus => context.l10n.historyAdBonus, PlanetHistoryKind.funding => context.l10n.historyFunding, PlanetHistoryKind.eclipseSuccess => context.l10n.historyEclipseSuccess, PlanetHistoryKind.emblemEvolution => '${context.l10n.historyEmblemEvolution} · STAGE ${item.toStage ?? ''}', PlanetHistoryKind.founderBadge => '${context.l10n.historyFounderBadge} · #${(item.memberNo ?? 0).toString().padLeft(3, '0')}', }), subtitle: Text( [ MaterialLocalizations.of(context) .formatMediumDate(item.occurredAt.toLocal()), if (item.amount != null) context.l10n.historyGrowthPop('${item.amount}'), if (item.participationConfirmed) context.l10n.historyJoined, ].join('\n'), ), ), ), if (_loading) const Center(child: CircularProgressIndicator()), if (_failed) ...[ Text(context.l10n.historyLoadFailed), TextButton(onPressed: _load, child: Text(context.l10n.commonRetry)), ] else if (_loaded && _items.isEmpty && _cursor == null) Text( _personal ? context.l10n.historyMineEmpty : context.l10n.historyFandomEmpty, ), if (!_loading && !_failed && _cursor != null) TextButton(onPressed: _load, child: Text(context.l10n.historyMore)), ], ), ); } /// 「역사적 사건」(UX·FX v1.5 §7.2)은 테두리 Stamp 와 표지로 일반 활동 줄과 가른다. /// 앱 Figma 에 PASSPORT 기록 화면이 없어 화면 테마 색만 쓴다. class _Stamp extends StatelessWidget { const _Stamp({required this.historic, required this.child}); final bool historic; final Widget child; @override Widget build(BuildContext context) { if (!historic) return child; final scheme = Theme.of(context).colorScheme; return Container( margin: const EdgeInsets.symmetric(vertical: 4), padding: const EdgeInsets.fromLTRB(12, 8, 12, 0), decoration: BoxDecoration( border: Border.all(color: scheme.primary), borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.l10n.historyHistoric, style: Theme.of(context).textTheme.labelSmall ?.copyWith(color: scheme.primary), ), child, ], ), ); } }