dyn addcard

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-05-22 14:12:14 +08:00
parent 4bd4178cbf
commit b6aa6aebb9
7 changed files with 656 additions and 59 deletions

View File

@@ -77,11 +77,6 @@ class Fallback {
id: json['id'],
type: json['type'],
);
Map<String, dynamic> toJson() => {
'id': id,
'type': type,
};
}
// 单个动态详情
@@ -241,12 +236,32 @@ class Button {
String? icon;
String? jumpUrl;
String? text;
JumpStyle? jumpStyle;
int? status;
int? type;
Check? check;
Button.fromJson(Map<String, dynamic> json) {
handleType = json['handle_type'];
icon = json['icon'];
jumpUrl = json['jump_url'];
text = json['text'];
jumpStyle = json['jump_style'] == null
? null
: JumpStyle.fromJson(json['jump_style']);
status = json['status'];
type = json['type'];
check = json['check'] == null ? null : Check.fromJson(json['check']);
}
}
class Check {
int? disable;
String? text;
Check.fromJson(Map<String, dynamic> json) {
disable = json['disable'];
text = json['text'];
}
}
@@ -357,12 +372,9 @@ class DynamicAddModel {
Ugc? ugc;
Reserve? reserve;
Good? goods;
/// TODO 比赛vs
String? match;
/// TODO 游戏信息
String? common;
UpowerLottery? upowerLottery;
AddCommon? common;
AddMatch? match;
DynamicAddModel.fromJson(Map<String, dynamic> json) {
type = json['type'];
@@ -371,9 +383,200 @@ class DynamicAddModel {
reserve =
json['reserve'] != null ? Reserve.fromJson(json['reserve']) : null;
goods = json['goods'] != null ? Good.fromJson(json['goods']) : null;
upowerLottery = json['upower_lottery'] != null
? UpowerLottery.fromJson(json['upower_lottery'])
: null;
common = json['common'] != null ? AddCommon.fromJson(json['common']) : null;
match = json['match'] != null ? AddMatch.fromJson(json['match']) : null;
}
}
class AddMatch {
Button? button;
String? headText;
String? idStr;
String? jumpUrl;
MatchInfo? matchInfo;
AddMatch({
this.button,
this.headText,
this.idStr,
this.jumpUrl,
this.matchInfo,
});
factory AddMatch.fromJson(Map<String, dynamic> json) => AddMatch(
button: json["button"] == null ? null : Button.fromJson(json["button"]),
headText: json["head_text"],
idStr: json["id_str"],
jumpUrl: json["jump_url"],
matchInfo: json["match_info"] == null
? null
: MatchInfo.fromJson(json["match_info"]),
);
}
class MatchInfo {
String? centerBottom;
List? centerTop;
TTeam? leftTeam;
TTeam? rightTeam;
int? status;
dynamic subTitle;
String? title;
MatchInfo({
this.centerBottom,
this.centerTop,
this.leftTeam,
this.rightTeam,
this.status,
this.subTitle,
this.title,
});
factory MatchInfo.fromJson(Map<String, dynamic> json) => MatchInfo(
centerBottom: json["center_bottom"],
centerTop: json["center_top"],
leftTeam: json["left_team"] == null
? null
: TTeam.fromJson(json["left_team"]),
rightTeam: json["right_team"] == null
? null
: TTeam.fromJson(json["right_team"]),
status: json["status"],
subTitle: json["sub_title"],
title: json["title"],
);
}
class TTeam {
int? id;
String? name;
String? pic;
TTeam({
this.id,
this.name,
this.pic,
});
factory TTeam.fromJson(Map<String, dynamic> json) => TTeam(
id: json["id"],
name: json["name"],
pic: json["pic"],
);
}
class AddCommon {
Button? button;
String? cover;
String? desc1;
String? desc2;
String? headText;
String? idStr;
String? jumpUrl;
int? style;
String? subType;
String? title;
AddCommon({
this.button,
this.cover,
this.desc1,
this.desc2,
this.headText,
this.idStr,
this.jumpUrl,
this.style,
this.subType,
this.title,
});
factory AddCommon.fromJson(Map<String, dynamic> json) => AddCommon(
button: json["button"] == null ? null : Button.fromJson(json["button"]),
cover: json["cover"],
desc1: json["desc1"],
desc2: json["desc2"],
headText: json["head_text"],
idStr: json["id_str"],
jumpUrl: json["jump_url"],
style: json["style"],
subType: json["sub_type"],
title: json["title"],
);
}
class UpowerLottery {
Button? button;
Desc? desc;
Hint? hint;
String? jumpUrl;
int? rid;
int? state;
String? title;
int? upMid;
int? upowerActionState;
int? upowerLevel;
UpowerLottery({
this.button,
this.desc,
this.hint,
this.jumpUrl,
this.rid,
this.state,
this.title,
this.upMid,
this.upowerActionState,
this.upowerLevel,
});
factory UpowerLottery.fromJson(Map<String, dynamic> json) => UpowerLottery(
button: json["button"] == null ? null : Button.fromJson(json["button"]),
desc: json["desc"] == null ? null : Desc.fromJson(json["desc"]),
hint: json["hint"] == null ? null : Hint.fromJson(json["hint"]),
jumpUrl: json["jump_url"],
rid: json["rid"],
state: json["state"],
title: json["title"],
upMid: json["up_mid"],
upowerActionState: json["upower_action_state"],
upowerLevel: json["upower_level"],
);
}
class Hint {
int? style;
String? text;
Hint({
this.style,
this.text,
});
factory Hint.fromJson(Map<String, dynamic> json) => Hint(
style: json["style"],
text: json["text"],
);
}
class JumpStyle {
String? iconUrl;
String? text;
JumpStyle({
this.iconUrl,
this.text,
});
factory JumpStyle.fromJson(Map<String, dynamic> json) => JumpStyle(
iconUrl: json["icon_url"],
text: json["text"],
);
}
class Vote {
Vote({
this.choiceCnt,
@@ -452,6 +655,7 @@ class Reserve {
this.button,
this.desc1,
this.desc2,
this.desc3,
this.jumpUrl,
this.reserveTotal,
this.rid,
@@ -464,6 +668,7 @@ class Reserve {
ReserveBtn? button;
Desc? desc1;
Desc? desc2;
Desc? desc3;
String? jumpUrl;
int? reserveTotal;
int? rid;
@@ -477,6 +682,7 @@ class Reserve {
json['button'] == null ? null : ReserveBtn.fromJson(json['button']);
desc1 = json['desc1'] == null ? null : Desc.fromJson(json['desc1']);
desc2 = json['desc2'] == null ? null : Desc.fromJson(json['desc2']);
desc3 = json['desc3'] == null ? null : Desc.fromJson(json['desc3']);
jumpUrl = json['jump_url'];
reserveTotal = json['reserve_total'];
rid = json['rid'];
@@ -520,16 +726,19 @@ class Desc {
this.style,
this.text,
this.visible,
this.jumpUrl,
});
int? style;
String? text;
bool? visible;
String? jumpUrl;
Desc.fromJson(Map<String, dynamic> json) {
style = json['style'];
text = json['text'];
visible = json['visible'];
jumpUrl = json["jump_url"];
}
}

View File

@@ -6,8 +6,10 @@ import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
Widget addWidget(
ThemeData theme, DynamicItemModel item, BuildContext context, type,
@@ -102,22 +104,60 @@ Widget addWidget(
TextSpan(
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme
.textTheme.labelMedium!.fontSize,
fontSize: 13,
),
children: [
if (reserve.desc1 != null)
if (reserve.desc1?.text?.isNotEmpty ==
true)
TextSpan(text: reserve.desc1!.text),
const TextSpan(text: ' '),
if (reserve.desc2 != null)
TextSpan(text: reserve.desc2!.text),
if (reserve.desc2?.text?.isNotEmpty ==
true)
TextSpan(
text:
' ${reserve.desc2!.text}'),
if (reserve.desc3?.text?.isNotEmpty ==
true) ...[
const TextSpan(text: '\n'),
WidgetSpan(
alignment:
PlaceholderAlignment.middle,
child: Icon(
size: 17,
Icons.card_giftcard,
color:
theme.colorScheme.primary,
),
),
TextSpan(
text: ' ${reserve.desc3!.text}',
style: TextStyle(
color:
theme.colorScheme.primary,
),
recognizer:
reserve.desc3!.jumpUrl == null
? null
: (TapGestureRecognizer()
..onTap = () {
Get.toNamed(
'/webview',
parameters: {
'url': reserve
.desc3!
.jumpUrl!
},
);
}),
),
],
],
),
)
],
),
),
if (reserve.button != null)
if (reserve.button != null) ...[
const SizedBox(width: 10),
Builder(
builder: (context) {
final btn = reserve.button!;
@@ -187,6 +227,7 @@ Widget addWidget(
);
},
),
],
],
),
),
@@ -195,6 +236,104 @@ Widget addWidget(
)
: const SizedBox.shrink()
: const SizedBox.shrink();
case 'ADDITIONAL_TYPE_UPOWER_LOTTERY':
final content = item.modules.moduleDynamic!.additional!.upowerLottery!;
final borderRadius = floor == 1 ? null : StyleString.mdRadius;
return Padding(
padding: const EdgeInsets.only(top: 6),
child: Material(
color: bgColor,
borderRadius: borderRadius,
child: InkWell(
borderRadius: borderRadius,
onTap: content.jumpUrl == null
? null
: () => PiliScheme.routePushFromUrl(content.jumpUrl!),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
Expanded(
child: Column(
spacing: 2,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (content.title?.isNotEmpty == true)
Text(content.title!),
if (content.hint?.text?.isNotEmpty == true)
Text(
content.hint!.text!,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: 13,
),
),
if (content.desc?.text?.isNotEmpty == true)
Text.rich(
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
size: 17,
Icons.card_giftcard,
color: theme.colorScheme.primary,
),
),
TextSpan(
text: ' ${content.desc!.text!}',
style: TextStyle(
color: theme.colorScheme.primary,
fontSize: 13,
),
recognizer: content.desc!.jumpUrl == null
? null
: (TapGestureRecognizer()
..onTap = () {
Get.toNamed(
'/webview',
parameters: {
'url': content.desc!.jumpUrl!
},
);
}),
),
],
),
),
],
),
),
if (content.button != null) ...[
const SizedBox(width: 10),
FilledButton.tonal(
onPressed: content.button!.jumpUrl == null
? null
: () => PiliScheme.routePushFromUrl(
content.button!.jumpUrl!,
),
style: FilledButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6)),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
visualDensity:
const VisualDensity(horizontal: -2, vertical: -3),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(content.button!.jumpStyle?.text ??
content.button!.check?.text ??
''),
),
],
],
),
),
),
),
);
// 商品
case 'ADDITIONAL_TYPE_GOODS':
final content = item.modules.moduleDynamic!.additional!.goods!;
@@ -283,12 +422,6 @@ Widget addWidget(
);
}
return const SizedBox.shrink();
// case 'ADDITIONAL_TYPE_MATCH':
// final content = item.modules.moduleDynamic!.additional!.match;
// return const SizedBox.shrink();
// case 'ADDITIONAL_TYPE_COMMON':
// final content = item.modules.moduleDynamic!.additional!.common;
// return const SizedBox.shrink();
case 'ADDITIONAL_TYPE_VOTE':
final vote = item.modules.moduleDynamic!.additional!.vote!;
final borderRadius = floor == 1 ? null : StyleString.mdRadius;
@@ -376,6 +509,204 @@ Widget addWidget(
),
),
);
case 'ADDITIONAL_TYPE_COMMON':
final content = item.modules.moduleDynamic!.additional!.common!;
final borderRadius = floor == 1 ? null : StyleString.mdRadius;
return Padding(
padding: const EdgeInsets.only(top: 6),
child: Material(
color: bgColor,
borderRadius: borderRadius,
child: InkWell(
borderRadius: borderRadius,
onTap: content.jumpUrl == null
? null
: () => PiliScheme.routePushFromUrl(content.jumpUrl!),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
if (content.cover?.isNotEmpty == true) ...[
NetworkImgLayer(
width: 45,
height: 45,
src: content.cover,
radius: 6,
),
const SizedBox(width: 10),
],
Expanded(
child: Column(
spacing: 2,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (content.title?.isNotEmpty == true)
Text(content.title!),
if (content.desc1?.isNotEmpty == true)
Text(
content.desc1!,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: 13,
),
),
if (content.desc2?.isNotEmpty == true)
Text(
content.desc2!,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: 13,
),
),
],
),
),
if (content.button?.jumpUrl?.isNotEmpty == true) ...[
const SizedBox(width: 10),
FilledButton.tonal(
onPressed: () => PiliScheme.routePushFromUrl(
content.button!.jumpUrl!,
),
style: FilledButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6)),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
visualDensity:
const VisualDensity(horizontal: -2, vertical: -3),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(content.button!.jumpStyle?.text ?? ''),
),
],
],
),
),
),
),
);
case 'ADDITIONAL_TYPE_MATCH':
final content = item.modules.moduleDynamic!.additional!.match!;
final borderRadius = floor == 1 ? null : StyleString.mdRadius;
return Padding(
padding: const EdgeInsets.only(top: 6),
child: Material(
color: bgColor,
borderRadius: borderRadius,
child: InkWell(
borderRadius: borderRadius,
onTap: content.jumpUrl == null
? null
: () => PiliScheme.routePushFromUrl(content.jumpUrl!),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
if (content.matchInfo?.title?.isNotEmpty == true)
Text(
content.matchInfo!.title!,
style: const TextStyle(fontSize: 13),
),
if (content.matchInfo?.subTitle?.isNotEmpty == true)
Text(
content.matchInfo!.subTitle!,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.outline,
),
),
],
),
const Spacer(),
if (content.matchInfo?.leftTeam != null) ...[
Column(
mainAxisSize: MainAxisSize.min,
children: [
NetworkImgLayer(
width: 30,
height: 30,
src: content.matchInfo!.leftTeam!.pic,
),
const SizedBox(height: 5),
Text(
content.matchInfo!.leftTeam!.name!,
style: const TextStyle(fontSize: 13),
)
],
),
const SizedBox(width: 16),
],
Column(
children: [
if (content.matchInfo?.centerTop?.isNotEmpty == true)
Container(
height: 35,
alignment: Alignment.center,
child: Text(
content.matchInfo!.centerTop!.join(' '),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
if (content.matchInfo?.centerBottom?.isNotEmpty == true)
Text(
content.matchInfo!.centerBottom!,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.outline,
),
),
],
),
if (content.matchInfo?.rightTeam != null) ...[
const SizedBox(width: 16),
Column(
mainAxisSize: MainAxisSize.min,
children: [
NetworkImgLayer(
width: 30,
height: 30,
src: content.matchInfo!.rightTeam!.pic,
),
const SizedBox(height: 5),
Text(
content.matchInfo!.rightTeam!.name!,
style: const TextStyle(fontSize: 13),
)
],
),
],
const Spacer(),
if (content.button != null)
FilledButton.tonal(
onPressed: () => PiliScheme.routePushFromUrl(
content.button!.jumpUrl!,
),
style: FilledButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6)),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
visualDensity:
const VisualDensity(horizontal: -2, vertical: -3),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(content.button!.jumpStyle?.text ?? ''),
),
],
),
),
),
),
);
default:
if (BuildConfig.isDebug) {
return Padding(

View File

@@ -78,7 +78,7 @@ Widget content(
),
),
if (richNodes != null)
source == 'detail'
source == 'detail' && floor == 1
? SelectableText.rich(
richNodes,
style: isSave

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/badge_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/additional_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/content_panel.dart';
import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -201,6 +202,14 @@ Widget videoSeasonWidget(
overflow: source == 'detail' ? null : TextOverflow.ellipsis,
),
),
if (item.modules.moduleDynamic?.additional != null)
addWidget(
theme,
item,
context,
item.modules.moduleDynamic?.additional?.type,
floor: floor,
),
],
);
}

