faat: message to comment
This commit is contained in:
@ -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(
|
||||
|
||||
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
89
lib/pages/message/utils/index.dart
Normal file
89
lib/pages/message/utils/index.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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,15 +85,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: widget.source == 'videoDetail' ? widget.sheetHeight : null,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.source == 'videoDetail')
|
||||
AppBar(
|
||||
Widget _buildAppBar() {
|
||||
return AppBar(
|
||||
toolbarHeight: 45,
|
||||
automaticallyImplyLeading: false,
|
||||
centerTitle: false,
|
||||
@ -101,13 +99,87 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
icon: const Icon(Icons.close, size: 20),
|
||||
onPressed: () {
|
||||
_videoReplyReplyController.currentPage = 0;
|
||||
widget.closePanel?.call;
|
||||
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(
|
||||
height: widget.source == 'videoDetail' ? widget.sheetHeight : null,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
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,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
height: 20,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
thickness: 6,
|
||||
),
|
||||
child: _buildReplyItem(widget.firstFloor, '2'),
|
||||
),
|
||||
),
|
||||
],
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return _buildSliverList();
|
||||
} 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 请求错误
|
||||
return HttpError(
|
||||
errMsg: data?['msg'] ?? '请求错误',
|
||||
fn: () => setState(() {}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return const VideoReplySkeleton();
|
||||
}, childCount: 8),
|
||||
},
|
||||
childCount: 8,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -237,7 +248,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user