opt: save unsent replies

This commit is contained in:
bggRGjQaUbCoE
2024-09-02 14:50:21 +08:00
parent b7c7f3743d
commit 915688e4d1
3 changed files with 115 additions and 36 deletions

View File

@@ -37,6 +37,8 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
late VideoReplyController _videoReplyController; late VideoReplyController _videoReplyController;
late AnimationController fabAnimationCtr; late AnimationController fabAnimationCtr;
late final _savedReplies = {};
bool _isFabVisible = true; bool _isFabVisible = true;
String replyLevel = '1'; String replyLevel = '1';
late String heroTag; late String heroTag;
@@ -218,6 +220,59 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
replyReply: (replyItem) => replyReply: (replyItem) =>
replyReply(replyItem), replyReply(replyItem),
replyType: ReplyType.video, replyType: ReplyType.video,
onReply: () {
dynamic oid = _videoReplyController
.replyList[index].oid;
dynamic root = _videoReplyController
.replyList[index].rpid;
dynamic parent = _videoReplyController
.replyList[index].rpid;
dynamic key = oid + root + parent;
Navigator.of(context)
.push(
GetDialogRoute(
pageBuilder: (buildContext, animation,
secondaryAnimation) {
return ReplyPage(
oid: oid,
root: root,
parent: parent,
replyType: ReplyType.video,
replyItem: _videoReplyController
.replyList[index],
savedReply: _savedReplies[key],
onSaveReply: (reply) {
_savedReplies[key] = reply;
},
);
},
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((value) {
// 完成评论,数据添加
if (value != null &&
value['data'] != null) {
_savedReplies[key] = null;
}
});
},
); );
} }
}, },
@@ -243,46 +298,51 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
heroTag: null, heroTag: null,
onPressed: () { onPressed: () {
feedBack(); feedBack();
dynamic oid = _videoReplyController.aid ??
IdUtils.bv2av(Get.parameters['bvid']!);
Navigator.of(context) Navigator.of(context)
.push( .push(
GetDialogRoute( GetDialogRoute(
pageBuilder: pageBuilder:
(buildContext, animation, secondaryAnimation) { (buildContext, animation, secondaryAnimation) {
return ReplyPage( return ReplyPage(
oid: _videoReplyController.aid ?? oid: oid,
IdUtils.bv2av(Get.parameters['bvid']!), root: 0,
root: 0, parent: 0,
parent: 0, replyType: ReplyType.video,
replyType: ReplyType.video, savedReply: _savedReplies[oid],
); onSaveReply: (reply) {
_savedReplies[oid] = reply;
}, },
transitionDuration: const Duration(milliseconds: 500), );
transitionBuilder: },
(context, animation, secondaryAnimation, child) { transitionDuration: const Duration(milliseconds: 500),
const begin = Offset(0.0, 1.0); transitionBuilder:
const end = Offset.zero; (context, animation, secondaryAnimation, child) {
const curve = Curves.linear; const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.linear;
var tween = Tween(begin: begin, end: end) var tween = Tween(begin: begin, end: end)
.chain(CurveTween(curve: curve)); .chain(CurveTween(curve: curve));
return SlideTransition( return SlideTransition(
position: animation.drive(tween), position: animation.drive(tween),
child: child, child: child,
); );
}, },
), ),
) )
.then( .then(
(value) => { (value) {
// 完成评论,数据添加 // 完成评论,数据添加
if (value != null && value['data'] != null) if (value != null && value['data'] != null) {
{ _savedReplies[oid] = null;
_videoReplyController.replyList _videoReplyController.replyList
.insert(0, value['data']) .insert(0, value['data']);
} }
}, },
); );
// showModalBottomSheet( // showModalBottomSheet(
// context: context, // context: context,
// isScrollControlled: true, // isScrollControlled: true,

View File

@@ -31,6 +31,7 @@ class ReplyItem extends StatelessWidget {
this.replyReply, this.replyReply,
this.replyType, this.replyType,
this.needDivider = true, this.needDivider = true,
this.onReply,
super.key, super.key,
}); });
final ReplyItemModel? replyItem; final ReplyItemModel? replyItem;
@@ -40,6 +41,7 @@ class ReplyItem extends StatelessWidget {
final Function? replyReply; final Function? replyReply;
final ReplyType? replyType; final ReplyType? replyType;
final bool needDivider; final bool needDivider;
final Function()? onReply;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -303,6 +305,10 @@ class ReplyItem extends StatelessWidget {
child: TextButton( child: TextButton(
onPressed: () { onPressed: () {
feedBack(); feedBack();
if (onReply != null) {
onReply!();
return;
}
Navigator.of(context) Navigator.of(context)
.push( .push(
GetDialogRoute( GetDialogRoute(

View File

@@ -23,6 +23,8 @@ class ReplyPage extends StatefulWidget {
final int? parent; final int? parent;
final ReplyType? replyType; final ReplyType? replyType;
final ReplyItemModel? replyItem; final ReplyItemModel? replyItem;
final String? savedReply;
final Function(String reply)? onSaveReply;
const ReplyPage({ const ReplyPage({
super.key, super.key,
@@ -31,6 +33,8 @@ class ReplyPage extends StatefulWidget {
this.parent, this.parent,
this.replyType, this.replyType,
this.replyItem, this.replyItem,
this.savedReply,
this.onSaveReply,
}); });
@override @override
@@ -41,7 +45,8 @@ class _ReplyPageState extends State<ReplyPage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late final _focusNode = FocusNode(); late final _focusNode = FocusNode();
late final _controller = ChatBottomPanelContainerController<PanelType>(); late final _controller = ChatBottomPanelContainerController<PanelType>();
final TextEditingController _replyContentController = TextEditingController(); late final TextEditingController _replyContentController =
TextEditingController(text: widget.savedReply);
PanelType _currentPanelType = PanelType.none; PanelType _currentPanelType = PanelType.none;
bool _readOnly = false; bool _readOnly = false;
final _readOnlyStream = StreamController<bool>(); final _readOnlyStream = StreamController<bool>();
@@ -54,6 +59,11 @@ class _ReplyPageState extends State<ReplyPage>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.savedReply != null && widget.savedReply!.isNotEmpty) {
_enablePublish = true;
}
() async { () async {
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
if (mounted) { if (mounted) {
@@ -194,6 +204,9 @@ class _ReplyPageState extends State<ReplyPage>
_enablePublish = false; _enablePublish = false;
_publishStream.add(false); _publishStream.add(false);
} }
if (widget.onSaveReply != null) {
widget.onSaveReply!(value);
}
}, },
focusNode: _focusNode, focusNode: _focusNode,
decoration: const InputDecoration( decoration: const InputDecoration(
@@ -255,7 +268,7 @@ class _ReplyPageState extends State<ReplyPage>
), ),
const Spacer(), const Spacer(),
StreamBuilder( StreamBuilder(
initialData: false, initialData: _enablePublish,
stream: _publishStream.stream, stream: _publishStream.stream,
builder: (_, snapshot) => FilledButton.tonal( builder: (_, snapshot) => FilledButton.tonal(
onPressed: snapshot.data == true ? submitReplyAdd : null, onPressed: snapshot.data == true ? submitReplyAdd : null,