View File

@@ -109,7 +109,6 @@ class _HotPageState extends CommonPageState<HotPage, HotController>
'url':
'https://www.bilibili.com/h5/weekly-recommend'
},
arguments: {'off': false},
),
),
_buildEntranceItem(
@@ -122,7 +121,6 @@ class _HotPageState extends CommonPageState<HotPage, HotController>
'url':
'https://www.bilibili.com/h5/good-history'
},
arguments: {'off': false},
),
),
],

View File

@@ -33,7 +33,7 @@ class _WebviewPageState extends State<WebviewPage> {
final RxString title = ''.obs;
final RxDouble progress = 1.0.obs;
bool? _inApp;
bool? _off;
bool _off = false;
InAppWebViewController? _webViewController;
@@ -282,7 +282,7 @@ class _WebviewPageState extends State<WebviewPage> {
bool hasMatch = await PiliScheme.routePush(
navigationAction.request.url?.uriValue ?? Uri(),
selfHandle: true,
off: _off ?? true,
off: _off,
);
// debugPrint('webview: [$url], [$hasMatch]');
if (hasMatch) {

View File

@@ -108,13 +108,15 @@ class PiliScheme {
// to video reply
String? oid = uriDigitRegExp.firstMatch(path)?.group(1);
int? rpid = int.tryParse(queryParameters['comment_root_id']!);
String? commentSecondaryId =
queryParameters['comment_secondary_id'];
if (oid != null && rpid != null) {
Get.to(
arguments: {
'oid': oid,
'rpid': rpid,
'type': 1,
'id': queryParameters['comment_secondary_id'],
'id': commentSecondaryId,
},
() => Scaffold(
resizeToAvoidBottomInset: false,
@@ -142,9 +144,8 @@ class PiliScheme {
source: 'routePush',
replyType: 1,
firstFloor: null,
id: queryParameters['comment_secondary_id'] != null
? int.tryParse(
queryParameters['comment_secondary_id']!)
id: commentSecondaryId != null
? int.tryParse(commentSecondaryId)
: null,
),
),
@@ -369,13 +370,15 @@ class PiliScheme {
if (commentRootId != null) {
String? dynId = uriDigitRegExp.firstMatch(path)?.group(1);
int? rpid = int.tryParse(commentRootId);
final commentSecondaryId =
queryParameters['comment_secondary_id'];
if (dynId != null && rpid != null) {
Get.to(
arguments: {
'oid': oid ?? dynId,
'rpid': rpid,
'type': businessId ?? 17,
'id': queryParameters['comment_secondary_id'],
'id': commentSecondaryId,
},
() => Scaffold(
resizeToAvoidBottomInset: false,
@@ -399,9 +402,8 @@ class PiliScheme {
source: 'routePush',
replyType: businessId ?? 17,
firstFloor: null,
id: queryParameters['comment_secondary_id'] != null
? int.tryParse(
queryParameters['comment_secondary_id']!)
id: commentSecondaryId != null
? int.tryParse(commentSecondaryId)
: null,
),
),
@@ -448,8 +450,7 @@ class PiliScheme {
return false;
case 'm.bilibili.com':
// bilibili://m.bilibili.com/topic-detail?topic_id=1028161&frommodule=H5&h5awaken=xxx
final id =
RegExp(r'topic_id=(\d+)').firstMatch(uri.query)?.group(1);
final id = uri.queryParameters['topic_id'];
if (id != null) {
PageUtils.toDupNamed(
'/dynTopic',
@@ -589,27 +590,24 @@ class PiliScheme {
launchURL();
return false;
}
final String? area = pathSegments.first == 'mobile'
? pathSegments.getOrNull(1)
: pathSegments.first;
final String? area =
pathSegments.first == 'mobile' || pathSegments.first == 'h5'
? pathSegments.getOrNull(1)
: pathSegments.first;
debugPrint('area: $area');
switch (area) {
case 'h5':
if (path.startsWith('/h5/note')) {
String? id = RegExp(r'cvid=(\d+)', caseSensitive: false)
.firstMatch(uri.query)
?.group(1);
if (id != null) {
PageUtils.toDupNamed(
'/articlePage',
parameters: {
'id': id,
'type': 'read',
},
off: off,
);
return true;
}
case 'note':
String? id = uri.queryParameters['cvid'];
if (id != null) {
PageUtils.toDupNamed(
'/articlePage',
parameters: {
'id': id,
'type': 'read',
},
off: off,
);
return true;
}
launchURL();
return false;
@@ -742,7 +740,7 @@ class PiliScheme {
launchURL();
return false;
case 'topic-detail':
String? id = RegExp(r'topic_id=(\d+)').firstMatch(uri.query)?.group(1);
String? id = uri.queryParameters['topic_id'];
if (id != null) {
PageUtils.toDupNamed(
'/dynTopic',
@@ -753,6 +751,58 @@ class PiliScheme {
}
launchURL();
return false;
case 'comment':
// https://www.bilibili.com/h5/comment/sub?oid=123456&pageType=1&root=87654321
final queryParameters = uri.queryParameters;
String? oid = queryParameters['oid'];
String? root = queryParameters['root'];
String? pageType = queryParameters['pageType'];
if (oid != null && root != null && pageType != null) {
String? commentSecondaryId = queryParameters['comment_secondary_id'];
Get.to(
arguments: {
'oid': oid,
'rpid': root,
'type': pageType,
'id': commentSecondaryId,
},
() => Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('评论详情'),
actions: pageType == '1'
? [
IconButton(
tooltip: '前往',
onPressed: () {
videoPush(int.parse(oid), null);
},
icon: const Icon(Icons.open_in_new),
),
]
: null,
),
body: SafeArea(
top: false,
bottom: false,
child: VideoReplyReplyPanel(
enableSlide: false,
oid: int.parse(oid),
rpid: int.parse(root),
source: 'routePush',
replyType: int.parse(pageType),
firstFloor: null,
id: commentSecondaryId != null
? int.tryParse(commentSecondaryId)
: null,
),
),
),
);
return true;
}
launchURL();
return false;
default:
Map map = IdUtils.matchAvorBv(input: area?.split('?').first);
if (map.isNotEmpty) {