145 lines
4.5 KiB
Dart
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)));
|
|
}
|
|
}
|