feat: phase1 UI prep — 设备ID系统传递 + 国家本地化展示 + 继续观看直进播放页

设备标识: Android SSAID/ iOS IDFV 原生通道 → Dart DeviceIdProvider
国家解析: 注册/登录从后端 IP → 国家/语言/时区/币种 自动落库
账号资料: 全部本地化展示 (account_display.dart)
我的页: 继续观看卡片点击直接进播放页
This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-27 15:45:20 +08:00
1 parent 1b19513efb
commit a3f3481f4f
15 files changed
+468 -25

No files matched your search

+30 -7
View File
@@ -8,6 +8,7 @@ import 'package:provider/provider.dart';
import '../../core/data/models/account_profile.dart';
import '../../core/data/repositories/account_repository.dart';
import '../../core/data/services/auth_controller.dart';
import '../../core/formatting/account_display.dart';
import '../../core/formatting/error_text.dart';
import '../../core/formatting/localized_date_time.dart';
import '../../core/theme/app_colors.dart';
@@ -225,6 +226,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
);
Widget _readOnlyCard(AccountProfile profile, AppLocalizations l10n) {
final localeName = Localizations.localeOf(context).languageCode;
return _AccountCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -238,13 +240,34 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
),
const SizedBox(height: 10),
_info(l10n.accountMaskedAccount, profile.maskedTarget),
_info(l10n.accountCountry, profile.countryCode),
_info(
l10n.accountCountry,
countryName(profile.countryCode, localeName),
),
_info(l10n.accountCurrency, profile.currency),
_info(l10n.accountStatus, profile.status),
_info(l10n.accountLanguage, profile.language),
_info(l10n.accountTimezone, profile.timezone),
_info(l10n.accountRegion, profile.regionCode),
_info(l10n.accountVipLevel, '${profile.vipLevel}'),
_info(
l10n.accountStatus,
accountStatusText(profile.status, localeName),
),
_info(
l10n.accountLanguage,
languageName(profile.language, localeName),
),
_info(
l10n.accountTimezone,
timezoneName(profile.timezone, localeName),
),
// region 与 country 后端写的是同一个值,重复时不再单列一行
if (profile.regionCode.isNotEmpty &&
profile.regionCode != profile.countryCode)
_info(
l10n.accountRegion,
countryName(profile.regionCode, localeName),
),
_info(
l10n.accountVipLevel,
vipLevelText(profile.vipLevel, localeName),
),
if (profile.createdAt != null)
_info(
l10n.accountCreatedAt,
@@ -275,7 +298,7 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
),
Flexible(
child: Text(
value.isEmpty ? '-' : value,
value.isEmpty ? '—' : value,
textAlign: TextAlign.right,
style: const TextStyle(fontSize: AppDimens.body),
),
+35
View File
@@ -6,8 +6,11 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.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/favorite_controller.dart';
import '../../core/data/services/locale_controller.dart';
import '../../core/data/services/wallet_controller.dart';
@@ -22,6 +25,7 @@ import '../downloads/downloads_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/vip_page.dart';
import '../wallet/wallet_page.dart';
@@ -222,6 +226,7 @@ class _MyPageState extends State<MyPage> {
const SizedBox(height: AppDimens.mineSectionGap),
MineContinueWatching(
records: continueWatching,
onOpenRecord: _openContinueRecord,
onOpenAll: _openHistoryTab,
),
],
@@ -294,6 +299,36 @@ class _MyPageState extends State<MyPage> {
unawaited(_openHistoryPage());
}
/// 继续观看卡片:补齐剧集后直接进全屏播放页,播放器自己会定位到记录的秒数。
Future<void> _openContinueRecord(WatchProgress record) async {
final catalog = context.read<CatalogController>();
// 续看记录只有剧的概要信息,先拼一个占位对象让目录去取完整详情
final summary = Drama(
id: record.dramaId,
title: record.dramaTitle ?? '',
synopsis: '',
posterUrl: record.dramaCover ?? '',
);
Drama drama;
try {
drama = await catalog.requireDetail(summary);
} catch (_) {
if (!mounted) return;
final l10n = AppLocalizations.of(context)!;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(l10n.detailLoadFailed)));
return;
}
if (!mounted) return;
await playDramaFromCatalog(
context,
drama,
episodeId: record.episodeId.isEmpty ? null : record.episodeId,
);
if (mounted) unawaited(logic.refreshRecent());
}
Future<void> _openHistoryPage() async {
await _push(const WatchRecordsPage());
if (mounted) unawaited(logic.refreshRecent());
@@ -12,10 +12,14 @@ class MineContinueWatching extends StatelessWidget {
const MineContinueWatching({
super.key,
required this.records,
required this.onOpenRecord,
required this.onOpenAll,
});
final List<WatchProgress> records;
/// 单张卡片:直接进入该剧的播放页,并定位到记录的集。
final ValueChanged<WatchProgress> onOpenRecord;
final VoidCallback onOpenAll;
@override
@@ -94,7 +98,11 @@ class MineContinueWatching extends StatelessWidget {
);
}
Widget _card(AppLocalizations l10n, WatchProgress record, String languageCode) {
Widget _card(
AppLocalizations l10n,
WatchProgress record,
String languageCode,
) {
final title = record.localizedTitle(languageCode).trim();
final episode = record.episodeNumber;
final hasProgress = record.duration > Duration.zero;
@@ -103,7 +111,7 @@ class MineContinueWatching extends StatelessWidget {
child: InkWell(
key: Key('mine-continue-${record.dramaId}'),
borderRadius: BorderRadius.circular(AppDimens.homeCardRadius),
onTap: onOpenAll,
onTap: () => onOpenRecord(record),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [