Compare commits
16 Commits
feature-fa
...
fix-routeQ
| Author | SHA1 | Date | |
|---|---|---|---|
| d57f84a1d7 | |||
| 32b2f0ceff | |||
| bae871cfa1 | |||
| d95fe9fe14 | |||
| eb006e4c55 | |||
| cb88d0c9ae | |||
| 3efad736ae | |||
| 42ad959155 | |||
| cdf800c49f | |||
| 569277572a | |||
| 19b84571c1 | |||
| 0812b8339e | |||
| b817a0c807 | |||
| 3da70d7e27 | |||
| 5e59db85be | |||
| 44a162762c |
@ -24,11 +24,13 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
Get.put(FavDetailController());
|
Get.put(FavDetailController());
|
||||||
late StreamController<bool> titleStreamC; // a
|
late StreamController<bool> titleStreamC; // a
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
|
late String mediaId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
|
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
|
||||||
|
mediaId = Get.parameters['mediaId']!;
|
||||||
titleStreamC = StreamController<bool>();
|
titleStreamC = StreamController<bool>();
|
||||||
_controller.addListener(
|
_controller.addListener(
|
||||||
() {
|
() {
|
||||||
@ -94,8 +96,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () =>
|
||||||
'/favSearch?searchType=0&mediaId=${Get.parameters['mediaId']!}'),
|
Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
),
|
),
|
||||||
// IconButton(
|
// IconButton(
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import 'package:pilipala/http/constants.dart';
|
|||||||
import 'package:pilipala/http/live.dart';
|
import 'package:pilipala/http/live.dart';
|
||||||
import 'package:pilipala/models/live/room_info.dart';
|
import 'package:pilipala/models/live/room_info.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
|
|
||||||
import '../../models/live/room_info_h5.dart';
|
import '../../models/live/room_info_h5.dart';
|
||||||
|
|
||||||
class LiveRoomController extends GetxController {
|
class LiveRoomController extends GetxController {
|
||||||
@ -16,13 +15,6 @@ class LiveRoomController extends GetxController {
|
|||||||
RxBool volumeOff = false.obs;
|
RxBool volumeOff = false.obs;
|
||||||
PlPlayerController plPlayerController =
|
PlPlayerController plPlayerController =
|
||||||
PlPlayerController.getInstance(videoType: 'live');
|
PlPlayerController.getInstance(videoType: 'live');
|
||||||
|
|
||||||
// MeeduPlayerController meeduPlayerController = MeeduPlayerController(
|
|
||||||
// colorTheme: Theme.of(Get.context!).colorScheme.primary,
|
|
||||||
// pipEnabled: true,
|
|
||||||
// controlsStyle: ControlsStyle.live,
|
|
||||||
// enabledButtons: const EnabledButtons(pip: true),
|
|
||||||
// );
|
|
||||||
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -39,8 +31,6 @@ class LiveRoomController extends GetxController {
|
|||||||
cover = liveItem.cover;
|
cover = liveItem.cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queryLiveInfo();
|
|
||||||
queryLiveInfoH5();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerInit(source) async {
|
playerInit(source) async {
|
||||||
|
|||||||
@ -29,22 +29,18 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
plPlayerController = _liveRoomController.plPlayerController;
|
|
||||||
plPlayerController!.onPlayerStatusChanged.listen(
|
|
||||||
(PlayerStatus status) {
|
|
||||||
if (status == PlayerStatus.playing) {
|
|
||||||
isShowCover = false;
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
floating = Floating();
|
floating = Floating();
|
||||||
}
|
}
|
||||||
_futureBuilder = _liveRoomController.queryLiveInfoH5();
|
videoSourceInit();
|
||||||
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
|
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> videoSourceInit() async {
|
||||||
|
_futureBuilder = _liveRoomController.queryLiveInfoH5();
|
||||||
|
plPlayerController = _liveRoomController.plPlayerController;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
plPlayerController!.dispose();
|
plPlayerController!.dispose();
|
||||||
|
|||||||
@ -105,7 +105,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
'/memberSearch?mid=${Get.parameters['mid']}&uname=${_memberController.memberInfo.value.name!}'),
|
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
|
|||||||
@ -114,7 +114,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
final VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
if (replyItem != null) {
|
if (replyItem != null) {
|
||||||
videoDetailCtr.oid = replyItem.oid;
|
videoDetailCtr.oid.value = replyItem.oid;
|
||||||
videoDetailCtr.fRpid = replyItem.rpid!;
|
videoDetailCtr.fRpid = replyItem.rpid!;
|
||||||
videoDetailCtr.firstFloor = replyItem;
|
videoDetailCtr.firstFloor = replyItem;
|
||||||
videoDetailCtr.showReplyReplyPanel();
|
videoDetailCtr.showReplyReplyPanel();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.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:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
@ -12,10 +12,9 @@ import 'package:pilipala/pages/preview/index.dart';
|
|||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
import 'package:pilipala/pages/video/detail/reply_new/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import 'package:pilipala/utils/url_utils.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
import 'zan.dart';
|
import 'zan.dart';
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
@ -48,6 +47,17 @@ class ReplyItem extends StatelessWidget {
|
|||||||
replyReply!(replyItem);
|
replyReply!(replyItem);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
feedBack();
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) {
|
||||||
|
return MorePanel(item: replyItem);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@ -123,98 +133,6 @@ class ReplyItem extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// 头像、昵称
|
|
||||||
// SizedBox(
|
|
||||||
// width: double.infinity,
|
|
||||||
// child: Stack(
|
|
||||||
// children: [
|
|
||||||
// GestureDetector(
|
|
||||||
// behavior: HitTestBehavior.opaque,
|
|
||||||
// onTap: () {
|
|
||||||
// feedBack();
|
|
||||||
// Get.toNamed('/member?mid=${replyItem!.mid}', arguments: {
|
|
||||||
// 'face': replyItem!.member!.avatar!,
|
|
||||||
// 'heroTag': heroTag
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// child: Row(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
// children: <Widget>[
|
|
||||||
// lfAvtar(context, heroTag),
|
|
||||||
// const SizedBox(width: 12),
|
|
||||||
// Text(
|
|
||||||
// replyItem!.member!.uname!,
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: replyItem!.member!.vip!['vipStatus'] > 0
|
|
||||||
// ? const Color.fromARGB(255, 251, 100, 163)
|
|
||||||
// : Theme.of(context).colorScheme.outline,
|
|
||||||
// fontSize: 13,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 6),
|
|
||||||
// Image.asset(
|
|
||||||
// 'assets/images/lv/lv${replyItem!.member!.level}.png',
|
|
||||||
// height: 11,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 6),
|
|
||||||
// if (replyItem!.isUp!)
|
|
||||||
// const PBadge(
|
|
||||||
// text: 'UP',
|
|
||||||
// size: 'small',
|
|
||||||
// stack: 'normal',
|
|
||||||
// fs: 9,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Positioned(
|
|
||||||
// top: 0,
|
|
||||||
// left: 0,
|
|
||||||
// right: 0,
|
|
||||||
// child: Container(
|
|
||||||
// width: double.infinity,
|
|
||||||
// height: 45,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// image: replyItem!.member!.userSailing!.cardbg != null
|
|
||||||
// ? DecorationImage(
|
|
||||||
// alignment: Alignment.centerRight,
|
|
||||||
// fit: BoxFit.fitHeight,
|
|
||||||
// image: NetworkImage(
|
|
||||||
// replyItem!.member!.userSailing!.cardbg!['image'],
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// : null,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// if (replyItem!.member!.userSailing!.cardbg != null &&
|
|
||||||
// replyItem!.member!.userSailing!.cardbg!['fan']['number'] > 0)
|
|
||||||
// Positioned(
|
|
||||||
// top: 10,
|
|
||||||
// left: Get.size.width / 7 * 5.8,
|
|
||||||
// child: DefaultTextStyle(
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontFamily: 'fansCard',
|
|
||||||
// fontSize: 9,
|
|
||||||
// color: Theme.of(context).colorScheme.primary,
|
|
||||||
// ),
|
|
||||||
// child: Column(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
// children: [
|
|
||||||
// const Text('NO.'),
|
|
||||||
// Text(
|
|
||||||
// replyItem!.member!.userSailing!.cardbg!['fan']
|
|
||||||
// ['num_desc'],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
/// fix Stack内GestureDetector onTap无效
|
/// fix Stack内GestureDetector onTap无效
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
@ -291,30 +209,26 @@ class ReplyItem extends StatelessWidget {
|
|||||||
// title
|
// title
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
||||||
child: SelectableRegion(
|
child: Text.rich(
|
||||||
focusNode: FocusNode(),
|
style: const TextStyle(height: 1.75),
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
maxLines:
|
||||||
child: Text.rich(
|
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
|
||||||
style: const TextStyle(height: 1.75),
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines:
|
TextSpan(
|
||||||
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
|
children: [
|
||||||
overflow: TextOverflow.ellipsis,
|
if (replyItem!.isTop!)
|
||||||
TextSpan(
|
const WidgetSpan(
|
||||||
children: [
|
alignment: PlaceholderAlignment.top,
|
||||||
if (replyItem!.isTop!)
|
child: PBadge(
|
||||||
const WidgetSpan(
|
text: 'TOP',
|
||||||
alignment: PlaceholderAlignment.top,
|
size: 'small',
|
||||||
child: PBadge(
|
stack: 'normal',
|
||||||
text: 'TOP',
|
type: 'line',
|
||||||
size: 'small',
|
fs: 9,
|
||||||
stack: 'normal',
|
|
||||||
type: 'line',
|
|
||||||
fs: 9,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
buildContent(context, replyItem!, replyReply, null),
|
),
|
||||||
],
|
buildContent(context, replyItem!, replyReply, null),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -447,6 +361,17 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
InkWell(
|
InkWell(
|
||||||
// 一楼点击评论展开评论详情
|
// 一楼点击评论展开评论详情
|
||||||
onTap: () => replyReply!(replyItem),
|
onTap: () => replyReply!(replyItem),
|
||||||
|
onLongPress: () {
|
||||||
|
feedBack();
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) {
|
||||||
|
return MorePanel(item: replies![i]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
@ -541,7 +466,6 @@ InlineSpan buildContent(
|
|||||||
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
||||||
final content = replyItem.content;
|
final content = replyItem.content;
|
||||||
final List<InlineSpan> spanChilds = <InlineSpan>[];
|
final List<InlineSpan> spanChilds = <InlineSpan>[];
|
||||||
bool hasMatchMember = false;
|
|
||||||
|
|
||||||
// 投票
|
// 投票
|
||||||
if (content.vote.isNotEmpty) {
|
if (content.vote.isNotEmpty) {
|
||||||
@ -591,7 +515,7 @@ InlineSpan buildContent(
|
|||||||
if (patternStr.isNotEmpty) {
|
if (patternStr.isNotEmpty) {
|
||||||
patternStr += "|";
|
patternStr += "|";
|
||||||
}
|
}
|
||||||
patternStr += r'(\b\d{1,2}[::]\d{2}\b)';
|
patternStr += r'(\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b)';
|
||||||
final RegExp pattern = RegExp(patternStr);
|
final RegExp pattern = RegExp(patternStr);
|
||||||
List<String> matchedStrs = [];
|
List<String> matchedStrs = [];
|
||||||
void addPlainTextSpan(str) {
|
void addPlainTextSpan(str) {
|
||||||
@ -639,7 +563,9 @@ InlineSpan buildContent(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (RegExp(r'^\b[0-9]{1,2}[::][0-9]{2}\b$').hasMatch(matchStr)) {
|
} else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$')
|
||||||
|
.hasMatch(matchStr)) {
|
||||||
|
matchStr = matchStr.replaceAll(':', ':');
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' $matchStr ',
|
text: ' $matchStr ',
|
||||||
@ -650,7 +576,6 @@ InlineSpan buildContent(
|
|||||||
..onTap = () {
|
..onTap = () {
|
||||||
// 跳转到指定位置
|
// 跳转到指定位置
|
||||||
try {
|
try {
|
||||||
matchStr = matchStr.replaceAll(':', ':');
|
|
||||||
SmartDialog.showToast('跳转至:$matchStr');
|
SmartDialog.showToast('跳转至:$matchStr');
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
||||||
.plPlayerController
|
.plPlayerController
|
||||||
@ -692,16 +617,54 @@ InlineSpan buildContent(
|
|||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () async {
|
||||||
|
final String title = content.jumpUrl[matchStr]['title'];
|
||||||
if (appUrlSchema == '') {
|
if (appUrlSchema == '') {
|
||||||
final String str = Uri.parse(matchStr).pathSegments[0];
|
final String redirectUrl =
|
||||||
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
await UrlUtils.parseRedirectUrl(matchStr);
|
||||||
final List matchKeys = matchRes.keys.toList();
|
final String pathSegment = Uri.parse(redirectUrl).path;
|
||||||
if (matchKeys.isNotEmpty) {
|
final String lastPathSegment =
|
||||||
if (matchKeys.first == 'BV') {
|
pathSegment.split('/').last;
|
||||||
|
if (lastPathSegment.startsWith('BV')) {
|
||||||
|
UrlUtils.matchUrlPush(
|
||||||
|
lastPathSegment,
|
||||||
|
title,
|
||||||
|
redirectUrl,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.toNamed(
|
||||||
|
'/webview',
|
||||||
|
parameters: {
|
||||||
|
'url': redirectUrl,
|
||||||
|
'type': 'url',
|
||||||
|
'pageTitle': title
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||||
|
Get.toNamed('/searchResult',
|
||||||
|
parameters: {'keyword': title});
|
||||||
|
} else if (matchStr.startsWith('https://b23.tv')) {
|
||||||
|
final String redirectUrl =
|
||||||
|
await UrlUtils.parseRedirectUrl(matchStr);
|
||||||
|
final String pathSegment = Uri.parse(redirectUrl).path;
|
||||||
|
final String lastPathSegment =
|
||||||
|
pathSegment.split('/').last;
|
||||||
|
if (lastPathSegment.startsWith('BV')) {
|
||||||
|
UrlUtils.matchUrlPush(
|
||||||
|
lastPathSegment,
|
||||||
|
title,
|
||||||
|
redirectUrl,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/searchResult',
|
'/webview',
|
||||||
parameters: {'keyword': matchRes['BV']},
|
parameters: {
|
||||||
|
'url': redirectUrl,
|
||||||
|
'type': 'url',
|
||||||
|
'pageTitle': title
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -710,16 +673,10 @@ InlineSpan buildContent(
|
|||||||
parameters: {
|
parameters: {
|
||||||
'url': matchStr,
|
'url': matchStr,
|
||||||
'type': 'url',
|
'type': 'url',
|
||||||
'pageTitle': ''
|
'pageTitle': title
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (appUrlSchema.startsWith('bilibili://search')) {
|
|
||||||
Get.toNamed('/searchResult', parameters: {
|
|
||||||
'keyword': content.jumpUrl[matchStr]['title']
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -739,6 +696,47 @@ InlineSpan buildContent(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (content.jumpUrl.keys.isNotEmpty) {
|
||||||
|
List<String> unmatchedItems = content.jumpUrl.keys
|
||||||
|
.toList()
|
||||||
|
.where((item) => !content.message.contains(item))
|
||||||
|
.toList();
|
||||||
|
if (unmatchedItems.isNotEmpty) {
|
||||||
|
for (int i = 0; i < unmatchedItems.length; i++) {
|
||||||
|
String patternStr = unmatchedItems[i];
|
||||||
|
spanChilds.addAll(
|
||||||
|
[
|
||||||
|
if (content.jumpUrl[patternStr]?['prefix_icon'] != null) ...[
|
||||||
|
WidgetSpan(
|
||||||
|
child: Image.network(
|
||||||
|
content.jumpUrl[patternStr]['prefix_icon'],
|
||||||
|
height: 19,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
TextSpan(
|
||||||
|
text: content.jumpUrl[patternStr]['title'],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
Get.toNamed(
|
||||||
|
'/webview',
|
||||||
|
parameters: {
|
||||||
|
'url': patternStr,
|
||||||
|
'type': 'url',
|
||||||
|
'pageTitle': content.jumpUrl[patternStr]['title']
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 图片渲染
|
// 图片渲染
|
||||||
if (content.pictures.isNotEmpty) {
|
if (content.pictures.isNotEmpty) {
|
||||||
final List<String> picList = <String>[];
|
final List<String> picList = <String>[];
|
||||||
@ -753,11 +751,15 @@ InlineSpan buildContent(
|
|||||||
builder: (BuildContext context, BoxConstraints box) {
|
builder: (BuildContext context, BoxConstraints box) {
|
||||||
double maxHeight = box.maxWidth * 0.6; // 设置最大高度
|
double maxHeight = box.maxWidth * 0.6; // 设置最大高度
|
||||||
// double width = (box.maxWidth / 2).truncateToDouble();
|
// double width = (box.maxWidth / 2).truncateToDouble();
|
||||||
double height = ((box.maxWidth /
|
double height = 100;
|
||||||
2 *
|
try {
|
||||||
pictureItem['img_height'] /
|
height = ((box.maxWidth /
|
||||||
pictureItem['img_width']))
|
2 *
|
||||||
.truncateToDouble();
|
pictureItem['img_height'] /
|
||||||
|
pictureItem['img_width']))
|
||||||
|
.truncateToDouble();
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
@ -880,3 +882,100 @@ InlineSpan buildContent(
|
|||||||
// spanChilds.add(TextSpan(text: matchMember));
|
// spanChilds.add(TextSpan(text: matchMember));
|
||||||
return TextSpan(children: spanChilds);
|
return TextSpan(children: spanChilds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MorePanel extends StatelessWidget {
|
||||||
|
final dynamic item;
|
||||||
|
const MorePanel({super.key, required this.item});
|
||||||
|
|
||||||
|
Future<dynamic> menuActionHandler(String type) async {
|
||||||
|
String message = item.content.message ?? item.content;
|
||||||
|
switch (type) {
|
||||||
|
case 'copyAll':
|
||||||
|
await Clipboard.setData(ClipboardData(text: message));
|
||||||
|
SmartDialog.showToast('已复制');
|
||||||
|
Get.back();
|
||||||
|
break;
|
||||||
|
case 'copyFreedom':
|
||||||
|
Get.back();
|
||||||
|
showDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('自由复制'),
|
||||||
|
content: SelectableText(message),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
// case 'block':
|
||||||
|
// SmartDialog.showToast('加入黑名单');
|
||||||
|
// break;
|
||||||
|
// case 'report':
|
||||||
|
// SmartDialog.showToast('举报');
|
||||||
|
// break;
|
||||||
|
// case 'delete':
|
||||||
|
// SmartDialog.showToast('删除');
|
||||||
|
// break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Color errorColor = Theme.of(context).colorScheme.error;
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () => Get.back(),
|
||||||
|
child: Container(
|
||||||
|
height: 35,
|
||||||
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 32,
|
||||||
|
height: 3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () async => await menuActionHandler('copyAll'),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
leading: const Icon(Icons.copy_all_outlined, size: 19),
|
||||||
|
title: Text('复制全部', style: Theme.of(context).textTheme.titleSmall),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
onTap: () async => await menuActionHandler('copyFreedom'),
|
||||||
|
minLeadingWidth: 0,
|
||||||
|
leading: const Icon(Icons.copy_outlined, size: 19),
|
||||||
|
title: Text('自由复制', style: Theme.of(context).textTheme.titleSmall),
|
||||||
|
),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () async => await menuActionHandler('block'),
|
||||||
|
// minLeadingWidth: 0,
|
||||||
|
// leading: Icon(Icons.block_outlined, color: errorColor),
|
||||||
|
// title: Text('加入黑名单', style: TextStyle(color: errorColor)),
|
||||||
|
// ),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () async => await menuActionHandler('report'),
|
||||||
|
// minLeadingWidth: 0,
|
||||||
|
// leading: Icon(Icons.report_outlined, color: errorColor),
|
||||||
|
// title: Text('举报', style: TextStyle(color: errorColor)),
|
||||||
|
// ),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () async => await menuActionHandler('del'),
|
||||||
|
// minLeadingWidth: 0,
|
||||||
|
// leading: Icon(Icons.delete_outline, color: errorColor),
|
||||||
|
// title: Text('删除', style: TextStyle(color: errorColor)),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -438,7 +438,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
}),
|
}),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: () => Get.back(),
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
|||||||
61
lib/utils/url_utils.dart
Normal file
61
lib/utils/url_utils.dart
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../http/search.dart';
|
||||||
|
import 'id_utils.dart';
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
|
class UrlUtils {
|
||||||
|
// 302重定向路由截取
|
||||||
|
static Future<String> parseRedirectUrl(String url) async {
|
||||||
|
late String redirectUrl;
|
||||||
|
final dio = Dio();
|
||||||
|
dio.options.followRedirects = false;
|
||||||
|
dio.options.validateStatus = (status) {
|
||||||
|
return status == 200 || status == 301 || status == 302;
|
||||||
|
};
|
||||||
|
final response = await dio.get(url);
|
||||||
|
if (response.statusCode == 302) {
|
||||||
|
redirectUrl = response.headers['location']?.first as String;
|
||||||
|
if (redirectUrl.endsWith('/')) {
|
||||||
|
redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (url.endsWith('/')) {
|
||||||
|
url = url.substring(0, url.length - 1);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return redirectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配url路由跳转
|
||||||
|
static matchUrlPush(
|
||||||
|
String pathSegment,
|
||||||
|
String title,
|
||||||
|
String redirectUrl,
|
||||||
|
) async {
|
||||||
|
final Map matchRes = IdUtils.matchAvorBv(input: pathSegment);
|
||||||
|
if (matchRes.containsKey('BV')) {
|
||||||
|
final String bv = matchRes['BV'];
|
||||||
|
final int cid = await SearchHttp.ab2c(bvid: bv);
|
||||||
|
final String heroTag = Utils.makeHeroTag(bv);
|
||||||
|
await Get.toNamed(
|
||||||
|
'/video?bvid=$bv&cid=$cid',
|
||||||
|
arguments: <String, String?>{
|
||||||
|
'pic': '',
|
||||||
|
'heroTag': heroTag,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await Get.toNamed(
|
||||||
|
'/webview',
|
||||||
|
parameters: {
|
||||||
|
'url': redirectUrl,
|
||||||
|
'type': 'url',
|
||||||
|
'pageTitle': title,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user