import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo, DetailListReply, Mode; import 'package:PiliPlus/grpc/reply.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/pages/common/reply_controller.dart'; import 'package:PiliPlus/pages/video/reply_new/view.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get/get_navigation/src/dialog/dialog_route.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class VideoReplyReplyController extends ReplyController with GetSingleTickerProviderStateMixin { VideoReplyReplyController({ required this.hasRoot, required this.id, required this.oid, required this.rpid, required this.dialog, required this.replyType, required this.isDialogue, }); final int? dialog; final bool isDialogue; final itemScrollCtr = ItemScrollController(); bool hasRoot = false; int? id; // 视频aid 请求时使用的oid int oid; // rpid 请求楼中楼回复 int rpid; int replyType; // = ReplyType.video; ReplyInfo? firstFloor; int? index; AnimationController? controller; late final horizontalPreview = Pref.horizontalPreview; @override dynamic get sourceId => replyType == 1 ? IdUtils.av2bv(oid) : oid; @override void onInit() { super.onInit(); mode.value = Mode.MAIN_LIST_TIME; queryData(); } @override Future onRefresh() { paginationReply = null; return super.onRefresh(); } @override List? getDataList(response) { return isDialogue ? response.replies : response.root.replies; } @override bool customHandleResponse(bool isRefresh, Success response) { final data = response.response; upMid ??= data.subjectControl.upMid; paginationReply = data.paginationReply; isEnd = data.cursor.isEnd; // reply2Reply // isDialogue.not if (data is DetailListReply) { count.value = data.root.count.toInt(); if (isRefresh && firstFloor == null) { firstFloor = data.root; } if (id != null) { index = data.root.replies.indexWhere((item) => item.id.toInt() == id); if (index == -1) { index = null; } else { controller = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); WidgetsBinding.instance.addPostFrameCallback((_) async { if (index != null) { try { itemScrollCtr.jumpTo( index: hasRoot ? index! + 3 : index! + 1, alignment: 0.25, ); await Future.delayed(const Duration(milliseconds: 800)); await controller?.forward(); index = null; } catch (_) {} } }); } id = null; } } return false; } @override Future customGetData() => isDialogue ? ReplyGrpc.dialogList( type: replyType, oid: oid, root: rpid, dialog: dialog!, offset: paginationReply?.nextOffset, ) : ReplyGrpc.detailList( type: replyType, oid: oid, root: rpid, rpid: id ?? 0, mode: mode.value, offset: paginationReply?.nextOffset, ); @override void queryBySort() { mode.value = mode.value == Mode.MAIN_LIST_HOT ? Mode.MAIN_LIST_TIME : Mode.MAIN_LIST_HOT; onReload(); } @override void onReply( BuildContext context, { int? oid, ReplyInfo? replyItem, int? replyType, int? index, }) { assert(replyItem != null && index != null); final oid = replyItem!.oid.toInt(); final root = replyItem.id.toInt(); final key = oid + root; Navigator.of(context) .push( GetDialogRoute( pageBuilder: (buildContext, animation, secondaryAnimation) { return ReplyPage( oid: oid, root: root, parent: root, replyType: this.replyType, replyItem: replyItem, items: savedReplies[key], onSave: (reply) { if (reply.isEmpty) { savedReplies.remove(key); } else { savedReplies[key] = reply.toList(); } }, ); }, transitionDuration: const Duration(milliseconds: 500), transitionBuilder: (context, animation, secondaryAnimation, child) { const begin = Offset(0.0, 1.0); const end = Offset.zero; const curve = Curves.linear; var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); return SlideTransition( position: animation.drive(tween), child: child, ); }, ), ) .then((res) { if (res != null) { savedReplies.remove(key); ReplyInfo replyInfo = RequestUtils.replyCast(res); count.value += 1; loadingState ..value.dataOrNull?.insert(index! + 1, replyInfo) ..refresh(); if (enableCommAntifraud) { onCheckReply(replyInfo, isManual: false); } } }); } @override void onClose() { controller?.dispose(); super.dispose(); } }