comment_dialog.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import 'dart:math';
  2. import 'package:dartin/dartin.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/services.dart';
  5. import 'package:flutter_easyrefresh/easy_refresh.dart';
  6. import 'package:sport/bean/comment.dart';
  7. import 'package:sport/bean/post_user.dart';
  8. import 'package:sport/pages/social/notification.dart';
  9. import 'package:sport/provider/comment_sub_list_model.dart';
  10. import 'package:sport/provider/lib/provider_widget.dart';
  11. import 'package:sport/provider/lib/view_state_lifecycle.dart';
  12. import 'package:sport/router/navigator_util.dart';
  13. import 'package:sport/services/api/inject_api.dart';
  14. import 'package:sport/services/api/rest_client.dart';
  15. import 'package:sport/services/userid.dart';
  16. import 'package:sport/utils/DateFormat.dart';
  17. import 'package:sport/utils/toast.dart';
  18. import 'package:sport/widgets/dialog/alert_dialog.dart';
  19. import 'package:sport/widgets/dialog/modal_bottom_action.dart';
  20. import 'package:sport/widgets/dialog/request_dialog.dart';
  21. import 'package:sport/widgets/error.dart';
  22. import 'package:sport/widgets/image.dart';
  23. import 'package:sport/widgets/loading.dart';
  24. import 'package:sport/widgets/misc.dart';
  25. import 'package:sport/widgets/space.dart';
  26. import 'package:sport/widgets/text_input.dart' as input;
  27. class CommentDialog extends StatefulWidget {
  28. final String postId;
  29. final Comment comment;
  30. final bool showInput;
  31. CommentDialog(this.postId, this.comment, {this.showInput = false});
  32. @override
  33. State<StatefulWidget> createState() => _PageState();
  34. }
  35. var _padding = Space(
  36. width: 5,
  37. );
  38. class _PageState extends ViewStateLifecycle<CommentDialog, CommentSubListModel> with UserId, InjectApi {
  39. FocusNode _focusNode;
  40. Comment _toComment;
  41. PostUser _toUser;
  42. @override
  43. void initState() {
  44. _focusNode = FocusNode();
  45. super.initState();
  46. }
  47. @override
  48. void dispose() {
  49. _focusNode?.dispose();
  50. super.dispose();
  51. }
  52. @override
  53. Widget build(BuildContext context) {
  54. return Column(
  55. children: <Widget>[
  56. Container(
  57. width: double.infinity,
  58. height: 50,
  59. child: Stack(
  60. alignment: Alignment.center,
  61. children: <Widget>[
  62. Text(
  63. "回复详情",
  64. style: TextStyle(fontSize: 16),
  65. ),
  66. Positioned(
  67. left: 0,
  68. child: IconButton(
  69. icon: arrowBack(),
  70. onPressed: () {
  71. Navigator.of(context).pop();
  72. },
  73. ))
  74. ],
  75. ),
  76. ),
  77. Expanded(child: _buildCommentListWidget(model, widget.comment)),
  78. input.TextInput(
  79. widget.postId,
  80. focusNode: _focusNode,
  81. parentCommentId: widget.comment.id,
  82. comment: widget.showInput,
  83. toCommentId: _toComment == null ? null : _toComment.id,
  84. user: _toUser,
  85. callback: () {
  86. _toComment = null;
  87. _toUser = null;
  88. model.initData();
  89. widget.comment.commentCount += 1;
  90. },
  91. )
  92. ],
  93. );
  94. }
  95. @override
  96. CommentSubListModel createModel() => CommentSubListModel(widget.comment);
  97. Widget _buildCommentListWidget(CommentSubListModel subModel, Comment parent) {
  98. return ProviderWidget<CommentSubListModel>(
  99. model: subModel,
  100. onModelReady: (model) => model.initData(),
  101. builder: (_, model, __) => EasyRefresh.custom(
  102. controller: model.refreshController,
  103. enableControlFinishRefresh: true,
  104. enableControlFinishLoad: true,
  105. onLoad: model.isIdle ? () => model.loadMore() : null,
  106. footer: buildClassicalFooter(),
  107. slivers: <Widget>[
  108. SliverToBoxAdapter(
  109. child: _buildCommentListHeaderWidget(parent),
  110. ),
  111. if (model.isBusy)
  112. SliverToBoxAdapter(
  113. child: RequestLoadingWidget(),
  114. ),
  115. if (model.isEmpty)
  116. SliverToBoxAdapter(
  117. child: RequestErrorWidget(
  118. null,
  119. msg: "暂无评论~",
  120. assets: RequestErrorWidget.ASSETS_NO_COMMENT,
  121. ),
  122. ),
  123. if (model.isIdle)
  124. SliverList(
  125. delegate: SliverChildBuilderDelegate(
  126. (context, index) {
  127. return _buildCommentListItemWidget(model.list[index]);
  128. },
  129. childCount: model.list.length,
  130. ),
  131. ),
  132. SliverToBoxAdapter(
  133. child: SizedBox(
  134. height: 30,
  135. ),
  136. ),
  137. ]),
  138. );
  139. }
  140. Widget _buildCommentListHeaderWidget(Comment item) {
  141. return Column(
  142. children: <Widget>[
  143. Padding(
  144. padding: EdgeInsets.fromLTRB(12.0, 6.0, 12.0, 0.0),
  145. child: buildCommentWidget(
  146. context,
  147. item,
  148. () {
  149. setState(() {
  150. _toComment = item;
  151. _toUser = null;
  152. _focusNode?.requestFocus();
  153. });
  154. },
  155. selfId == item.userId,
  156. onLiked: () {
  157. setState(() {
  158. item.toggleLike();
  159. });
  160. }),
  161. ),
  162. Divider(
  163. height: 1,
  164. ),
  165. SizedBox(height: 20.0,)
  166. ],
  167. );
  168. }
  169. Widget _buildCommentListItemWidget(Comment item) {
  170. return Padding(
  171. padding: EdgeInsets.fromLTRB(50.0, 0.0, 12.0, 0.0),
  172. child: buildCommentWidget(
  173. context,
  174. item,
  175. () {
  176. setState(() {
  177. _toComment = item;
  178. _toUser = item.socialInfo;
  179. _focusNode?.requestFocus();
  180. });
  181. },
  182. selfId == item.userId,
  183. width: 25,
  184. onLiked: () {
  185. setState(() {
  186. item.toggleLike();
  187. });
  188. }),
  189. );
  190. }
  191. }
  192. Future showCommentList(BuildContext context, Comment currentItem, {bool comment = false}) async {
  193. return await showModalBottomSheet(
  194. context: context,
  195. isScrollControlled: true,
  196. backgroundColor: Colors.white,
  197. shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(10))),
  198. builder: (BuildContext context) {
  199. return Container(
  200. height: max(520, MediaQuery.of(context).size.height / 3 * 2),
  201. padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), // !important
  202. child: CommentDialog(currentItem.subjectId, currentItem, showInput: comment),
  203. );
  204. },
  205. );
  206. }
  207. Widget buildCommentWidget(BuildContext context, Comment item, Function onTap, bool isSelf, {int width = 30, Function onLiked}) {
  208. return Column(
  209. crossAxisAlignment: CrossAxisAlignment.start,
  210. children: <Widget>[
  211. item.socialInfo == null
  212. ? Container()
  213. : Row(
  214. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  215. children: <Widget>[
  216. GestureDetector(
  217. behavior: HitTestBehavior.opaque,
  218. onTap: () => NavigatorUtil.goSocialUserDetail(context, item.socialInfo),
  219. child: Row(
  220. children: <Widget>[
  221. CircleAvatar(backgroundImage: userAvatarProvider(item.socialInfo?.avatar), radius: width / 2),
  222. Space(
  223. width: 8,
  224. ),
  225. Text(
  226. item.socialInfo?.name,
  227. style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600),
  228. ),
  229. ],
  230. )),
  231. GestureDetector(
  232. behavior: HitTestBehavior.opaque,
  233. onTap: () async {
  234. await inject<RestClient>().postForumLike(item.id, "comment_id");
  235. onLiked?.call();
  236. },
  237. child: Row(
  238. children: <Widget>[
  239. Image.asset("lib/assets/img/bbslist_icon_like${item.isLiked ?? false ? 'd' : ''}.png"),
  240. _padding,
  241. Text(item.likeCount == 0 ? "" : "${item.likeCount}", style: Theme.of(context).textTheme.bodyText1, strutStyle: fixedLine),
  242. ],
  243. ),
  244. ),
  245. ],
  246. ),
  247. Padding(
  248. padding: EdgeInsets.fromLTRB(width + 8.0, 6.0, 12.0, 20.0),
  249. child: Column(
  250. crossAxisAlignment: CrossAxisAlignment.start,
  251. children: <Widget>[
  252. GestureDetector(
  253. onTap: onTap,
  254. onLongPress: () async {
  255. await showActionDialog(context, {
  256. if (isSelf == false)
  257. "举报评论": () {
  258. request(context, () async {
  259. await inject<RestClient>().postForumReport(commentId: item.id).catchError((onError) {});
  260. ToastUtil.show("举报成功");
  261. });
  262. },
  263. if (isSelf == false)
  264. "屏蔽此条": () {
  265. request(context, () async {
  266. await inject<RestClient>().postForumBlockObject(item.id, "comment").catchError((onError) {});
  267. ToastUtil.show("屏蔽成功");
  268. CommentNotification(item, CommentNotification.TYPE_DEL).dispatch(context);
  269. });
  270. },
  271. if (isSelf == true)
  272. "删除此条": () async {
  273. if (await showDialog(
  274. context: context,
  275. builder: (context) => CustomAlertDialog(title: '确定删除此条?', ok: () => Navigator.of(context).pop(true)),
  276. ) ==
  277. true) {
  278. request(context, () async {
  279. await inject<RestClient>().postDelComment(item.id);
  280. ToastUtil.show("删除成功");
  281. CommentNotification(item, CommentNotification.TYPE_DEL).dispatch(context);
  282. });
  283. }
  284. },
  285. "复制文字": () {
  286. Clipboard.setData(ClipboardData(text: item.content));
  287. ToastUtil.show("已复制到粘贴板");
  288. },
  289. });
  290. },
  291. child: Padding(
  292. padding: const EdgeInsets.only(bottom: 8.0),
  293. child: RichText(
  294. text: TextSpan(style: DefaultTextStyle.of(context).style, children: <InlineSpan>[
  295. if (item.toUserName?.isNotEmpty == true)
  296. TextSpan(
  297. text: '@${item.toUserName} ',
  298. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  299. // recognizer: TapGestureRecognizer()..onTap = () {
  300. // print("111111111111");
  301. // setState(() {
  302. // _toUser = PostUser(id: item.toUserId, name: item.toUserName);
  303. // });
  304. // }
  305. ),
  306. TextSpan(text: item.content, style: Theme.of(context).textTheme.subtitle1),
  307. ]),
  308. )),
  309. ),
  310. Row(
  311. children: <Widget>[
  312. Text(DateFormat.formatTime(item.createTime), style: Theme.of(context).textTheme.bodyText1, strutStyle: fixedLine),
  313. dot,
  314. InkWell(
  315. onTap: onTap,
  316. child: Text("回复", style: Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 12.0), strutStyle: fixedLine),
  317. )
  318. ],
  319. )
  320. ],
  321. ),
  322. ),
  323. ],
  324. );
  325. }