faat: message to comment

This commit is contained in:
guozhigq
2024-10-19 18:16:04 +08:00
parent b4ccd2f4bf
commit be29f70b30
7 changed files with 230 additions and 161 deletions

View File

@ -101,6 +101,7 @@ class ReplyReplyData {
this.page,
this.config,
this.replies,
this.root,
this.topReplies,
this.upper,
});
@ -108,6 +109,7 @@ class ReplyReplyData {
ReplyPage? page;
ReplyConfig? config;
late List<ReplyItemModel>? replies;
ReplyItemModel? root;
late List<ReplyItemModel>? topReplies;
ReplyUpper? upper;
@ -120,6 +122,9 @@ class ReplyReplyData {
(item) => ReplyItemModel.fromJson(item, json['upper']['mid']))
.toList()
: [];
root = json['root'] != null
? ReplyItemModel.fromJson(json['root'], false)
: null;
topReplies = json['top_replies'] != null
? json['top_replies']
.map<ReplyItemModel>((item) => ReplyItemModel.fromJson(

View File

@ -1,13 +1,11 @@
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/msg/like.dart';
import 'package:pilipala/utils/utils.dart';
import '../utils/index.dart';
import 'controller.dart';
class MessageLikePage extends StatefulWidget {
@ -122,39 +120,13 @@ class LikeItem extends StatelessWidget {
final nickNameList = item.users!.map((e) => e.nickname).take(2).toList();
int usersLen = item.users!.length > 3 ? 3 : item.users!.length;
final Uri uri = Uri.parse(item.item!.uri!);
final String path = uri.path;
final String bvid = path.split('/').last;
/// bilibili://
final Uri nativeUri = Uri.parse(item.item!.nativeUri!);
final Map<String, String> queryParameters = nativeUri.queryParameters;
final String type = item.item!.type!;
// cid
final String? argCid = queryParameters['cid'];
// 页码
final String? page = queryParameters['page'];
// 根评论id
final String? commentRootId = queryParameters['comment_root_id'];
// 二级评论id
final String? commentSecondaryId = queryParameters['comment_secondary_id'];
return InkWell(
onTap: () async {
try {
final int cid = argCid != null
? int.parse(argCid)
: await SearchHttp.ab2c(bvid: bvid);
final String heroTag = Utils.makeHeroTag(bvid);
Get.toNamed<dynamic>(
'/video?bvid=$bvid&cid=$cid',
arguments: <String, String?>{
'pic': '',
'heroTag': heroTag,
},
);
} catch (e) {
SmartDialog.showToast('视频可能失效了$e');
}
MessageUtils.onClickMessage(context, uri, nativeUri, type);
},
child: Stack(
children: [
@ -243,6 +215,7 @@ class LikeItem extends StatelessWidget {
width: 60,
height: 60,
src: item.item!.image,
radius: 6,
),
],
),

View File

@ -4,10 +4,9 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/msg/reply.dart';
import 'package:pilipala/pages/message/utils/index.dart';
import 'package:pilipala/utils/utils.dart';
import 'controller.dart';
class MessageReplyPage extends StatefulWidget {
@ -112,28 +111,14 @@ class ReplyItem extends StatelessWidget {
Widget build(BuildContext context) {
Color outline = Theme.of(context).colorScheme.outline;
final String heroTag = Utils.makeHeroTag(item.user!.mid);
final String bvid = item.item!.uri!.split('/').last;
// 页码
final String page =
item.item!.nativeUri!.split('page=').last.split('&').first;
// 根评论id
final String commentRootId =
item.item!.nativeUri!.split('comment_root_id=').last.split('&').first;
// 二级评论id
final String commentSecondaryId =
item.item!.nativeUri!.split('comment_secondary_id=').last;
final Uri uri = Uri.parse(item.item!.uri!);
/// bilibili://
final Uri nativeUri = Uri.parse(item.item!.nativeUri!);
final String type = item.item!.type!;
return InkWell(
onTap: () async {
final int cid = await SearchHttp.ab2c(bvid: bvid);
final String heroTag = Utils.makeHeroTag(bvid);
Get.toNamed<dynamic>(
'/video?bvid=$bvid&cid=$cid',
arguments: <String, String?>{
'pic': '',
'heroTag': heroTag,
},
);
MessageUtils.onClickMessage(context, uri, nativeUri, type);
},
child: Padding(
padding: const EdgeInsets.all(14),
@ -217,6 +202,7 @@ class ReplyItem extends StatelessWidget {
width: 60,
height: 60,
src: item.item!.image,
radius: 6,
),
],
),

View File

@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/pages/video/detail/reply_reply/index.dart';
import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/utils.dart';
class MessageUtils {
// 回复我的、收到的赞点击
static void onClickMessage(
BuildContext context, Uri uri, Uri nativeUri, String type) async {
final String path = uri.path;
final String bvid = path.split('/').last;
final String nativePath = nativeUri.path;
final String oid = nativePath.split('/').last;
final Map<String, String> queryParameters = nativeUri.queryParameters;
final String? argCid = queryParameters['cid'];
// final String? page = queryParameters['page'];
final String? commentRootId = queryParameters['comment_root_id'];
// final String? commentSecondaryId = queryParameters['comment_secondary_id'];
switch (type) {
case 'video':
case 'danmu':
try {
final int cid = argCid != null
? int.parse(argCid)
: await SearchHttp.ab2c(bvid: bvid);
final String heroTag = Utils.makeHeroTag(bvid);
Get.toNamed<dynamic>(
'/video?bvid=$bvid&cid=$cid',
arguments: <String, String?>{
'pic': '',
'heroTag': heroTag,
},
);
} catch (e) {
SmartDialog.showToast('视频可能失效了$e');
}
break;
case 'reply':
debugPrint('commentRootId: $oid, $commentRootId');
navigateToComment(
context, oid, commentRootId!, ReplyType.video, nativeUri);
break;
default:
break;
}
}
// 跳转查看评论
static void navigateToComment(
BuildContext context,
String oid,
String rpid,
ReplyType replyType,
Uri nativeUri,
) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: const Text('评论详情'),
actions: [
IconButton(
tooltip: '查看原内容',
onPressed: () {
PiliSchame.routePush(nativeUri);
},
icon: const Icon(Icons.open_in_new_outlined),
),
const SizedBox(width: 10),
],
),
body: VideoReplyReplyPanel(
oid: int.tryParse(oid),
rpid: int.tryParse(rpid),
source: 'routePush',
replyType: replyType,
firstFloor: null,
showRoot: true,
),
),
),
);
}
}

View File

@ -5,13 +5,15 @@ import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/item.dart';
class VideoReplyReplyController extends GetxController {
VideoReplyReplyController(this.aid, this.rpid, this.replyType);
VideoReplyReplyController(this.aid, this.rpid, this.replyType, this.showRoot);
final ScrollController scrollController = ScrollController();
// 视频aid 请求时使用的oid
int? aid;
// rpid 请求楼中楼回复
String? rpid;
ReplyType replyType = ReplyType.video;
bool showRoot = false;
ReplyItemModel? rootReply;
RxList<ReplyItemModel> replyList = <ReplyItemModel>[].obs;
// 当前页
int currentPage = 0;
@ -42,6 +44,7 @@ class VideoReplyReplyController extends GetxController {
);
if (res['status']) {
final List<ReplyItemModel> replies = res['data'].replies;
ReplyItemModel? root = res['data'].root;
if (replies.isNotEmpty) {
noMore.value = '加载中...';
if (replies.length == res['data'].page.count) {
@ -60,7 +63,9 @@ class VideoReplyReplyController extends GetxController {
return;
}
replyList.addAll(replies);
// res['data'].replies.addAll(replyList);
}
if (showRoot && root != null) {
rootReply = root;
}
}
if (replyList.isNotEmpty && currentReply != null) {

View File

@ -8,7 +8,6 @@ import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
import 'package:pilipala/utils/storage.dart';
import 'controller.dart';
class VideoReplyReplyPanel extends StatefulWidget {
@ -22,6 +21,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
this.sheetHeight,
this.currentReply,
this.loadMore = true,
this.showRoot = false,
super.key,
});
final int? oid;
@ -33,6 +33,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
final double? sheetHeight;
final dynamic currentReply;
final bool loadMore;
final bool showRoot;
@override
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
@ -49,7 +50,11 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
void initState() {
_videoReplyReplyController = Get.put(
VideoReplyReplyController(
widget.oid, widget.rpid.toString(), widget.replyType!),
widget.oid,
widget.rpid.toString(),
widget.replyType!,
widget.showRoot,
),
tag: widget.rpid.toString());
super.initState();
@ -80,6 +85,93 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
super.dispose();
}
Widget _buildAppBar() {
return AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
centerTitle: false,
title: Text(
'评论详情',
style: Theme.of(context).textTheme.titleSmall,
),
actions: [
IconButton(
icon: const Icon(Icons.close, size: 20),
onPressed: () {
_videoReplyReplyController.currentPage = 0;
widget.closePanel?.call();
Navigator.pop(context);
},
),
const SizedBox(width: 14),
],
);
}
Widget _buildReplyItem(ReplyItemModel? replyItem, String replyLevel) {
return ReplyItem(
replyItem: replyItem,
replyLevel: replyLevel,
showReplyRow: false,
addReply: (replyItem) {
_videoReplyReplyController.replyList.add(replyItem);
},
replyType: widget.replyType,
replyReply: (replyItem) => replyReply(replyItem),
);
}
Widget _buildSliverList() {
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index == 0) {
return _videoReplyReplyController.rootReply != null
? Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color:
Theme.of(context).dividerColor.withOpacity(0.1),
width: 6,
),
),
),
child: _buildReplyItem(
_videoReplyReplyController.rootReply, '1'),
)
: const SizedBox();
}
int adjustedIndex = index - 1;
if (adjustedIndex == _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 _buildReplyItem(
_videoReplyReplyController.replyList[adjustedIndex], '2');
}
},
childCount: _videoReplyReplyController.replyList.length + 2,
),
),
);
}
@override
Widget build(BuildContext context) {
return Container(
@ -87,27 +179,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
if (widget.source == 'videoDetail')
AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
centerTitle: false,
title: Text(
'评论详情',
style: Theme.of(context).textTheme.titleSmall,
),
actions: [
IconButton(
icon: const Icon(Icons.close, size: 20),
onPressed: () {
_videoReplyReplyController.currentPage = 0;
widget.closePanel?.call;
Navigator.pop(context);
},
),
const SizedBox(width: 14),
],
),
if (widget.source == 'videoDetail') _buildAppBar(),
Expanded(
child: RefreshIndicator(
onRefresh: () async {
@ -120,28 +192,22 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
child: CustomScrollView(
controller: _videoReplyReplyController.scrollController,
slivers: <Widget>[
if (widget.firstFloor != null) ...[
// const SliverToBoxAdapter(child: SizedBox(height: 10)),
if (widget.firstFloor != null)
SliverToBoxAdapter(
child: ReplyItem(
replyItem: widget.firstFloor,
replyLevel: '2',
showReplyRow: false,
addReply: (replyItem) {
_videoReplyReplyController.replyList.add(replyItem);
},
replyType: widget.replyType,
replyReply: (replyItem) => replyReply(replyItem),
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context)
.dividerColor
.withOpacity(0.1),
width: 6,
),
),
),
child: _buildReplyItem(widget.firstFloor, '2'),
),
),
SliverToBoxAdapter(
child: Divider(
height: 20,
color: Theme.of(context).dividerColor.withOpacity(0.1),
thickness: 6,
),
),
],
widget.loadMore
? FutureBuilder(
future: _futureBuilderFuture,
@ -150,76 +216,21 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
ConnectionState.done) {
Map? data = snapshot.data;
if (data != null && 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,
),
),
),
),
);
} else {
return ReplyItem(
replyItem:
_videoReplyReplyController
.replyList[index],
replyLevel: '2',
showReplyRow: false,
addReply: (replyItem) {
_videoReplyReplyController
.replyList
.add(replyItem);
},
replyType: widget.replyType,
replyReply: (replyItem) =>
replyReply(replyItem),
);
}
},
childCount: _videoReplyReplyController
.replyList.length +
1,
),
),
);
return _buildSliverList();
} else {
// 请求错误
return HttpError(
errMsg: data?['msg'] ?? '请求错误',
fn: () => setState(() {}),
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return const VideoReplySkeleton();
}, childCount: 8),
(BuildContext context, int index) {
return const VideoReplySkeleton();
},
childCount: 8,
),
);
}
},
@ -237,7 +248,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
),
),
),
)
),
],
),
),

View File

@ -15,26 +15,26 @@ class PiliSchame {
///
final SchemeEntity? value = await appScheme.getInitScheme();
if (value != null) {
_routePush(value);
routePush(value);
}
/// 完整链接进入 b23.无效
appScheme.getLatestScheme().then((SchemeEntity? value) {
if (value != null) {
_routePush(value);
routePush(value);
}
});
/// 注册从外部打开的Scheme监听信息 #
appScheme.registerSchemeListener().listen((SchemeEntity? event) {
if (event != null) {
_routePush(event);
routePush(event);
}
});
}
/// 路由跳转
static void _routePush(value) async {
static void routePush(value) async {
final String scheme = value.scheme;
final String host = value.host;
final String path = value.path;