Compare commits

...

7 Commits

Author SHA1 Message Date
569277572a Merge pull request #536 from KoolShow/fix_seekto_regexp
fix: 含有小时的时间无法跳转
2024-02-12 18:11:26 +08:00
19b84571c1 Merge branch 'main' into fix_seekto_regexp 2024-02-12 18:11:15 +08:00
0812b8339e Merge branch 'feature-replyItem' 2024-02-12 17:55:08 +08:00
b817a0c807 修正正则表达式以匹配含小时的时间 2024-02-12 17:20:18 +08:00
3da70d7e27 Merge branch 'fix-replyRepeat' 2024-02-12 16:55:56 +08:00
5e59db85be fix: 评论笔记跳转 issues #472 2024-02-12 16:51:05 +08:00
44a162762c fix: 评论页面路由跳转 issues #405 2024-02-09 23:24:26 +08:00
3 changed files with 164 additions and 28 deletions

View File

@ -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();

View File

@ -1,7 +1,6 @@
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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 +11,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;
@ -541,7 +539,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 +588,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 +636,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 +649,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 +690,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 +746,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 +769,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 +824,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(

61
lib/utils/url_utils.dart Normal file
View 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,
},
);
}
}
}