FunyMeeAI/lib/features/history/history_screen.dart

235 lines
6.8 KiB
Dart

import 'package:client_proxy_framework/client_proxy_framework.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../core/app_env.dart';
import '../../design/pencil_theme.dart';
import '../../widgets/pencil_chrome.dart';
import 'credit_record_tab.dart';
import 'widgets/history_grid_card.dart';
/// `WBRp4` My History — 黄→白渐变、顶栏、双 Tab、24h 提示、双列卡片。
class HistoryScreen extends StatefulWidget {
const HistoryScreen({super.key});
@override
State<HistoryScreen> createState() => _HistoryScreenState();
}
class _HistoryScreenState extends State<HistoryScreen> {
int _tab = 0;
bool _loading = true;
String? _error;
List<MyTaskItem> _items = [];
Map<String, String> _localCovers = {};
@override
void initState() {
super.initState();
_load();
}
Future<void> _load() async {
setState(() {
_loading = true;
_error = null;
});
final res = await ImageApi.getMyTasks(
app: currentBackendAppType(),
page: '1',
pageSize: '30',
);
if (!mounted) return;
if (!res.isSuccess || res.data == null) {
setState(() {
_loading = false;
_error = res.msg.isNotEmpty ? res.msg : 'Failed to load';
});
return;
}
final tasks = res.data!.tasks ?? [];
final locals = await ImageTaskHistory.localCoverPathsForMyTaskItems(tasks);
setState(() {
_loading = false;
_items = tasks;
_localCovers = locals;
});
}
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
gradient: PencilTheme.yellowWhitePageGradient,
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
bottom: false,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(4, 0, 4, 8),
child: SizedBox(
height: 58,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
PencilRoundBackButton(
onPressed: () => Navigator.of(context).pop(),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_headerTab('My History', 0),
const SizedBox(width: 26),
_headerTab('Credit Record', 1),
],
),
),
const SizedBox(width: 44),
],
),
),
),
),
Expanded(
child: _tab == 0 ? _myHistoryBody() : const CreditRecordTab(),
),
],
),
),
),
);
}
Widget _headerTab(String label, int index) {
final selected = _tab == index;
return InkWell(
onTap: () => setState(() => _tab = index),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
label,
style: GoogleFonts.inter(
fontSize: 17,
fontWeight: selected ? FontWeight.w600 : FontWeight.w500,
fontStyle: FontStyle.italic,
color: selected ? PencilTheme.ink : PencilTheme.inkSoft,
),
),
const SizedBox(height: 8),
if (selected)
Container(
width: 50,
height: 4,
decoration: BoxDecoration(
color: PencilTheme.underlineGold,
borderRadius: BorderRadius.circular(2),
),
)
else
const SizedBox(height: 4),
],
),
);
}
Widget _myHistoryBody() {
if (_loading) {
return const Center(child: CircularProgressIndicator());
}
if (_error != null) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(_error!),
TextButton(onPressed: _load, child: const Text('Retry')),
],
),
);
}
return RefreshIndicator(
onRefresh: _load,
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 12),
sliver: SliverToBoxAdapter(child: _expiryNotice()),
),
if (_items.isEmpty)
SliverFillRemaining(
child: Center(
child: Text('No tasks yet.',
style: GoogleFonts.inter(color: PencilTheme.inkSoft)),
),
)
else
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 28),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 14,
crossAxisSpacing: 14,
childAspectRatio: 171 / 182,
),
delegate: SliverChildBuilderDelegate(
(context, i) {
final t = _items[i];
final id = t.taskId ?? '';
return HistoryGridCard(
item: t,
localCoverPath:
id.isEmpty ? null : _localCovers[id],
onDownload: () {},
);
},
childCount: _items.length,
),
),
),
],
),
);
}
Widget _expiryNotice() {
return Container(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 10),
decoration: BoxDecoration(
color: PencilTheme.expiryBg,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: PencilTheme.expiryBorder),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'24-hour expiry',
style: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: PencilTheme.expiryHead,
),
),
const SizedBox(height: 4),
Text(
'Each item is kept for 24 hours after creation. Download before it expires.',
style: GoogleFonts.inter(
fontSize: 11,
height: 1.35,
color: PencilTheme.expiryBody,
),
),
],
),
);
}
}