mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: intro panel
Closes #14 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -63,7 +63,6 @@ class BangumiIntroController extends CommonController {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryVideoTags();
|
||||
if (Get.arguments.isNotEmpty as bool) {
|
||||
if (Get.arguments.containsKey('bangumiItem') as bool) {
|
||||
preRender = true;
|
||||
@@ -105,6 +104,12 @@ class BangumiIntroController extends CommonController {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future queryData([bool isRefresh = true]) async {
|
||||
await queryVideoTags();
|
||||
return super.queryData(isRefresh);
|
||||
}
|
||||
|
||||
Future queryVideoTags() async {
|
||||
var result = await UserHttp.videoTags(bvid: bvid);
|
||||
if (result['status']) {
|
||||
|
||||
@@ -124,7 +124,7 @@ class VideoIntroController extends GetxController
|
||||
|
||||
// 获取视频简介&分p
|
||||
void queryVideoIntro() async {
|
||||
queryVideoTags();
|
||||
await queryVideoTags();
|
||||
var result = await VideoHttp.videoIntro(bvid: bvid);
|
||||
if (result['status']) {
|
||||
videoDetail.value = result['data']!;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import 'package:PiliPalaX/common/widgets/self_sized_horizontal_list.dart';
|
||||
import 'package:PiliPalaX/pages/search/widgets/search_text.dart';
|
||||
import 'package:PiliPalaX/utils/extension.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
@@ -154,6 +158,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
|
||||
late final _coinKey = GlobalKey<ActionItemState>();
|
||||
late final _favKey = GlobalKey<ActionItemState>();
|
||||
late final _expandableCtr = ExpandableController(initialExpanded: false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -167,6 +172,12 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_expandableCtr.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showFavBottomSheet() => showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
@@ -222,7 +233,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
return;
|
||||
}
|
||||
feedBack();
|
||||
widget.showIntroDetail();
|
||||
// widget.showIntroDetail();
|
||||
_expandableCtr.toggle();
|
||||
}
|
||||
|
||||
// 用户主页
|
||||
@@ -382,35 +394,54 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () => showIntroDetail(),
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
onTap: showIntroDetail,
|
||||
child: ExpandablePanel(
|
||||
controller: _expandableCtr,
|
||||
collapsed: GestureDetector(
|
||||
onLongPress: () {
|
||||
feedBack();
|
||||
Utils.copyText(
|
||||
'${widget.videoDetail?.title ?? videoItem['title'] ?? ''}');
|
||||
},
|
||||
child: Text(
|
||||
widget.videoDetail?.title ?? videoItem['title'] ?? "",
|
||||
// !loadingStatus
|
||||
// ? "${widget.videoDetail?.title}"
|
||||
// : videoItem['title'] ?? "",
|
||||
'${widget.videoDetail?.title ?? videoItem['title'] ?? ''}',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: t.colorScheme.outline,
|
||||
),
|
||||
]),
|
||||
),
|
||||
expanded: GestureDetector(
|
||||
onLongPress: () {
|
||||
feedBack();
|
||||
Utils.copyText(
|
||||
'${widget.videoDetail?.title ?? videoItem['title'] ?? ''}');
|
||||
},
|
||||
child: Text(
|
||||
widget.videoDetail?.title ?? videoItem['title'] ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
theme: const ExpandableThemeData(
|
||||
animationDuration: Duration(milliseconds: 300),
|
||||
scrollAnimationDuration: Duration(milliseconds: 300),
|
||||
crossFadePoint: 0,
|
||||
fadeCurve: Curves.ease,
|
||||
sizeCurve: Curves.linear,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Stack(
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () => showIntroDetail(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 7, bottom: 6),
|
||||
onTap: showIntroDetail,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
StatView(
|
||||
@@ -463,7 +494,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (enableAi)
|
||||
Positioned(
|
||||
right: 10,
|
||||
@@ -488,7 +518,80 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: showIntroDetail,
|
||||
child: ExpandablePanel(
|
||||
controller: _expandableCtr,
|
||||
collapsed: const SizedBox.shrink(),
|
||||
expanded: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Utils.copyText(
|
||||
'${videoIntroController.videoDetail.value.bvid}');
|
||||
},
|
||||
child: Text(
|
||||
'${videoIntroController.videoDetail.value.bvid}',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (videoIntroController
|
||||
.videoDetail.value.descV2.isNullOrEmpty.not) ...[
|
||||
const SizedBox(height: 8),
|
||||
SelectableText.rich(
|
||||
style: const TextStyle(
|
||||
height: 1.4,
|
||||
// fontSize: 13,
|
||||
),
|
||||
TextSpan(
|
||||
children: [
|
||||
buildContent(context,
|
||||
videoIntroController.videoDetail.value),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
if (videoIntroController.videoTags is List &&
|
||||
videoIntroController.videoTags.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: (videoIntroController.videoTags as List)
|
||||
.map(
|
||||
(item) => SearchText(
|
||||
fontSize: 13,
|
||||
searchText: item['tag_name'],
|
||||
onSelect: (_) => Get.toNamed('/searchResult',
|
||||
parameters: {
|
||||
'keyword': item['tag_name']
|
||||
}),
|
||||
onLongSelect: (_) =>
|
||||
Utils.copyText(item['tag_name']),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
theme: const ExpandableThemeData(
|
||||
animationDuration: Duration(milliseconds: 300),
|
||||
scrollAnimationDuration: Duration(milliseconds: 300),
|
||||
crossFadePoint: 0,
|
||||
fadeCurve: Curves.ease,
|
||||
sizeCurve: Curves.linear,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Obx(
|
||||
() => videoIntroController.queryVideoIntroData.value["status"]
|
||||
? const SizedBox()
|
||||
@@ -748,4 +851,80 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
text: '转发'),
|
||||
]);
|
||||
}
|
||||
|
||||
InlineSpan buildContent(BuildContext context, VideoDetailData content) {
|
||||
final List descV2 = content.descV2!;
|
||||
// type
|
||||
// 1 普通文本
|
||||
// 2 @用户
|
||||
final List<TextSpan> spanChildren = List.generate(descV2.length, (index) {
|
||||
final currentDesc = descV2[index];
|
||||
switch (currentDesc.type) {
|
||||
case 1:
|
||||
final List<InlineSpan> spanChildren = <InlineSpan>[];
|
||||
final RegExp urlRegExp = RegExp(r'https?://\S+\b');
|
||||
final Iterable<Match> matches =
|
||||
urlRegExp.allMatches(currentDesc.rawText);
|
||||
|
||||
int previousEndIndex = 0;
|
||||
for (final Match match in matches) {
|
||||
if (match.start > previousEndIndex) {
|
||||
spanChildren.add(TextSpan(
|
||||
text: currentDesc.rawText
|
||||
.substring(previousEndIndex, match.start)));
|
||||
}
|
||||
spanChildren.add(
|
||||
TextSpan(
|
||||
text: match.group(0),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary), // 设置颜色为蓝色
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
// 处理点击事件
|
||||
try {
|
||||
Get.toNamed(
|
||||
'/webviewnew',
|
||||
parameters: {
|
||||
'url': match.group(0)!,
|
||||
'type': 'url',
|
||||
'pageTitle': match.group(0)!,
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
previousEndIndex = match.end;
|
||||
}
|
||||
|
||||
if (previousEndIndex < currentDesc.rawText.length) {
|
||||
spanChildren.add(TextSpan(
|
||||
text: currentDesc.rawText.substring(previousEndIndex)));
|
||||
}
|
||||
|
||||
final TextSpan result = TextSpan(children: spanChildren);
|
||||
return result;
|
||||
case 2:
|
||||
final Color colorSchemePrimary =
|
||||
Theme.of(context).colorScheme.primary;
|
||||
final String heroTag = Utils.makeHeroTag(currentDesc.bizId);
|
||||
return TextSpan(
|
||||
text: '@${currentDesc.rawText}',
|
||||
style: TextStyle(color: colorSchemePrimary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Get.toNamed(
|
||||
'/member?mid=${currentDesc.bizId}',
|
||||
arguments: {'face': '', 'heroTag': heroTag},
|
||||
);
|
||||
},
|
||||
);
|
||||
default:
|
||||
return const TextSpan();
|
||||
}
|
||||
});
|
||||
return TextSpan(children: spanChildren);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1273,6 +1273,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
||||
showIntroDetail: showIntroDetail,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: MediaQuery.paddingOf(context).bottom),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -480,6 +480,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.3"
|
||||
expandable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: expandable
|
||||
sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
extended_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -168,6 +168,7 @@ dependencies:
|
||||
image_cropper: ^8.0.2
|
||||
#解压直播消息
|
||||
brotli: ^0.6.0
|
||||
expandable: ^5.0.1
|
||||
|
||||
dependency_overrides:
|
||||
screen_brightness: ^2.0.0+2
|
||||
|
||||
Reference in New Issue
Block a user