feat(mine): 重排我的页并新增迷你播放器
This commit is contained in:
1 parent
5aa8170951
commit
cbc6d80cd4
21 files changed
+860
-531
No files matched your search
+130
-43
@@ -5,8 +5,11 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../core/data/models/drama.dart';
|
||||
import '../../core/data/models/watch_progress.dart';
|
||||
import '../../core/data/repositories/watch_history_repository.dart';
|
||||
import '../../core/data/services/auth_controller.dart';
|
||||
import '../../core/data/services/catalog_controller.dart';
|
||||
import '../../core/data/services/locale_controller.dart';
|
||||
import '../../core/data/services/wallet_controller.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
@@ -17,10 +20,11 @@ import '../../core/widgets/auth_gate.dart';
|
||||
import '../../core/widgets/plain_page_scaffold.dart';
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../downloads/downloads_page.dart';
|
||||
import '../favorites/favorites_page.dart';
|
||||
import '../account/account_profile_page.dart';
|
||||
import '../filter/filter_page.dart';
|
||||
import '../notices/notices_page.dart';
|
||||
import '../player/open_player.dart';
|
||||
import '../settings/settings_page.dart';
|
||||
import '../store/recharge_page.dart';
|
||||
import '../store/vip_page.dart';
|
||||
import '../wallet/wallet_page.dart';
|
||||
import '../watch_records/watch_records_page.dart';
|
||||
@@ -29,6 +33,7 @@ import 'mine_logic.dart';
|
||||
import 'mine_types.dart';
|
||||
import 'widgets/mine_header.dart';
|
||||
import 'widgets/mine_menu.dart';
|
||||
import 'widgets/mine_mini_player.dart';
|
||||
|
||||
/// K28-B 我的页。
|
||||
class MyPage extends StatefulWidget {
|
||||
@@ -47,6 +52,7 @@ class MyPage extends StatefulWidget {
|
||||
|
||||
class _MyPageState extends State<MyPage> {
|
||||
late final MineLogic logic;
|
||||
bool _miniDismissed = false;
|
||||
|
||||
bool get embedded => widget.callbacks.embedded;
|
||||
|
||||
@@ -126,53 +132,51 @@ class _MyPageState extends State<MyPage> {
|
||||
}
|
||||
|
||||
Widget _body(BuildContext context, AppLocalizations l10n) {
|
||||
final auth = context.watch<AuthController>();
|
||||
final wallet = context.watch<WalletController>();
|
||||
final locale = context.watch<LocaleController>();
|
||||
return ListView(
|
||||
key: const Key('mine-page-scroll'),
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppDimens.pageHorizontal,
|
||||
0,
|
||||
AppDimens.pageHorizontal,
|
||||
AppDimens.spaceXl,
|
||||
),
|
||||
final recent = _miniRecord;
|
||||
return Stack(
|
||||
children: [
|
||||
MineHeader(onTap: () => _openDestination(MineDestination.settings)),
|
||||
const SizedBox(height: AppDimens.spaceMd),
|
||||
MineShortcutGrid(onOpen: _openDestination),
|
||||
const SizedBox(height: AppDimens.spaceMd),
|
||||
MineWalletCard(
|
||||
balance: _walletValue(l10n, auth, wallet),
|
||||
onOpen: () => _openDestination(MineDestination.wallet),
|
||||
onRecharge: _openRecharge,
|
||||
),
|
||||
const SizedBox(height: AppDimens.spaceMd),
|
||||
MineVipCard(onOpen: () => _openDestination(MineDestination.vip)),
|
||||
const SizedBox(height: AppDimens.spaceMd),
|
||||
MineMenuList(
|
||||
onOpen: _openDestination,
|
||||
onLanguage: _openLanguage,
|
||||
languageLabel: _languageLabel(l10n, locale.locale),
|
||||
Positioned.fill(
|
||||
child: ListView(
|
||||
key: const Key('mine-page-scroll'),
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
AppDimens.pageHorizontal,
|
||||
0,
|
||||
AppDimens.pageHorizontal,
|
||||
recent == null ? AppDimens.spaceXl : 156,
|
||||
),
|
||||
children: [
|
||||
MineHeader(onLogin: _openLogin, onEdit: _openEditActions),
|
||||
MineVipBanner(
|
||||
onOpen: () => _openDestination(MineDestination.vip),
|
||||
),
|
||||
const SizedBox(height: AppDimens.spaceMd),
|
||||
MineMenuList(
|
||||
onOpen: _openDestination,
|
||||
onLanguage: _openLanguage,
|
||||
languageLabel: _languageLabel(l10n, locale.locale),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (recent != null)
|
||||
Positioned(
|
||||
left: AppDimens.pageHorizontal,
|
||||
bottom: AppDimens.spaceMd,
|
||||
child: MineMiniPlayer(
|
||||
title: _miniTitle(l10n, recent),
|
||||
coverUrl: recent.dramaCover?.trim() ?? '',
|
||||
episodeLabel: l10n.episodeLabel(recent.episodeNumber ?? 0),
|
||||
onTap: () => unawaited(_openRecent(recent)),
|
||||
onClose: () => setState(() => _miniDismissed = true),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _walletValue(
|
||||
AppLocalizations l10n,
|
||||
AuthController auth,
|
||||
WalletController wallet,
|
||||
) {
|
||||
if (!auth.isSignedIn) return l10n.logInHint;
|
||||
if (wallet.snapshot == null) return l10n.coins;
|
||||
return l10n.walletBalanceShort(wallet.balance);
|
||||
}
|
||||
|
||||
void _openDestination(MineDestination destination) {
|
||||
switch (destination) {
|
||||
case MineDestination.watchlist:
|
||||
_push(const FavoritesPage());
|
||||
case MineDestination.history:
|
||||
_push(const WatchRecordsPage());
|
||||
case MineDestination.downloads:
|
||||
@@ -181,6 +185,8 @@ class _MyPageState extends State<MyPage> {
|
||||
_openProtected(const WalletPage());
|
||||
case MineDestination.vip:
|
||||
_openVip();
|
||||
case MineDestination.preferences:
|
||||
_push(const FilterPage());
|
||||
case MineDestination.works:
|
||||
_pushPlaceholder(MinePlaceholderKind.works);
|
||||
case MineDestination.creatorCenter:
|
||||
@@ -194,6 +200,64 @@ class _MyPageState extends State<MyPage> {
|
||||
}
|
||||
}
|
||||
|
||||
void _openLogin() {
|
||||
unawaited(openAuthPage(context));
|
||||
}
|
||||
|
||||
Future<void> _openEditActions() async {
|
||||
final selected = await showModalBottomSheet<String>(
|
||||
context: context,
|
||||
backgroundColor: AppColors.transparent,
|
||||
showDragHandle: false,
|
||||
builder: (sheetContext) => Material(
|
||||
color: AppColors.mineCard,
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(AppDimens.mineCardRadius),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
key: const Key('mine-edit-profile'),
|
||||
title: Text(AppLocalizations.of(sheetContext)!.accountProfile),
|
||||
onTap: () => Navigator.pop(sheetContext, 'profile'),
|
||||
),
|
||||
ListTile(
|
||||
key: const Key('mine-edit-works'),
|
||||
title: Text(AppLocalizations.of(sheetContext)!.myWorks),
|
||||
onTap: () => Navigator.pop(sheetContext, 'works'),
|
||||
),
|
||||
ListTile(
|
||||
key: const Key('mine-edit-creator'),
|
||||
title: Text(AppLocalizations.of(sheetContext)!.creatorCenter),
|
||||
onTap: () => Navigator.pop(sheetContext, 'creator'),
|
||||
),
|
||||
ListTile(
|
||||
key: const Key('mine-edit-notices'),
|
||||
title: Text(AppLocalizations.of(sheetContext)!.notices),
|
||||
onTap: () => Navigator.pop(sheetContext, 'notices'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!mounted || selected == null) return;
|
||||
switch (selected) {
|
||||
case 'profile':
|
||||
_push(const AccountProfilePage());
|
||||
case 'works':
|
||||
_pushPlaceholder(MinePlaceholderKind.works);
|
||||
case 'creator':
|
||||
_pushPlaceholder(MinePlaceholderKind.creator);
|
||||
case 'notices':
|
||||
_push(const NoticesPage());
|
||||
}
|
||||
}
|
||||
|
||||
void _push(Widget page) {
|
||||
Navigator.of(context).push<void>(MaterialPageRoute(builder: (_) => page));
|
||||
}
|
||||
@@ -236,9 +300,32 @@ class _MyPageState extends State<MyPage> {
|
||||
await context.read<LocaleController>().select(selected);
|
||||
}
|
||||
|
||||
void _openRecharge() {
|
||||
final wallet = context.read<WalletController>();
|
||||
_openProtected(RechargePage(onPurchased: () => unawaited(wallet.load())));
|
||||
WatchProgress? get _miniRecord {
|
||||
if (_miniDismissed || logic.recent.isEmpty) return null;
|
||||
final recent = logic.recent.first;
|
||||
return recent.episodeId.isEmpty ? null : recent;
|
||||
}
|
||||
|
||||
String _miniTitle(AppLocalizations l10n, WatchProgress recent) {
|
||||
final title = recent.dramaTitle?.trim() ?? '';
|
||||
return title.isEmpty ? l10n.episodeLabel(recent.episodeNumber ?? 0) : title;
|
||||
}
|
||||
|
||||
Future<void> _openRecent(WatchProgress recent) async {
|
||||
final catalog = context.read<CatalogController>();
|
||||
final seed = Drama(
|
||||
id: recent.dramaId,
|
||||
title: recent.dramaTitle?.trim() ?? '',
|
||||
synopsis: '',
|
||||
posterUrl: recent.dramaCover?.trim() ?? '',
|
||||
);
|
||||
try {
|
||||
final detail = await catalog.requireDetail(seed);
|
||||
if (!mounted) return;
|
||||
await playDramaFromCatalog(context, detail, episodeId: recent.episodeId);
|
||||
} catch (_) {
|
||||
// 最近记录失效时不投射错误页,等待下一次记录刷新。
|
||||
}
|
||||
}
|
||||
|
||||
Widget _bottomNav() {
|
||||
|
||||
Reference in new issue
Block a user