feat(app): 完善播放协议搜索与空状态

This commit is contained in:
Zhou Weidong committed 2026-10-23 12:38:47 +08:00
1 parent 671e0ce143
commit ef29fc5f15
85 files changed
+2976 -768

No files matched your search

+44 -14
View File
@@ -363,7 +363,7 @@ class _PlayerPageState extends State<PlayerPage>
final lastDoubleTapAt = _lastDoubleTapAt;
return lastDoubleTapAt != null &&
DateTime.now().difference(lastDoubleTapAt) <
const Duration(milliseconds: 350);
const Duration(milliseconds: 500);
}
void _handleDoubleTapLike(
@@ -545,14 +545,18 @@ class _PlayerPageState extends State<PlayerPage>
(selected ? logic.session.active : null) ??
logic.session.preparedPlayerAt(index);
final videoPort = port is VideoPlayerPort ? port : null;
final ready = videoPort?.controller.value.isInitialized ?? false;
final handoff = selected ? logic.session.handoffAt(index) : null;
final handoffPort = handoff is VideoPlayerPort ? handoff : null;
// 测试播放器没有纹理。有播放器但不是视频控制器时视为已就绪,避免加载圈一直转。
final videoReady = videoPort?.controller.value.isInitialized ?? false;
final ready = videoReady || port != null;
final theater = logic.pip || logic.pipPreparing;
final locked = logic.session.isLocked || episode.locked;
if (selected && ready) {
if (selected && videoPort != null && videoPort.controller.value.isInitialized) {
unawaited(
_spriteAxisService.prepareAt(
episode.thumbnailsUrl,
videoPort!.controller.value.position,
videoPort.controller.value.position,
),
);
}
@@ -567,11 +571,30 @@ class _PlayerPageState extends State<PlayerPage>
),
PlayerVideoSurface(
controller: videoPort?.controller,
handoff: handoffPort?.controller,
videoUrl: videoPort?.controller.dataSource ?? episode.videoUrl,
theater: theater,
),
if (selected &&
!theater &&
!ready &&
!logic.ticketLoading &&
logic.ticketError == null &&
logic.session.error == null &&
logic.blockedEpisodeId != episode.id &&
!logic.session.isLocked)
const Center(
child: SizedBox.square(
dimension: _playerLoadingSize,
child: CircularProgressIndicator(
key: Key('player-video-loading'),
color: AppColors.blue,
strokeWidth: _playerLoadingStrokeWidth,
),
),
),
// 票据三态之一:起播前正在向后端要签名地址
if (selected && logic.ticketLoading)
if (selected && !theater && logic.ticketLoading)
const Center(
child: SizedBox.square(
dimension: _playerLoadingSize,
@@ -583,7 +606,10 @@ class _PlayerPageState extends State<PlayerPage>
),
),
// 票据三态之二:拿票失败(断网/服务异常)→ 可重试,不假装在加载
if (selected && !logic.ticketLoading && logic.ticketError != null)
if (selected &&
!theater &&
!logic.ticketLoading &&
logic.ticketError != null)
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 36),
@@ -610,7 +636,7 @@ class _PlayerPageState extends State<PlayerPage>
),
),
),
if (selected && !ready && logic.session.error != null)
if (selected && !theater && !ready && logic.session.error != null)
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 36),
@@ -634,6 +660,7 @@ class _PlayerPageState extends State<PlayerPage>
),
// 没有票据信息时的兜底(例如详情里就没地址、也没走票据流程)
if (selected &&
!theater &&
!ready &&
logic.blockedEpisodeId != episode.id &&
logic.session.error == null &&
@@ -668,7 +695,7 @@ class _PlayerPageState extends State<PlayerPage>
l10n: l10n,
drama: drama,
episode: episode,
controller: ready ? videoPort!.controller : null,
controller: videoReady ? videoPort!.controller : null,
locked: locked,
visible: _chromeVisible,
boosting: _boosting,
@@ -699,11 +726,11 @@ class _PlayerPageState extends State<PlayerPage>
},
onEpisodes: () => logic.showEpisodes(context),
onMore: _openCurrentMore,
onScrubStart: ready ? () => logic.beginScrub(videoPort!) : null,
onScrubStart: videoReady ? () => logic.beginScrub(videoPort!) : null,
onScrubEnd: _finishFeedScrub,
onScrubVisibilityChanged: _setScrubbing,
cancelScrubRequest: widget.scrubCancelRequest,
onSeek: ready
onSeek: videoReady
? (position) => logic.endScrub(context, videoPort!, position)
: null,
thumbnailService: _thumbnailService,
@@ -715,7 +742,7 @@ class _PlayerPageState extends State<PlayerPage>
l10n: l10n,
drama: drama,
episode: episode,
controller: ready ? videoPort!.controller : null,
controller: videoReady ? videoPort!.controller : null,
locked: locked,
liked: interactions.isEpisodeLiked(episode.id),
likeCount:
@@ -751,14 +778,14 @@ class _PlayerPageState extends State<PlayerPage>
onSave: () => unawaited(_toggleSave(context, drama)),
onShare: () => logic.shareDrama(context, drama),
onMore: () => _openMore(context, episode),
onScrubStart: ready
onScrubStart: videoReady
? () {
_setScrubbing(true);
logic.beginScrub(videoPort!);
}
: null,
onScrubEnd: () => _setScrubbing(false),
onSeek: ready
onSeek: videoReady
? (position) => logic.seekTo(context, videoPort!, position)
: null,
onLongPressCenter: () => _openMore(context, episode),
@@ -768,7 +795,10 @@ class _PlayerPageState extends State<PlayerPage>
controlsVisible: _chromeVisible,
),
// 票据三态之三:后端判定不可播放 —— 用 accessType 驱动文案,不在前端猜
if (selected && !ready && logic.blockedEpisodeId == episode.id)
if (selected &&
!theater &&
!ready &&
logic.blockedEpisodeId == episode.id)
PlayerBlockedNotice(
l10n: l10n,
episode: episode,