149 lines
4.5 KiB
Dart
149 lines
4.5 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../../core/data/models/drama.dart';
|
|
import '../../core/data/services/auth_controller.dart';
|
|
import '../../core/data/services/favorite_controller.dart';
|
|
import '../../core/formatting/error_text.dart';
|
|
import '../../core/widgets/auth_gate.dart';
|
|
import '../../core/widgets/library_drama_tile.dart';
|
|
import '../../l10n/app_localizations.dart';
|
|
import '../drama_detail/drama_detail_view.dart';
|
|
import 'favorites_binding.dart';
|
|
|
|
/// 独立我的收藏页。
|
|
class FavoritesPage extends StatefulWidget {
|
|
const FavoritesPage({super.key});
|
|
|
|
@override
|
|
State<FavoritesPage> createState() => _FavoritesPageState();
|
|
}
|
|
|
|
class _FavoritesPageState extends State<FavoritesPage> {
|
|
bool _requested = false;
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
final signedIn = context.watch<AuthController>().isSignedIn;
|
|
if (!signedIn) {
|
|
_requested = false;
|
|
return;
|
|
}
|
|
if (_requested) return;
|
|
_requested = true;
|
|
final favorites = context.read<FavoriteController>();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (mounted) unawaited(FavoritesBinding().load(favorites));
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final signedIn = context.watch<AuthController>().isSignedIn;
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFF0B0C10),
|
|
appBar: AppBar(
|
|
backgroundColor: const Color(0xFF0B0C10),
|
|
foregroundColor: Colors.white,
|
|
title: Text(l10n.myFavorites),
|
|
),
|
|
body: signedIn
|
|
? _FavoritesBody(favorites: context.watch<FavoriteController>())
|
|
: const AuthGatePanel(),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _FavoritesBody extends StatelessWidget {
|
|
const _FavoritesBody({required this.favorites});
|
|
|
|
final FavoriteController favorites;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
if (favorites.loading && favorites.cards.isEmpty) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
if (favorites.error != null && favorites.cards.isEmpty) {
|
|
return Center(
|
|
key: const Key('favorites-error'),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
errorText(l10n, favorites.error, l10n.favoritesError),
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(color: Colors.white70),
|
|
),
|
|
const SizedBox(height: 14),
|
|
FilledButton(
|
|
key: const Key('favorites-retry'),
|
|
onPressed: () => unawaited(favorites.load()),
|
|
child: Text(l10n.retry),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
if (favorites.cards.isEmpty) {
|
|
return Center(
|
|
key: const Key('favorites-empty'),
|
|
child: Text(
|
|
l10n.noFavorites,
|
|
style: const TextStyle(color: Colors.white70),
|
|
),
|
|
);
|
|
}
|
|
return RefreshIndicator(
|
|
onRefresh: favorites.load,
|
|
child: ListView.separated(
|
|
padding: const EdgeInsets.fromLTRB(16, 12, 16, 28),
|
|
itemCount: favorites.cards.length,
|
|
separatorBuilder: (_, _) => const SizedBox(height: 10),
|
|
itemBuilder: (context, index) {
|
|
final drama = favorites.cards[index];
|
|
return LibraryDramaTile(
|
|
key: ValueKey('favorite-card-${drama.id}'),
|
|
drama: drama,
|
|
onTap: () => _open(context, drama),
|
|
trailing: IconButton(
|
|
key: ValueKey('favorite-remove-${drama.id}'),
|
|
tooltip: l10n.remove,
|
|
onPressed: () => _remove(context, drama.id),
|
|
icon: const Icon(
|
|
Icons.favorite_rounded,
|
|
color: Color(0xFFD74A7D),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _open(BuildContext context, Drama drama) async {
|
|
await Navigator.of(context).push<void>(
|
|
MaterialPageRoute(builder: (_) => DramaDetailPage(drama: drama)),
|
|
);
|
|
}
|
|
|
|
Future<void> _remove(BuildContext context, String dramaId) async {
|
|
final ok = await favorites.remove(dramaId);
|
|
if (ok || !context.mounted) return;
|
|
final l10n = AppLocalizations.of(context)!;
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(SnackBar(content: Text(l10n.favoriteToggleFailed)));
|
|
}
|
|
}
|