opt: reply type (#850)

This commit is contained in:
My-Responsitories
2025-05-11 16:38:15 +08:00
committed by GitHub
parent 0b57cd3555
commit c899ea95e1
5 changed files with 77 additions and 80 deletions

View File

@@ -138,7 +138,7 @@ class ReplyHttp {
} }
@Deprecated('Use replyReplyListGrpc instead') @Deprecated('Use replyReplyListGrpc instead')
static Future<LoadingState> replyReplyList({ static Future<LoadingState<ReplyReplyData>> replyReplyList({
required bool isLogin, required bool isLogin,
required int oid, required int oid,
required int root, required int root,

View File

@@ -33,7 +33,7 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
late final bool isLogin = Accounts.main.isLogin; late final bool isLogin = Accounts.main.isLogin;
dynamic upMid; Int64? upMid;
Int64? cursorNext; Int64? cursorNext;
FeedPaginationReply? paginationReply; FeedPaginationReply? paginationReply;
late Rx<Mode> mode = Mode.MAIN_LIST_HOT.obs; late Rx<Mode> mode = Mode.MAIN_LIST_HOT.obs;
@@ -256,8 +256,8 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
// ref https://github.com/freedom-introvert/biliSendCommAntifraud // ref https://github.com/freedom-introvert/biliSendCommAntifraud
Future<void> checkReply({ Future<void> checkReply({
required BuildContext context, required BuildContext context,
required dynamic oid, required int oid,
required dynamic rpid, required int? rpid,
required int replyType, required int replyType,
required int replyId, required int replyId,
required String message, required String message,
@@ -346,7 +346,7 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
// not found // not found
if (context.mounted.not) return; if (context.mounted.not) return;
// cookie check // cookie check
dynamic res1 = await ReplyHttp.replyReplyList( final res1 = await ReplyHttp.replyReplyList(
isLogin: isLogin, isLogin: isLogin,
oid: oid, oid: oid,
root: rpid ?? replyId, root: rpid ?? replyId,
@@ -363,11 +363,11 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
'无法找到你的评论。\n\n你的评论:$message', '无法找到你的评论。\n\n你的评论:$message',
); );
} }
} else if (res1 is Success) { } else {
// found // found
if (context.mounted.not) return; if (context.mounted.not) return;
// no cookie check // no cookie check
dynamic res2 = await ReplyHttp.replyReplyList( final res2 = await ReplyHttp.replyReplyList(
isLogin: false, isLogin: false,
oid: oid, oid: oid,
root: rpid ?? replyId, root: rpid ?? replyId,
@@ -387,7 +387,7 @@ abstract class ReplyController<R> extends CommonListController<R, ReplyInfo> {
: '评论不可见(${res2.errMsg}): $message', : '评论不可见(${res2.errMsg}): $message',
); );
} }
} else if (res2 is Success) { } else {
// found // found
if (context.mounted) { if (context.mounted) {
showReplyCheckResult(isManual showReplyCheckResult(isManual
@@ -404,12 +404,12 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
} }
} }
} else { } else {
for (int i = 1; true; i++) { for (int i = 1;; i++) {
if (context.mounted.not) return; if (context.mounted.not) return;
dynamic res3 = await ReplyHttp.replyReplyList( final res3 = await ReplyHttp.replyReplyList(
isLogin: false, isLogin: false,
oid: oid, oid: oid,
root: rpid ?? replyId, root: rpid,
pageNum: i, pageNum: i,
type: replyType, type: replyType,
filterBanWord: false, filterBanWord: false,
@@ -418,8 +418,8 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
); );
if (res3 is Error) { if (res3 is Error) {
break; break;
} else if (res3 is Success) { } else {
ReplyReplyData data = res3.response; final data = res3.data;
if (data.replies.isNullOrEmpty) { if (data.replies.isNullOrEmpty) {
break; break;
} }
@@ -439,12 +439,12 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
} }
} }
for (int i = 1; true; i++) { for (int i = 1;; i++) {
if (context.mounted.not) return; if (context.mounted.not) return;
dynamic res4 = await ReplyHttp.replyReplyList( final res4 = await ReplyHttp.replyReplyList(
isLogin: true, isLogin: true,
oid: oid, oid: oid,
root: rpid ?? replyId, root: rpid,
pageNum: i, pageNum: i,
type: replyType, type: replyType,
filterBanWord: false, filterBanWord: false,
@@ -453,8 +453,8 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
); );
if (res4 is Error) { if (res4 is Error) {
break; break;
} else if (res4 is Success) { } else {
ReplyReplyData data = res4.response; final data = res4.data;
if (data.replies.isNullOrEmpty) { if (data.replies.isNullOrEmpty) {
break; break;
} }

View File

@@ -25,6 +25,7 @@ import 'package:PiliPlus/utils/url_utils.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:fixnum/fixnum.dart';
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';
@@ -54,7 +55,7 @@ class ReplyItemGrpc extends StatelessWidget {
final bool needDivider; final bool needDivider;
final VoidCallback? onReply; final VoidCallback? onReply;
final ValueChanged<int?>? onDelete; final ValueChanged<int?>? onDelete;
final dynamic upMid; final Int64? upMid;
final VoidCallback? showDialogue; final VoidCallback? showDialogue;
final Function? getTag; final Function? getTag;
final VoidCallback? onViewImage; final VoidCallback? onViewImage;
@@ -667,7 +668,7 @@ class ReplyItemGrpc extends StatelessWidget {
content.atNameToMid.containsKey(matchStr.substring(1))) { content.atNameToMid.containsKey(matchStr.substring(1))) {
// 处理@用户 // 处理@用户
final String userName = matchStr.substring(1); final String userName = matchStr.substring(1);
final int userId = content.atNameToMid[userName]!.toInt(); final userId = content.atNameToMid[userName]!.toString();
spanChildren.add( spanChildren.add(
TextSpan( TextSpan(
text: matchStr, text: matchStr,
@@ -966,8 +967,8 @@ class ReplyItemGrpc extends StatelessWidget {
required onDelete, required onDelete,
required bool isSubReply, required bool isSubReply,
}) { }) {
int ownerMid = Accounts.main.mid; final ownerMid = Int64(Accounts.main.mid);
Future<dynamic> menuActionHandler(String type) async { Future<void> menuActionHandler(String type) async {
late String message = item.content.message; late String message = item.content.message;
switch (type) { switch (type) {
case 'report': case 'report':
@@ -1100,7 +1101,7 @@ class ReplyItemGrpc extends StatelessWidget {
} }
final theme = Theme.of(context); final theme = Theme.of(context);
Color errorColor = theme.colorScheme.error; final errorColor = theme.colorScheme.error;
final style = theme.textTheme.titleSmall; final style = theme.textTheme.titleSmall;
return Padding( return Padding(
@@ -1129,21 +1130,21 @@ class ReplyItemGrpc extends StatelessWidget {
), ),
), ),
), ),
if (ownerMid == upMid.toInt() || ownerMid == item.member.mid.toInt()) if (ownerMid == upMid || ownerMid == item.member.mid)
ListTile( ListTile(
onTap: () => menuActionHandler('delete'), onTap: () => menuActionHandler('delete'),
minLeadingWidth: 0, minLeadingWidth: 0,
leading: Icon(Icons.delete_outlined, color: errorColor, size: 19), leading: Icon(Icons.delete_outlined, color: errorColor, size: 19),
title: Text('删除', style: style!.copyWith(color: errorColor)), title: Text('删除', style: style!.copyWith(color: errorColor)),
), ),
if (ownerMid != 0) if (ownerMid != Int64.ZERO)
ListTile( ListTile(
onTap: () => menuActionHandler('report'), onTap: () => menuActionHandler('report'),
minLeadingWidth: 0, minLeadingWidth: 0,
leading: Icon(Icons.error_outline, color: errorColor, size: 19), leading: Icon(Icons.error_outline, color: errorColor, size: 19),
title: Text('举报', style: style!.copyWith(color: errorColor)), title: Text('举报', style: style!.copyWith(color: errorColor)),
), ),
if (replyLevel == '1' && isSubReply.not && ownerMid == upMid.toInt()) if (replyLevel == '1' && isSubReply.not && ownerMid == upMid)
ListTile( ListTile(
onTap: () => menuActionHandler('top'), onTap: () => menuActionHandler('top'),
minLeadingWidth: 0, minLeadingWidth: 0,
@@ -1171,7 +1172,7 @@ class ReplyItemGrpc extends StatelessWidget {
leading: const Icon(Icons.save_alt, size: 19), leading: const Icon(Icons.save_alt, size: 19),
title: Text('保存评论', style: style), title: Text('保存评论', style: style),
), ),
if (item.mid.toInt() == ownerMid) if (item.mid == ownerMid)
ListTile( ListTile(
onTap: () => menuActionHandler('checkReply'), onTap: () => menuActionHandler('checkReply'),
minLeadingWidth: 0, minLeadingWidth: 0,

View File

@@ -32,7 +32,7 @@ class VideoReplyReplyController extends ReplyController
int rpid; int rpid;
ReplyType replyType; // = ReplyType.video; ReplyType replyType; // = ReplyType.video;
dynamic firstFloor; ReplyInfo? firstFloor;
int? index; int? index;
AnimationController? controller; AnimationController? controller;
@@ -65,9 +65,9 @@ class VideoReplyReplyController extends ReplyController
bool customHandleResponse(bool isRefresh, Success response) { bool customHandleResponse(bool isRefresh, Success response) {
final data = response.response; final data = response.response;
upMid ??= data.subjectControl.upMid.toInt(); upMid ??= data.subjectControl.upMid;
paginationReply = data.paginationReply; paginationReply = data.paginationReply;
isEnd = data.cursor?.isEnd ?? false; isEnd = data.cursor.isEnd;
// reply2Reply // isDialogue.not // reply2Reply // isDialogue.not
if (data is DetailListReply) { if (data is DetailListReply) {

View File

@@ -37,7 +37,7 @@ class VideoReplyReplyPanel extends CommonSlidePage {
final int oid; final int oid;
final int rpid; final int rpid;
final int? dialog; final int? dialog;
final dynamic firstFloor; final ReplyInfo? firstFloor;
final String? source; final String? source;
final ReplyType replyType; final ReplyType replyType;
final bool isDialogue; final bool isDialogue;
@@ -53,14 +53,14 @@ class _VideoReplyReplyPanelState
extends CommonSlidePageState<VideoReplyReplyPanel> extends CommonSlidePageState<VideoReplyReplyPanel>
with TickerProviderStateMixin { with TickerProviderStateMixin {
late VideoReplyReplyController _videoReplyReplyController; late VideoReplyReplyController _videoReplyReplyController;
late final _savedReplies = {}; late final _savedReplies = <int, String>{};
late final itemPositionsListener = ItemPositionsListener.create(); late final itemPositionsListener = ItemPositionsListener.create();
late final _key = GlobalKey<ScaffoldState>(); late final _key = GlobalKey<ScaffoldState>();
late final _listKey = GlobalKey(); late final _listKey = GlobalKey();
late final _tag = late final _tag =
Utils.makeHeroTag('${widget.rpid}${widget.dialog}${widget.isDialogue}'); Utils.makeHeroTag('${widget.rpid}${widget.dialog}${widget.isDialogue}');
dynamic get firstFloor => ReplyInfo? get firstFloor =>
widget.firstFloor ?? _videoReplyReplyController.firstFloor; widget.firstFloor ?? _videoReplyReplyController.firstFloor;
bool get _horizontalPreview => bool get _horizontalPreview =>
@@ -179,11 +179,11 @@ class _VideoReplyReplyPanelState
} else if (firstFloor != null) { } else if (firstFloor != null) {
if (index == 0) { if (index == 0) {
return ReplyItemGrpc( return ReplyItemGrpc(
replyItem: firstFloor, replyItem: firstFloor!,
replyLevel: '2', replyLevel: '2',
needDivider: false, needDivider: false,
onReply: () { onReply: () {
_onReply(firstFloor, -1); _onReply(firstFloor!, -1);
}, },
upMid: _videoReplyReplyController.upMid, upMid: _videoReplyReplyController.upMid,
onViewImage: widget.onViewImage, onViewImage: widget.onViewImage,
@@ -287,41 +287,40 @@ class _VideoReplyReplyPanelState
), ),
); );
Function(dynamic imgList, dynamic index)? get _getImageCallback => Function(List<String>, int)? get _getImageCallback => _horizontalPreview
_horizontalPreview ? (imgList, index) {
? (imgList, index) { final ctr = AnimationController(
final ctr = AnimationController( vsync: this,
vsync: this, duration: const Duration(milliseconds: 200),
duration: const Duration(milliseconds: 200), )..forward();
)..forward(); PageUtils.onHorizontalPreview(
PageUtils.onHorizontalPreview( _key,
_key, AnimationController(
AnimationController( vsync: this,
vsync: this, duration: Duration.zero,
duration: Duration.zero, ),
), ctr,
ctr, imgList,
imgList, index,
index, (value) async {
(value) async { if (value == false) {
if (value == false) { await ctr.reverse();
await ctr.reverse(); }
} try {
try { ctr.dispose();
ctr.dispose(); } catch (_) {}
} catch (_) {} if (value == false) {
if (value == false) { Get.back();
Get.back(); }
} },
}, );
); }
} : null;
: null;
void _onReply(dynamic item, int index) { void _onReply(ReplyInfo item, int index) {
dynamic oid = item?.oid.toInt(); final oid = item.oid.toInt();
dynamic root = item?.id.toInt(); final root = item.id.toInt();
dynamic key = oid + root; final key = oid + root;
Navigator.of(context) Navigator.of(context)
.push( .push(
@@ -357,7 +356,7 @@ class _VideoReplyReplyPanelState
) )
.then((res) { .then((res) {
if (res != null) { if (res != null) {
_savedReplies[key] = null; _savedReplies.remove(key);
ReplyInfo replyInfo = RequestUtils.replyCast(res); ReplyInfo replyInfo = RequestUtils.replyCast(res);
_videoReplyReplyController.loadingState.value.dataOrNull _videoReplyReplyController.loadingState.value.dataOrNull
?.insert(index + 1, replyInfo); ?.insert(index + 1, replyInfo);
@@ -385,7 +384,8 @@ class _VideoReplyReplyPanelState
}); });
} }
Widget _buildBody(ThemeData theme, LoadingState loadingState, int index) { Widget _buildBody(
ThemeData theme, LoadingState<List<ReplyInfo>?> loadingState, int index) {
return switch (loadingState) { return switch (loadingState) {
Loading() => IgnorePointer( Loading() => IgnorePointer(
child: CustomScrollView( child: CustomScrollView(
@@ -402,7 +402,7 @@ class _VideoReplyReplyPanelState
), ),
), ),
Success(:var response) => () { Success(:var response) => () {
if (index == response.length) { if (index == response!.length) {
_videoReplyReplyController.onLoadMore(); _videoReplyReplyController.onLoadMore();
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
@@ -445,7 +445,7 @@ class _VideoReplyReplyPanelState
}; };
} }
Widget _replyItem(replyItem, index) { Widget _replyItem(ReplyInfo replyItem, int index) {
return ReplyItemGrpc( return ReplyItemGrpc(
replyItem: replyItem, replyItem: replyItem,
replyLevel: widget.isDialogue ? '3' : '2', replyLevel: widget.isDialogue ? '3' : '2',
@@ -484,18 +484,14 @@ class _VideoReplyReplyPanelState
); );
} }
int _itemCount(LoadingState loadingState) { int _itemCount(LoadingState<List<ReplyInfo>?> loadingState) {
if (widget.isDialogue) { if (widget.isDialogue) {
return (loadingState is Success ? loadingState.response.length : 0) + 1; return (loadingState.dataOrNull?.length ?? 0) + 1;
} }
int itemCount = 0; int itemCount = 0;
if (firstFloor != null) { if (firstFloor != null) {
itemCount = 2; itemCount = 2;
} }
if (loadingState is Success) { return (loadingState.dataOrNull?.length ?? 0) + itemCount + 2;
return loadingState.response.length + itemCount + 2;
} else {
return itemCount + 2;
}
} }
} }