This commit is contained in:
bggRGjQaUbCoE
2024-08-26 13:50:26 +08:00
50 changed files with 561 additions and 214 deletions

View File

@@ -234,7 +234,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
: bangumiItem!.title!,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,

View File

@@ -50,7 +50,7 @@ class IntroDetail extends StatelessWidget {
bangumiDetail!.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),

View File

@@ -160,7 +160,7 @@ class BangumiContent extends StatelessWidget {
bangumiItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
maxLines: 1,

View File

@@ -63,7 +63,7 @@ class DynamicDetailController extends GetxController {
isLoadingMore = false;
if (res['status']) {
List<ReplyItemModel> replies = res['data'].replies;
acount.value = res['data'].cursor.allCount;
acount.value = res['data'].cursor.allCount ?? 0;
nextOffset = res['data'].cursor.paginationReply.nextOffset ?? "";
if (replies.isNotEmpty) {
noMore.value = '加载中...';

View File

@@ -382,7 +382,8 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
return FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
Map data = snapshot.data as Map;
if (snapshot.data['status']) {
// 请求成功

View File

@@ -63,9 +63,9 @@ class _DynamicsPageState extends State<DynamicsPage>
scrollController.addListener(() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('following', const Duration(seconds: 1), () {
_dynamicsController.queryFollowing2();
});
EasyThrottle.throttle('following', const Duration(seconds: 1), () {
_dynamicsController.queryFollowing2();
});
}
});
}

View File

@@ -77,7 +77,7 @@ class VideoContent extends StatelessWidget {
favFolderItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
),

View File

@@ -157,7 +157,7 @@ class VideoContent extends StatelessWidget {
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
maxLines: 2,

View File

@@ -301,7 +301,7 @@ class VideoContent extends StatelessWidget {
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
maxLines: videoItem.videos > 1 ? 1 : 2,

View File

@@ -73,7 +73,7 @@ class HtmlRenderController extends GetxController {
);
if (res['status']) {
List<ReplyItemModel> replies = res['data'].replies;
acount.value = res['data'].cursor.allCount;
acount.value = res['data'].cursor.allCount ?? 0;
nextOffset = res['data'].cursor.paginationReply.nextOffset ?? "";
if (replies.isNotEmpty) {
noMore.value = '加载中...';

View File

@@ -105,7 +105,7 @@ class LiveContent extends StatelessWidget {
liveItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
maxLines: 2,

View File

@@ -102,9 +102,7 @@ class _BottomControlState extends State<BottomControl> {
canUsePiP = false;
}
if (canUsePiP) {
await widget.floating!.enable(
const EnableManual()
);
await widget.floating!.enable(const EnableManual());
} else {}
},
icon: const Icon(
@@ -117,9 +115,9 @@ class _BottomControlState extends State<BottomControl> {
const SizedBox(width: 4),
],
ComBtn(
tooltip: '全屏切换',
icon: const Icon(
Icons.fullscreen,
semanticLabel: '全屏切换',
size: 20,
color: Colors.white,
),

View File

@@ -310,6 +310,7 @@ class LoginPageController extends GetxController
Uri currentUri = Uri.parse(Url);
var safeCenterRes = await LoginHttp.safeCenterGetInfo(
tmpCode: currentUri.queryParameters['tmp_token']!);
//{"code":0,"message":"0","ttl":1,"data":{"account_info":{"hide_tel":"111*****111","hide_mail":"aaa*****aaaa.aaa","bind_mail":true,"bind_tel":true,"tel_verify":true,"mail_verify":true,"unneeded_check":false,"bind_safe_question":false,"mid":1111111},"member_info":{"nickname":"xxxxxxx","face":"https://i0.hdslb.com/bfs/face/xxxxxxx.jpg","realname_status":false},"sns_info":{"bind_google":false,"bind_fb":false,"bind_apple":false,"bind_qq":true,"bind_weibo":true,"bind_wechat":false},"account_safe":{"score":80}}}
if (!safeCenterRes['status']) {
SmartDialog.showToast("获取安全验证信息失败,请尝试其它登录方式\n"
"(${safeCenterRes['code']}) ${safeCenterRes['msg']}");
@@ -317,9 +318,9 @@ class LoginPageController extends GetxController
}
Map<String, String> accountInfo = {
"telVerify": safeCenterRes['data']['account_info']!['tel_verify'],
"bindTel": safeCenterRes['data']['account_info']!["bind_tel"],
"hindTel": safeCenterRes['data']['account_info']!["hide_tel"],
"mailVerify": safeCenterRes['data']['account_info']!['mailVerify'],
"bindMail": safeCenterRes['data']['account_info']!["bind_mail"],
"hindMail": safeCenterRes['data']['account_info']!["hide_mail"],
};
TextEditingController _textFieldController = TextEditingController();
String captchaKey = '';
@@ -327,7 +328,7 @@ class LoginPageController extends GetxController
title: const Text("本次登录需要验证您的手机号"),
content: Column(
children:[
Text(accountInfo['bindTel'] ?? '未能获取手机号'),
Text(accountInfo['hindTel'] ?? '未能获取手机号'),
TextField(
controller: _textFieldController,
decoration: const InputDecoration(hintText: "请输入短信验证码"),

View File

@@ -139,7 +139,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
stops: const [0.1, 0.4 ,0.7]),
stops: const [0.1, 0.4, 0.7]),
),
),
),

View File

@@ -81,7 +81,7 @@ class _SysMsgPageState extends State<SysMsgPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
Text(
SelectableText(
"$content",
style: Theme.of(context)
.textTheme

View File

@@ -74,7 +74,7 @@ Widget searchArticlePanel(BuildContext context, ctr, list) {
TextSpan(
text: i['text'],
style: TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context)

View File

@@ -114,7 +114,7 @@ class LiveContent extends StatelessWidget {
TextSpan(
text: i['text'],
style: TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary

View File

@@ -85,7 +85,7 @@ class _PlaySettingState extends State<PlaySetting> {
),
const SetSwitchItem(
title: '双击快退/快进',
subTitle: '左侧双击快退右侧双击快进',
subTitle: '左侧双击快退/右侧双击快进,关闭则双击均为暂停/播放',
leading: Icon(Icons.touch_app_outlined),
setKey: SettingBoxKey.enableQuickDouble,
defaultVal: true,
@@ -139,6 +139,13 @@ class _PlaySettingState extends State<PlaySetting> {
setKey: SettingBoxKey.enableAutoExit,
defaultVal: true,
),
const SetSwitchItem(
title: '延长播放控件显示时间',
subTitle: '开启后延长至30秒便于屏幕阅读器滑动切换控件焦点',
leading: Icon(Icons.timer_outlined),
setKey: SettingBoxKey.enableLongShowControl,
defaultVal: false
),
const SetSwitchItem(
title: '全向旋转',
subTitle: '小屏可受重力转为临时全屏,若系统锁定旋转仍触发请关闭,关闭会影响横屏适配',

View File

@@ -69,7 +69,7 @@ class _StyleSettingState extends State<StyleSetting> {
children: [
SetSwitchItem(
title: '横屏适配',
subTitle: '启用横屏布局与逻辑,适用于平板等设备;推荐全屏方向设为【不改变当前方向】',
subTitle: '启用横屏布局与逻辑,平板、折叠屏等可开启;建议全屏方向设为【不改变当前方向】',
leading: const Icon(Icons.phonelink_outlined),
setKey: SettingBoxKey.horizontalScreen,
defaultVal: false,

View File

@@ -97,7 +97,7 @@ class VideoContent extends StatelessWidget {
subFolderItem.title!,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
),

View File

@@ -130,7 +130,7 @@ class VideoContent extends StatelessWidget {
videoItem.title,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
maxLines: 2,

View File

@@ -303,7 +303,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
// : videoItem['title'] ?? "",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,

View File

@@ -47,7 +47,7 @@ class IntroDetail extends StatelessWidget {
videoDetail!.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 6),

View File

@@ -92,7 +92,7 @@ class VideoReplyController extends GetxController {
}
}
replies.insertAll(0, res['data'].topReplies);
count.value = res['data'].cursor.allCount;
count.value = res['data'].cursor.allCount ?? 0;
replyList.value = replies;
} else {
replyList.addAll(replies);

View File

@@ -11,7 +11,7 @@ class VideoReplyReplyController extends GetxController {
int? aid;
// rpid 请求楼中楼回复
String? rpid;
ReplyType replyType = ReplyType.video;
ReplyType replyType; // = ReplyType.video;
RxList<ReplyItemModel> replyList = <ReplyItemModel>[].obs;
// 当前页
int currentPage = 0;
@@ -19,6 +19,7 @@ class VideoReplyReplyController extends GetxController {
RxString noMore = ''.obs;
// 当前回复的回复
ReplyItemModel? currentReplyItem;
ReplyItemModel? root;
@override
void onInit() {
@@ -41,6 +42,7 @@ class VideoReplyReplyController extends GetxController {
type: replyType.index,
);
if (res['status']) {
if (res['data'].root != null) root = res['data'].root;
final List<ReplyItemModel> replies = res['data'].replies;
if (replies.isNotEmpty) {
noMore.value = '加载中...';

View File

@@ -73,7 +73,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
@override
Widget build(BuildContext context) {
return Container(
height: widget.source == 'videoDetail' ? Utils.getSheetHeight(context) : null,
height:
widget.source == 'videoDetail' ? Utils.getSheetHeight(context) : null,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
@@ -137,60 +138,91 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
FutureBuilder(
future: _futureBuilderFuture,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final Map data = snapshot.data as Map;
if (data['status']) {
// 请求成功
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index ==
_videoReplyReplyController
.replyList.length) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.padding
.bottom),
height: MediaQuery.of(context)
.padding
.bottom +
100,
child: Center(
child: Obx(
() => Text(
_videoReplyReplyController
.noMore.value,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.outline,
return SliverMainAxisGroup(
slivers: <Widget>[
if (widget.firstFloor == null &&
_videoReplyReplyController.root != null) ...[
SliverToBoxAdapter(
child: ReplyItem(
replyItem: _videoReplyReplyController.root,
replyLevel: '2',
showReplyRow: false,
addReply: (replyItem) {
_videoReplyReplyController.replyList
.add(replyItem);
},
replyType: widget.replyType,
replyReply: (replyItem) =>
replyReply(replyItem),
),
),
SliverToBoxAdapter(
child: Divider(
height: 20,
color: Theme.of(context)
.dividerColor
.withOpacity(0.1),
thickness: 6,
),
),
],
Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index ==
_videoReplyReplyController
.replyList.length) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context)
.padding
.bottom),
height: MediaQuery.of(context)
.padding
.bottom +
100,
child: Center(
child: Obx(
() => Text(
_videoReplyReplyController
.noMore.value,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.colorScheme
.outline,
),
),
),
),
),
),
);
} else {
return ReplyItem(
replyItem: _videoReplyReplyController
.replyList[index],
replyLevel: '2',
showReplyRow: false,
addReply: (replyItem) {
_videoReplyReplyController.replyList
.add(replyItem);
},
replyType: widget.replyType,
);
}
},
childCount: _videoReplyReplyController
.replyList.length +
1,
);
} else {
return ReplyItem(
replyItem: _videoReplyReplyController
.replyList[index],
replyLevel: '2',
showReplyRow: false,
addReply: (replyItem) {
_videoReplyReplyController.replyList
.add(replyItem);
},
replyType: widget.replyType,
);
}
},
childCount: _videoReplyReplyController
.replyList.length +
1,
),
),
),
),
],
);
} else {
// 请求错误

View File

@@ -453,7 +453,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
bottom: 10,
child: IconButton(
tooltip: '播放',
onPressed: () => handlePlay(),
onPressed: handlePlay,
icon: Image.asset(
'assets/images/play.png',
width: 60,
@@ -565,9 +565,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
left: 0,
right: 0,
child: GestureDetector(
onTap: () {
handlePlay();
},
onTap: handlePlay,
child: NetworkImgLayer(
type: 'emote',
src: videoDetailController
@@ -698,9 +696,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
left: 0,
right: 0,
child: GestureDetector(
onTap: () {
handlePlay();
},
onTap: handlePlay,
child: NetworkImgLayer(
type: 'emote',
src: videoDetailController.videoItem['pic'],
@@ -793,9 +789,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
left: 0,
right: 0,
child: GestureDetector(
onTap: () {
handlePlay();
},
onTap: handlePlay,
child: NetworkImgLayer(
type: 'emote',
src: videoDetailController.videoItem['pic'],
@@ -891,9 +885,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
left: 0,
right: 0,
child: GestureDetector(
onTap: () {
handlePlay();
},
onTap: handlePlay,
child: NetworkImgLayer(
type: 'emote',
src: videoDetailController.videoItem['pic'],
@@ -997,9 +989,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
left: 0,
right: 0,
child: GestureDetector(
onTap: () {
handlePlay();
},
onTap: handlePlay,
child: NetworkImgLayer(
type: 'emote',
src: videoDetailController

View File

@@ -4,11 +4,13 @@ import 'dart:math';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:media_kit/media_kit.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:PiliPalaX/http/user.dart';
import 'package:PiliPalaX/models/video/play/quality.dart';
@@ -223,6 +225,22 @@ class _HeaderControlState extends State<HeaderControl> {
}
},
),
ListTile(
onTap: () {
Get.back();
Player? player =
widget.controller?.videoPlayerController;
if (player == null) {
SmartDialog.showToast('播放器未初始化');
return;
}
var pp = player.platform as NativePlayer;
pp.setProperty("video", "no");
},
dense: true,
leading: const Icon(Icons.headphones_outlined, size: 20),
title: const Text('听视频(需返回首页才能终止该状态)', style: titleStyle),
),
ListTile(
onTap: () => {Get.back(), showSetVideoQa()},
dense: true,
@@ -265,6 +283,172 @@ class _HeaderControlState extends State<HeaderControl> {
leading: const Icon(Icons.subtitles_outlined, size: 20),
title: const Text('弹幕设置', style: titleStyle),
),
ListTile(
title: const Text('播放信息', style: titleStyle),
leading: const Icon(Icons.info_outline, size: 20),
onTap: () {
Player? player =
widget.controller?.videoPlayerController;
if (player == null) {
SmartDialog.showToast('播放器未初始化');
return;
}
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('播放信息'),
content: SizedBox(
width: double.maxFinite,
child: ListView(
children: [
ListTile(
title: const Text("Resolution"),
subtitle: Text(
'${player.state.width}x${player.state.height}'),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"Resolution\n${player.state.width}x${player.state.height}",
),
);
},
),
ListTile(
title: const Text("VideoParams"),
subtitle: Text(player.state.videoParams
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"VideoParams\n${player.state.videoParams}",
),
);
},
),
ListTile(
title: const Text("AudioParams"),
subtitle: Text(player.state.audioParams
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"AudioParams\n${player.state.audioParams}",
),
);
},
),
ListTile(
title: const Text("Media"),
subtitle: Text(
player.state.playlist.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"Media\n${player.state.playlist}",
),
);
},
),
ListTile(
title: const Text("AudioTrack"),
subtitle: Text(player.state.track.audio
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"AudioTrack\n${player.state.track.audio}",
),
);
},
),
ListTile(
title: const Text("VideoTrack"),
subtitle: Text(player.state.track.video
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"VideoTrack\n${player.state.track.audio}",
),
);
},
),
ListTile(
title: const Text("pitch"),
subtitle: Text(
player.state.pitch.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"pitch\n${player.state.pitch}",
),
);
}),
ListTile(
title: const Text("rate"),
subtitle: Text(
player.state.rate.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"rate\n${player.state.rate}",
),
);
}),
ListTile(
title: const Text("AudioBitrate"),
subtitle: Text(player.state.audioBitrate
.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"AudioBitrate\n${player.state.audioBitrate}",
),
);
},
),
ListTile(
title: const Text("Volume"),
subtitle: Text(
player.state.volume.toString()),
onTap: () {
Clipboard.setData(
ClipboardData(
text:
"Volume\n${player.state.volume}",
),
);
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'确定',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.outline),
),
),
],
);
},
);
})
],
),
))

View File

@@ -79,25 +79,27 @@ class _WebviewPageState extends State<WebviewPage> {
),
],
Expanded(
child: WebViewWidget(
controller: _webviewController.controller,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
),
Factory<PanGestureRecognizer>(
() => PanGestureRecognizer(),
),
Factory<ForcePressGestureRecognizer>(
() => ForcePressGestureRecognizer(),
),
Factory<EagerGestureRecognizer>(
() => EagerGestureRecognizer(),
),
Factory<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(),
),
}),
child: SafeArea(
child: WebViewWidget(
controller: _webviewController.controller,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
),
Factory<PanGestureRecognizer>(
() => PanGestureRecognizer(),
),
Factory<ForcePressGestureRecognizer>(
() => ForcePressGestureRecognizer(),
),
Factory<EagerGestureRecognizer>(
() => EagerGestureRecognizer(),
),
Factory<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(),
),
}),
),
),
],
));