Merge branch 'main' into feature-updateVideoDetailStructure
This commit is contained in:
@ -4,6 +4,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
@ -96,11 +97,14 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
return SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: Lottie.asset(
|
||||
'assets/loading.json',
|
||||
width: 200,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -589,8 +593,4 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Widget StaffPanel(BuildContext context, videoIntroController) {
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ class _FavPanelState extends State<FavPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
AppBar(
|
||||
|
||||
@ -57,7 +57,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
AppBar(
|
||||
|
||||
@ -12,7 +12,7 @@ class MenuRow extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@ -84,7 +84,7 @@ class MenuRow extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: selectStatus
|
||||
? Theme.of(context).colorScheme.onBackground
|
||||
? Theme.of(context).colorScheme.onSurface
|
||||
: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/http/reply.dart';
|
||||
@ -15,7 +14,6 @@ class VideoReplyController extends GetxController {
|
||||
this.rpid,
|
||||
this.replyLevel,
|
||||
);
|
||||
final ScrollController scrollController = ScrollController();
|
||||
// 视频aid 请求时使用的oid
|
||||
int? aid;
|
||||
// 层级 2为楼中楼
|
||||
|
||||
@ -67,13 +67,12 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
vsync: this, duration: const Duration(milliseconds: 300));
|
||||
|
||||
_futureBuilderFuture = _videoReplyController.queryReplyList();
|
||||
|
||||
scrollController = ScrollController();
|
||||
fabAnimationCtr.forward();
|
||||
scrollListener();
|
||||
}
|
||||
|
||||
void scrollListener() {
|
||||
scrollController = _videoReplyController.scrollController;
|
||||
scrollController.addListener(
|
||||
() {
|
||||
if (scrollController.position.pixels >=
|
||||
@ -185,7 +184,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
builder: (BuildContext context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
var data = snapshot.data;
|
||||
if (data['status']) {
|
||||
if (_videoReplyController.replyList.isNotEmpty ||
|
||||
(data && data['status'])) {
|
||||
// 请求成功
|
||||
return Obx(
|
||||
() => _videoReplyController.isLoadingMore &&
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:appscheme/appscheme.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -11,6 +12,7 @@ import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/pages/preview/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||
import 'package:pilipala/utils/app_scheme.dart';
|
||||
import 'package:pilipala/utils/feed_back.dart';
|
||||
import 'package:pilipala/utils/id_utils.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
@ -643,34 +645,17 @@ InlineSpan buildContent(
|
||||
'',
|
||||
);
|
||||
} else {
|
||||
final String pathSegment = Uri.parse(matchStr).path;
|
||||
Map matchRes = IdUtils.matchAvorBv(input: pathSegment);
|
||||
List matchKeys = matchRes.keys.toList();
|
||||
if (matchKeys.isNotEmpty) {
|
||||
UrlUtils.matchUrlPush(
|
||||
matchRes.containsKey('AV')
|
||||
? matchRes['AV']! as int
|
||||
: matchRes['BV'],
|
||||
title,
|
||||
matchStr,
|
||||
);
|
||||
} else {
|
||||
final String redirectUrl =
|
||||
await UrlUtils.parseRedirectUrl(matchStr);
|
||||
// if (redirectUrl == matchStr) {
|
||||
// Clipboard.setData(ClipboardData(text: matchStr));
|
||||
// SmartDialog.showToast('地址可能有误');
|
||||
// return;
|
||||
// }
|
||||
Get.toNamed(
|
||||
'/webview',
|
||||
parameters: {
|
||||
'url': redirectUrl,
|
||||
'type': 'url',
|
||||
'pageTitle': title
|
||||
},
|
||||
);
|
||||
}
|
||||
Uri uri = Uri.parse(matchStr);
|
||||
SchemeEntity scheme = SchemeEntity(
|
||||
scheme: uri.scheme,
|
||||
host: uri.host,
|
||||
port: uri.port,
|
||||
path: uri.path,
|
||||
query: uri.queryParameters,
|
||||
source: '',
|
||||
dataString: matchStr,
|
||||
);
|
||||
PiliSchame.fullPathPush(scheme);
|
||||
}
|
||||
} else {
|
||||
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||
|
||||
@ -170,7 +170,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@ -78,7 +78,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: widget.source == 'videoDetail' ? widget.sheetHeight : null,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.source == 'videoDetail')
|
||||
|
||||
@ -99,7 +99,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
fullScreenStatusListener();
|
||||
if (Platform.isAndroid) {
|
||||
floating = vdCtr.floating!;
|
||||
autoEnterPip();
|
||||
}
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
lifecycleListener();
|
||||
@ -128,8 +127,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
}
|
||||
|
||||
// 播放器状态监听
|
||||
void playerListener(PlayerStatus? status) async {
|
||||
playerStatus.value = status!;
|
||||
void playerListener(PlayerStatus status) async {
|
||||
playerStatus.value = status;
|
||||
autoEnterPip(status: status);
|
||||
if (status == PlayerStatus.completed) {
|
||||
// 结束播放退出全屏
|
||||
if (autoExitFullcreen) {
|
||||
@ -181,6 +181,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
plPlayerController!.addStatusLister(playerListener);
|
||||
vdCtr.autoPlay.value = true;
|
||||
vdCtr.isShowCover.value = false;
|
||||
autoEnterPip(status: PlayerStatus.playing);
|
||||
}
|
||||
|
||||
void fullScreenStatusListener() {
|
||||
@ -287,10 +288,12 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
.subscribe(this, ModalRoute.of(context)! as PageRoute);
|
||||
}
|
||||
|
||||
void autoEnterPip() {
|
||||
void autoEnterPip({PlayerStatus? status}) {
|
||||
final String routePath = Get.currentRoute;
|
||||
if (autoPiP && routePath.startsWith('/video')) {
|
||||
floating.toggleAutoPip(autoEnter: autoPiP);
|
||||
floating.toggleAutoPip(
|
||||
autoEnter: autoPiP && status == PlayerStatus.playing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,6 +317,15 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
case 'show' || 'restart':
|
||||
plPlayerController?.danmakuController?.clear();
|
||||
break;
|
||||
case 'pause':
|
||||
vdCtr.hiddenReplyReplyPanel();
|
||||
if (vdCtr.videoType == SearchType.video) {
|
||||
videoIntroController.hiddenEpisodeBottomSheet();
|
||||
}
|
||||
if (vdCtr.videoType == SearchType.media_bangumi) {
|
||||
bangumiIntroController.hiddenEpisodeBottomSheet();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,11 +537,10 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
key: vdCtr.scaffoldKey,
|
||||
backgroundColor: Colors.black,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(0),
|
||||
child: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor: Colors.black,
|
||||
elevation: 0,
|
||||
),
|
||||
),
|
||||
@ -559,8 +570,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
}
|
||||
return SliverAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
// 假装使用一个非空变量,避免Obx检测不到而罢工
|
||||
pinned: vdCtr.autoPlay.value,
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
@ -568,47 +578,42 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
backgroundColor: Colors.black,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: PopScope(
|
||||
canPop: plPlayerController?.isFullScreen.value !=
|
||||
true,
|
||||
onPopInvoked: (bool didPop) {
|
||||
if (plPlayerController?.isFullScreen.value ==
|
||||
true) {
|
||||
plPlayerController!
|
||||
.triggerFullScreen(status: false);
|
||||
}
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape) {
|
||||
verticalScreen();
|
||||
}
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context,
|
||||
BoxConstraints boxConstraints) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
if (isShowing)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: videoPlayerPanel,
|
||||
),
|
||||
canPop:
|
||||
plPlayerController?.isFullScreen.value != true,
|
||||
onPopInvoked: (bool didPop) {
|
||||
if (plPlayerController?.isFullScreen.value ==
|
||||
true) {
|
||||
plPlayerController!
|
||||
.triggerFullScreen(status: false);
|
||||
}
|
||||
if (MediaQuery.of(context).orientation ==
|
||||
Orientation.landscape) {
|
||||
verticalScreen();
|
||||
}
|
||||
},
|
||||
child: Hero(
|
||||
tag: heroTag,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
if (isShowing) videoPlayerPanel,
|
||||
|
||||
/// 关闭自动播放时 手动播放
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: !vdCtr.autoPlay.value &&
|
||||
vdCtr.isShowCover.value,
|
||||
child: Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: handlePlayPanel(),
|
||||
),
|
||||
),
|
||||
/// 关闭自动播放时 手动播放
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: !vdCtr.autoPlay.value &&
|
||||
vdCtr.isShowCover.value,
|
||||
child: Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: handlePlayPanel(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -627,55 +632,51 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
: pinnedHeaderHeight;
|
||||
},
|
||||
onlyOneScrollInBody: true,
|
||||
body: ColoredBox(
|
||||
key: Key(heroTag),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Column(
|
||||
children: [
|
||||
tabbarBuild(),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: vdCtr.tabCtr,
|
||||
children: <Widget>[
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
return CustomScrollView(
|
||||
key: const PageStorageKey<String>('简介'),
|
||||
slivers: <Widget>[
|
||||
if (vdCtr.videoType == SearchType.video) ...[
|
||||
VideoIntroPanel(bvid: vdCtr.bvid),
|
||||
] else if (vdCtr.videoType ==
|
||||
SearchType.media_bangumi) ...[
|
||||
Obx(() => BangumiIntroPanel(
|
||||
cid: vdCtr.cid.value)),
|
||||
],
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
indent: 12,
|
||||
endIndent: 12,
|
||||
color: Theme.of(context)
|
||||
.dividerColor
|
||||
.withOpacity(0.06),
|
||||
),
|
||||
),
|
||||
if (vdCtr.videoType == SearchType.video &&
|
||||
vdCtr.enableRelatedVideo)
|
||||
const RelatedVideoPanel(),
|
||||
body: Column(
|
||||
children: [
|
||||
tabbarBuild(),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: vdCtr.tabCtr,
|
||||
children: <Widget>[
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
return CustomScrollView(
|
||||
key: const PageStorageKey<String>('简介'),
|
||||
slivers: <Widget>[
|
||||
if (vdCtr.videoType == SearchType.video) ...[
|
||||
VideoIntroPanel(bvid: vdCtr.bvid),
|
||||
] else if (vdCtr.videoType ==
|
||||
SearchType.media_bangumi) ...[
|
||||
Obx(() =>
|
||||
BangumiIntroPanel(cid: vdCtr.cid.value)),
|
||||
],
|
||||
);
|
||||
},
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
indent: 12,
|
||||
endIndent: 12,
|
||||
color: Theme.of(context)
|
||||
.dividerColor
|
||||
.withOpacity(0.06),
|
||||
),
|
||||
),
|
||||
if (vdCtr.videoType == SearchType.video &&
|
||||
vdCtr.enableRelatedVideo)
|
||||
const RelatedVideoPanel(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
Obx(
|
||||
() => VideoReplyPanel(
|
||||
bvid: vdCtr.bvid,
|
||||
oid: vdCtr.oid.value,
|
||||
),
|
||||
Obx(
|
||||
() => VideoReplyPanel(
|
||||
bvid: vdCtr.bvid,
|
||||
oid: vdCtr.oid.value,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -23,7 +23,7 @@ class AiDetail extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
height: sheetHeight,
|
||||
child: Column(
|
||||
|
||||
@ -29,7 +29,7 @@ class ScrollAppBar extends StatelessWidget {
|
||||
opacity: scrollDistance / (videoHeight - kToolbarHeight),
|
||||
child: Container(
|
||||
height: statusBarHeight + kToolbarHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: EdgeInsets.only(top: statusBarHeight),
|
||||
child: AppBar(
|
||||
primary: false,
|
||||
|
||||
@ -93,7 +93,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 460,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -317,7 +317,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 500,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -377,7 +377,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
inactiveThumbColor:
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
inactiveTrackColor:
|
||||
Theme.of(context).colorScheme.background,
|
||||
Theme.of(context).colorScheme.surface,
|
||||
splashRadius: 10.0,
|
||||
// boolean variable value
|
||||
value: shutdownTimerService.waitForPlayingCompleted,
|
||||
@ -570,7 +570,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 310,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -660,7 +660,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 250,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -734,7 +734,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 250,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -828,7 +828,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 580,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -1084,7 +1084,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 250,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
|
||||
Reference in New Issue
Block a user