Files
ReelShort/lib/pages/favorites/favorites_page.dart
T

145 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/platform/haptics.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/auth_gate.dart';
import '../../core/widgets/empty_state.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 Theme(
data: AppTheme.light(),
child: Scaffold(
backgroundColor: AppColors.ink,
appBar: AppBar(
backgroundColor: AppColors.ink,
foregroundColor: Colors.white,
title: Text(l10n.myFavorites),
),
body: SafeArea(
top: false,
child: 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 EmptyState(
key: const Key('favorites-error'),
icon: Icons.cloud_off_rounded,
title: errorText(l10n, favorites.error, l10n.favoritesError),
actionLabel: l10n.retry,
actionKey: const Key('favorites-retry'),
onAction: () => unawaited(favorites.load()),
);
}
if (favorites.cards.isEmpty) {
return EmptyState(
key: const Key('favorites-empty'),
icon: Icons.favorite_border_rounded,
title: l10n.noFavorites,
subtitle: l10n.emptyHint,
);
}
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: AppColors.favorite,
),
),
);
},
),
);
}
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 {
unawaited(context.read<Haptics>().lightImpact());
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)));
}
}