03-02-1403 / Rhmn / edit mention section.

This commit is contained in:
OkaykOrhmn 2024-05-23 12:34:38 +03:30
parent 4c369c8588
commit f4689f3391
10 changed files with 265 additions and 759 deletions

View File

@ -6,12 +6,9 @@ class CommentData {
int id; int id;
final String text; final String text;
final String createdAt; final String createdAt;
String? type;
bool liked; bool liked;
bool disliked; bool disliked;
bool private;
int status; int status;
dynamic mention;
final FeedbackData feedback; final FeedbackData feedback;
final UserOverview user; final UserOverview user;
final List<Reply> replies; final List<Reply> replies;
@ -22,21 +19,17 @@ class CommentData {
required this.createdAt, required this.createdAt,
required this.liked, required this.liked,
required this.disliked, required this.disliked,
required this.private,
required this.feedback, required this.feedback,
required this.user, required this.user,
required this.replies, required this.replies,
required this.status, required this.status,
this.type,
required this.mention,
}); });
factory CommentData.fromJson(Map<String, dynamic> json, bool private) => CommentData( factory CommentData.fromJson(Map<String, dynamic> json) => CommentData(
id: json['id'], id: json['id'],
text: json['text'], text: json['text'],
createdAt: json['createdAt'], createdAt: json['createdAt'],
liked: json['liked'], liked: json['liked'],
private: private,
disliked: json['disliked'], disliked: json['disliked'],
feedback: FeedbackData.fromJson(json['feedback']), feedback: FeedbackData.fromJson(json['feedback']),
user: UserOverview.fromJson(json['user']), user: UserOverview.fromJson(json['user']),
@ -46,19 +39,14 @@ class CommentData {
), ),
), ),
status: json['status'], status: json['status'],
mention: json['mention'],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'text': text, 'text': text,
'createdAt': createdAt, 'createdAt': createdAt,
'liked': liked, 'liked': liked,
'disliked': disliked, 'disliked': disliked,
'private': private,
'mention': mention,
'type': type,
'feedback': feedback.toJson(), 'feedback': feedback.toJson(),
'user': user.toJson(), 'user': user.toJson(),
'replies': replies.map((e) => e.toJson()).toList(), 'replies': replies.map((e) => e.toJson()).toList(),

View File

@ -1,9 +1,9 @@
class MentionData { class MentionData {
final int id; int id;
final String fullName; String fullName;
final String text; String text;
final String createdAt; String createdAt;
final List<String> mentions; List<String> mentions;
MentionData( MentionData(
{required this.id, {required this.id,
@ -12,8 +12,7 @@ class MentionData {
required this.fullName, required this.fullName,
required this.mentions}); required this.mentions});
factory MentionData.fromJson(Map<String, dynamic> json, bool private) => factory MentionData.fromJson(Map<String, dynamic> json) => MentionData(
MentionData(
id: json['id'], id: json['id'],
text: json['text'], text: json['text'],
createdAt: json['createdAt'], createdAt: json['createdAt'],

View File

@ -1,17 +1,16 @@
class UsersMention { class UsersMention {
int? id; int id;
String? name; String name;
String? type; String? type;
String? photo; String? photo;
UsersMention({this.id, this.name, this.type, this.photo}); UsersMention({required this.id, required this.name, this.type, this.photo});
UsersMention.fromJson(Map<String, dynamic> json) { factory UsersMention.fromJson(Map<String, dynamic> json) => UsersMention(
id = json['id']; id: json['id'],
name = json['name']; name: json['name'],
type = json['type']; type: json['type'],
photo = json['photo']; photo: json['photo']);
}
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
@ -21,4 +20,4 @@ class UsersMention {
data['photo'] = photo; data['photo'] = photo;
return data; return data;
} }
} }

View File

@ -1,15 +1,11 @@
import 'dart:ui';
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/views/comments/comments_state.dart'; import 'package:didvan/views/comments/comments_state.dart';
import 'package:didvan/views/comments/widgets/comment.dart'; import 'package:didvan/views/comments/widgets/comment.dart';
import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/checkbox.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
@ -19,12 +15,8 @@ import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../widgets/didvan/didvan_title_divider.dart';
import '../widgets/user_mention.dart';
class Comments extends StatefulWidget { class Comments extends StatefulWidget {
final Map<String, dynamic> pageData; final Map<String, dynamic> pageData;
const Comments({ const Comments({
Key? key, Key? key,
required this.pageData, required this.pageData,
@ -55,8 +47,6 @@ class _CommentsState extends State<Comments> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final commentsState = context.watch<CommentsState>();
final bottomViewInset = MediaQuery.of(context).viewInsets.bottom; final bottomViewInset = MediaQuery.of(context).viewInsets.bottom;
if (bottomViewInset == 0) { if (bottomViewInset == 0) {
if (_bottomPadding != 0) { if (_bottomPadding != 0) {
@ -80,7 +70,6 @@ class _CommentsState extends State<Comments> {
) )
: null, : null,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 92), padding: const EdgeInsets.only(left: 16, right: 16, bottom: 92),
showSliversFirst: false,
slivers: [ slivers: [
Consumer<CommentsState>( Consumer<CommentsState>(
builder: (context, state, child) => builder: (context, state, child) =>
@ -91,8 +80,7 @@ class _CommentsState extends State<Comments> {
childCount: state.comments.length, childCount: state.comments.length,
placeholder: const _CommentPlaceholder(), placeholder: const _CommentPlaceholder(),
centerEmptyState: _isPage, centerEmptyState: _isPage,
enableEmptyState: enableEmptyState: state.comments.isEmpty,
state.comments.isEmpty && state.privateComments.isEmpty,
emptyState: EmptyState( emptyState: EmptyState(
asset: Assets.emptyChat, asset: Assets.emptyChat,
title: 'اولین نظر را بنویسید...', title: 'اولین نظر را بنویسید...',
@ -108,84 +96,6 @@ class _CommentsState extends State<Comments> {
), ),
), ),
], ],
children: commentsState.privateComments.isNotEmpty
? [
const SizedBox(
height: 6,
),
DidvanTitleDivider(
title: "نظرات پنهان شده",
color: Theme.of(context).colorScheme.primary,
icon: Icon(
commentsState.showPrivates
? DidvanIcons.angle_up_regular
: DidvanIcons.angle_down_regular,
color: Theme.of(context).colorScheme.primary,
),
onClick: () {
commentsState.showPrivates =
!commentsState.showPrivates;
commentsState.update();
},
),
const SizedBox(
height: 16,
),
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: commentsState.showPrivates,
child: _buildPrivateComments(commentsState),
),
const SizedBox(
height: 16,
),
const DidvanTitleDivider(
title: "سایر نظرات",
),
]
: null,
),
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: commentsState.showUsersForMentionsLayout,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Container(
decoration: BoxDecoration(
color:
Theme.of(context).colorScheme.focused.withOpacity(0.5)),
child: DidvanScaffold(
appBarData: null,
padding: const EdgeInsets.only(
left: 16, right: 16, top: 16, bottom: 92),
backgroundColor: Colors.white.withOpacity(0.0),
slivers: [
Consumer<CommentsState>(
builder: (context, state, child) =>
SliverStateHandler<CommentsState>(
onRetry: state.getUsersMention,
state: state,
itemPadding: const EdgeInsets.symmetric(vertical: 8),
childCount: state.usersMention.length,
placeholder: const _UsersPlaceholder(),
centerEmptyState: _isPage,
enableEmptyState: state.usersMention.isEmpty,
emptyState: EmptyState(
asset: Assets.emptyBookmark,
title: 'لیست خالی است',
),
builder: (context, state, index) {
return UserMention(
user: state.usersMention[index],
index: index,
);
},
),
),
],
),
),
),
), ),
Positioned( Positioned(
left: 0, left: 0,
@ -197,28 +107,10 @@ class _CommentsState extends State<Comments> {
), ),
); );
} }
ListView _buildPrivateComments(CommentsState commentsState) {
return ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: commentsState.privateComments.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Comment(
key: ValueKey(
commentsState.privateComments[index].id.toString() +
commentsState.privateComments[index].text,
),
focusNode: _focusNode,
comment: commentsState.privateComments[index],
);
});
}
} }
class _MessageBox extends StatefulWidget { class _MessageBox extends StatefulWidget {
final FocusNode focusNode; final FocusNode focusNode;
const _MessageBox({Key? key, required this.focusNode}) : super(key: key); const _MessageBox({Key? key, required this.focusNode}) : super(key: key);
@override @override
@ -226,179 +118,59 @@ class _MessageBox extends StatefulWidget {
} }
class _MessageBoxState extends State<_MessageBox> { class _MessageBoxState extends State<_MessageBox> {
final _controller = TextEditingController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.watch<CommentsState>(); final state = context.watch<CommentsState>();
void onCheckBoxChange(bool b) {
state.hideMentionedUser = b;
state.update();
}
return Column( return Column(
children: [ children: [
AnimatedVisibility( AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
isVisible: state.showReplyBox, isVisible: state.showReplyBox,
child: Column( child: Container(
children: [ padding: const EdgeInsets.all(8),
Container( decoration: BoxDecoration(
padding: const EdgeInsets.all(8), color: Theme.of(context).colorScheme.surface,
decoration: BoxDecoration( border: Border(
color: Theme.of(context).colorScheme.secondCTA, top: BorderSide(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(16),
topLeft: Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Theme.of(context)
.colorScheme
.title
.withOpacity(0.2),
offset: const Offset(
5.0,
5.0,
),
blurRadius: 10.0,
spreadRadius: 2.0,
)
]
// border: Border(
// top: BorderSide(
// color: Theme.of(context).colorScheme.border,
// ),
// ),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (state.replyingTo != null)
DidvanText(
'پاسخ به ${state.replyingTo!.fullName}:',
color: Theme.of(context).colorScheme.primary,
style: Theme.of(context).textTheme.bodySmall,
),
const Spacer(),
DidvanIconButton(
gestureSize: 24,
color: Theme.of(context).colorScheme.primary,
icon: DidvanIcons.close_regular,
onPressed: () {
state.commentId = null;
state.replyingTo = null;
state.showReplyBox = false;
state.update();
},
),
],
),
),
Container(
color: Theme.of(context).colorScheme.secondCTA,
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Divider(
height: 2,
color: Theme.of(context).colorScheme.border, color: Theme.of(context).colorScheme.border,
), ),
)
],
),
),
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: state.usersMentioned.name != null,
child: Column(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondCTA,
borderRadius: !state.showReplyBox
? const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16))
: null),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
color: Theme.of(context).colorScheme.primary,
width: 2,
height: 40,
),
const SizedBox(
width: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
"اشاره به",
color: Theme.of(context).colorScheme.text,
style: Theme.of(context).textTheme.labelSmall,
),
DidvanText(
state.usersMentioned.name.toString(),
color: Theme.of(context).colorScheme.text,
style: Theme.of(context).textTheme.bodySmall,
),
],
)
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
DidvanIconButton(
gestureSize: 24,
color: Theme.of(context).colorScheme.primary,
icon: DidvanIcons.close_regular,
onPressed: () {
state.usersMentioned = UsersMention();
state.update();
},
),
!state.showReplyBox
? DidvanCheckbox(
title: "پنهان کردن فراخوانی",
value: state.hideMentionedUser,
color: Theme.of(context).colorScheme.primary,
onChanged: onCheckBoxChange,
size: 12,
)
: const SizedBox(),
],
)
],
),
), ),
], ),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (state.replyingTo != null)
DidvanText(
'پاسخ به ${state.replyingTo!.fullName}:',
color: Theme.of(context).colorScheme.caption,
style: Theme.of(context).textTheme.bodySmall,
),
const Spacer(),
DidvanIconButton(
gestureSize: 24,
color: Theme.of(context).colorScheme.caption,
icon: DidvanIcons.close_regular,
onPressed: () {
state.commentId = null;
state.replyingTo = null;
state.showReplyBox = false;
state.update();
},
),
],
),
), ),
), ),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
boxShadow: state.showReplyBox && state.usersMentioned.name == null color: Theme.of(context).colorScheme.surface,
? null border: Border(
: [ top: BorderSide(
BoxShadow( color: Theme.of(context).colorScheme.border,
color: ),
Theme.of(context).colorScheme.title.withOpacity(0.2), ),
offset: const Offset(
5.0,
5.0,
),
blurRadius: 10.0,
spreadRadius: 2.0,
)
],
color: state.showReplyBox && state.usersMentioned.name == null
? Theme.of(context).colorScheme.secondCTA
: Theme.of(context).colorScheme.surface,
), ),
child: Row( child: Row(
children: [ children: [
@ -411,7 +183,7 @@ class _MessageBoxState extends State<_MessageBox> {
Expanded( Expanded(
child: TextField( child: TextField(
focusNode: widget.focusNode, focusNode: widget.focusNode,
controller: state.commentTextFieldController, controller: _controller,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
@ -423,7 +195,7 @@ class _MessageBoxState extends State<_MessageBox> {
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.disabledText), color: Theme.of(context).colorScheme.disabledText),
), ),
onChanged: (value) => _onChange(state, value), onChanged: (value) => state.text = value,
), ),
), ),
], ],
@ -434,33 +206,9 @@ class _MessageBoxState extends State<_MessageBox> {
} }
void _onSend(CommentsState state) { void _onSend(CommentsState state) {
if (state.commentTextFieldController.text.replaceAll(' ', '').isNotEmpty) { if (state.text.replaceAll(' ', '').isNotEmpty) {
state.addComment(); state.addComment();
state.commentTextFieldController.text = ''; _controller.text = '';
}
}
void _onChange(CommentsState state, value) {
state.commentTextFieldController.text = value;
if (state.usersMentioned.name == null) {
if (state.commentTextFieldController.text.contains("@")) {
int index = state.commentTextFieldController.text.indexOf("@");
if (state.commentTextFieldController.text.length > index + 1) {
if (state.commentTextFieldController.text
.substring(index)
.contains(" ")) {
state.showUsersForMentionsLayout = false;
} else {
state.mentionedText =
state.commentTextFieldController.text.substring(index);
state.getUsersMention();
state.showUsersForMentionsLayout = true;
}
}
} else {
state.showUsersForMentionsLayout = false;
}
state.update();
} }
} }
} }
@ -528,28 +276,3 @@ class _CommentPlaceholder extends StatelessWidget {
); );
} }
} }
class _UsersPlaceholder extends StatelessWidget {
const _UsersPlaceholder({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerPlaceholder(
height: 40,
width: 40,
borderRadius: DesignConfig.highBorderRadius,
),
SizedBox(width: 16),
Expanded(
child: ShimmerPlaceholder(
borderRadius: DesignConfig.highBorderRadius,
height: 40,
),
),
],
);
}
}

View File

@ -4,33 +4,24 @@ import 'package:didvan/models/comment/feedback.dart';
import 'package:didvan/models/comment/reply.dart'; import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/comment/user.dart'; import 'package:didvan/models/comment/user.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/providers/user.dart'; import 'package:didvan/providers/user.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class CommentsState extends CoreProvier { class CommentsState extends CoreProvier {
TextEditingController commentTextFieldController = TextEditingController(); String text = '';
UsersMention usersMentioned = UsersMention();
String mentionedText = '';
int? commentId; int? commentId;
UserOverview? replyingTo; UserOverview? replyingTo;
bool showReplyBox = false; bool showReplyBox = false;
bool showUsersForMentionsLayout = false;
bool showPrivates = false;
bool hideMentionedUser = false;
late void Function(int count) onCommentsChanged; late void Function(int count) onCommentsChanged;
int _count = 0; int _count = 0;
late String type; late String type;
final List<CommentData> comments = []; final List<CommentData> comments = [];
final List<CommentData> privateComments = [];
final List<UsersMention> usersMention = [];
final Map<int, MapEntry<bool, bool>> _feedbackQueue = {}; final Map<int, MapEntry<bool, bool>> _feedbackQueue = {};
int itemId = 0; int itemId = 0;
@ -42,40 +33,14 @@ class CommentsState extends CoreProvier {
await service.httpGet(); await service.httpGet();
if (service.isSuccess) { if (service.isSuccess) {
comments.clear(); comments.clear();
final messagesPublic = service.result['comments']['public']; final messages = service.result['comments'];
for (var i = 0; i < messagesPublic.length; i++) { for (var i = 0; i < messages.length; i++) {
comments.add(CommentData.fromJson(messagesPublic[i], false)); comments.add(CommentData.fromJson(messages[i]));
_count++; _count++;
for (var j = 0; j < messagesPublic[i]['replies'].length; j++) { for (var j = 0; j < messages[i]['replies'].length; j++) {
_count++; _count++;
} }
} }
final messagesPrivate = service.result['comments']['private'];
for (var i = 0; i < messagesPrivate.length; i++) {
privateComments.add(CommentData.fromJson(messagesPrivate[i], true));
_count++;
for (var j = 0; j < messagesPrivate[i]['replies'].length; j++) {
_count++;
}
}
appState = AppState.idle;
return;
}
appState = AppState.failed;
}
Future<void> getUsersMention() async {
final service = RequestService(
RequestHelper.usersMentions(mentionedText.replaceAll("@", "")),
);
await service.httpGet();
if (service.isSuccess) {
usersMention.clear();
final List<dynamic> users = service.data('users');
usersMention
.addAll(users.map((users) => UsersMention.fromJson(users)).toList());
appState = AppState.idle; appState = AppState.idle;
return; return;
} }
@ -122,14 +87,12 @@ class CommentsState extends CoreProvier {
} }
Future<void> addComment() async { Future<void> addComment() async {
late List<CommentData> cList =
hideMentionedUser ? privateComments : comments;
final user = DesignConfig.context!.read<UserProvider>().user; final user = DesignConfig.context!.read<UserProvider>().user;
if (replyingTo != null) { if (replyingTo != null) {
comments.firstWhere((comment) => comment.id == commentId).replies.add( comments.firstWhere((comment) => comment.id == commentId).replies.add(
Reply( Reply(
id: 0, id: 0,
text: commentTextFieldController.text, text: text,
createdAt: DateTime.now().toString(), createdAt: DateTime.now().toString(),
liked: false, liked: false,
disliked: false, disliked: false,
@ -141,15 +104,14 @@ class CommentsState extends CoreProvier {
photo: user.photo, photo: user.photo,
), ),
status: 2, status: 2,
mention: usersMentioned.name,
), ),
); );
} else { } else {
cList.insert( comments.insert(
0, 0,
CommentData( CommentData(
id: 0, id: 0,
text: commentTextFieldController.text, text: text,
createdAt: DateTime.now().toString(), createdAt: DateTime.now().toString(),
liked: false, liked: false,
disliked: false, disliked: false,
@ -161,8 +123,6 @@ class CommentsState extends CoreProvier {
), ),
replies: [], replies: [],
status: 2, status: 2,
private: hideMentionedUser,
mention: usersMentioned.name,
), ),
); );
} }
@ -178,12 +138,8 @@ class CommentsState extends CoreProvier {
body.addAll({'status': 2}); body.addAll({'status': 2});
showReplyBox = false; showReplyBox = false;
// update(); update();
body.addAll({ body.addAll({'text': text});
'text': commentTextFieldController.text,
"mention": usersMentioned.id,
"private": hideMentionedUser
});
final service = RequestService( final service = RequestService(
RequestHelper.addComment(itemId, type), RequestHelper.addComment(itemId, type),
body: body, body: body,
@ -192,20 +148,17 @@ class CommentsState extends CoreProvier {
await service.post(); await service.post();
if (service.isSuccess) { if (service.isSuccess) {
if (replyingTo != null) { if (replyingTo != null) {
cList comments
.firstWhere((comment) => comment.id == commentId) .firstWhere((comment) => comment.id == commentId)
.replies .replies
.firstWhere((reply) => reply.id == 0) .firstWhere((reply) => reply.id == 0)
.id = service.result['comment']['id']; .id = service.result['comment']['id'];
} else { } else {
cList.firstWhere((comment) => comment.id == 0).id = comments.firstWhere((comment) => comment.id == 0).id =
service.result['comment']['id']; service.result['comment']['id'];
} }
commentId = null; commentId = null;
replyingTo = null; replyingTo = null;
usersMentioned = UsersMention();
mentionedText = '';
update();
} }
} }

View File

@ -20,7 +20,6 @@ import 'package:provider/provider.dart';
class Comment extends StatefulWidget { class Comment extends StatefulWidget {
final FocusNode focusNode; final FocusNode focusNode;
final CommentData comment; final CommentData comment;
const Comment({ const Comment({
Key? key, Key? key,
required this.focusNode, required this.focusNode,
@ -110,13 +109,6 @@ class CommentState extends State<Comment> {
), ),
], ],
), ),
comment.mention != null
? DidvanText(
comment.mention.toString(),
color: Theme.of(context).colorScheme.primary,
style: Theme.of(context).textTheme.titleSmall,
)
: const SizedBox(),
const SizedBox(height: 8), const SizedBox(height: 8),
if (isReply) if (isReply)
DidvanText( DidvanText(
@ -132,22 +124,19 @@ class CommentState extends State<Comment> {
children: [ children: [
Icon( Icon(
Icons.circle, Icons.circle,
color: color: Theme.of(context)
Theme.of(context).colorScheme.secondaryDisabled, .colorScheme
.secondary
.withOpacity(0.3),
size: 18, size: 18,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
DidvanText( DidvanText(
'در انتظار تایید', 'در انتظار تایید',
style: Theme.of(context) color: Theme.of(context)
.textTheme .colorScheme
.titleSmall! .secondary
.copyWith( .withOpacity(0.3),
color: Theme.of(context)
.colorScheme
.secondaryDisabled),
color:
Theme.of(context).colorScheme.secondaryDisabled,
), ),
], ],
), ),
@ -265,7 +254,6 @@ class _FeedbackButtons extends StatefulWidget {
final bool dislikeValue; final bool dislikeValue;
final void Function(bool like, bool dislike, int likeCount, int dislikeCount) final void Function(bool like, bool dislike, int likeCount, int dislikeCount)
onFeedback; onFeedback;
const _FeedbackButtons({ const _FeedbackButtons({
Key? key, Key? key,
required this.onFeedback, required this.onFeedback,

View File

@ -4,10 +4,9 @@ import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/views/comments/widgets/comment.dart';
import 'package:didvan/views/mentions/mentions_state.dart'; import 'package:didvan/views/mentions/mentions_state.dart';
import 'package:didvan/views/mentions/widgets/mention.dart';
import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/checkbox.dart'; import 'package:didvan/views/widgets/didvan/checkbox.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
@ -18,8 +17,7 @@ import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../comments/comments_state.dart';
import '../widgets/didvan/didvan_title_divider.dart';
import '../widgets/user_mention.dart'; import '../widgets/user_mention.dart';
class Mentions extends StatefulWidget { class Mentions extends StatefulWidget {
@ -54,6 +52,12 @@ class _MentionsState extends State<Mentions> {
bool get _isPage => widget.pageData['isPage'] != false; bool get _isPage => widget.pageData['isPage'] != false;
void _onChange(MentionsState state, value) {
state.searchUsers = value;
state.getUsersMention();
state.update();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final commentsState = context.watch<MentionsState>(); final commentsState = context.watch<MentionsState>();
@ -92,12 +96,11 @@ class _MentionsState extends State<Mentions> {
childCount: state.comments.length, childCount: state.comments.length,
placeholder: const _CommentPlaceholder(), placeholder: const _CommentPlaceholder(),
centerEmptyState: _isPage, centerEmptyState: _isPage,
enableEmptyState: enableEmptyState: state.comments.isEmpty,
state.comments.isEmpty && state.privateComments.isEmpty,
emptyState: EmptyState( emptyState: EmptyState(
asset: Assets.emptyChat, asset: Assets.emptyChat,
title: 'دوستان خود را فراخوانی کنید'), title: 'دوستان خود را فراخوانی کنید'),
builder: (context, state, index) => Comment( builder: (context, state, index) => Mention(
key: ValueKey( key: ValueKey(
state.comments[index].id.toString() + state.comments[index].id.toString() +
state.comments[index].text, state.comments[index].text,
@ -108,42 +111,6 @@ class _MentionsState extends State<Mentions> {
), ),
), ),
], ],
children: commentsState.privateComments.isNotEmpty
? [
const SizedBox(
height: 6,
),
DidvanTitleDivider(
title: "نظرات پنهان شده",
color: Theme.of(context).colorScheme.primary,
icon: Icon(
commentsState.showPrivates
? DidvanIcons.angle_up_regular
: DidvanIcons.angle_down_regular,
color: Theme.of(context).colorScheme.primary,
),
onClick: () {
commentsState.showPrivates =
!commentsState.showPrivates;
commentsState.update();
},
),
const SizedBox(
height: 16,
),
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: commentsState.showPrivates,
child: _buildPrivateComments(commentsState),
),
const SizedBox(
height: 16,
),
const DidvanTitleDivider(
title: "سایر نظرات",
),
]
: null,
), ),
AnimatedVisibility( AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
@ -159,30 +126,73 @@ class _MentionsState extends State<Mentions> {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 16, right: 16, top: 16, bottom: 92), left: 16, right: 16, top: 16, bottom: 92),
backgroundColor: Colors.white.withOpacity(0.0), backgroundColor: Colors.white.withOpacity(0.0),
showSliversFirst: false,
slivers: [ slivers: [
Consumer<MentionsState>( Consumer<MentionsState>(
builder: (context, state, child) => builder: (context, state, child) =>
SliverStateHandler<MentionsState>( SliverStateHandler<MentionsState>(
onRetry: state.getUsersMention, onRetry: state.getUsersMention,
state: state, state: state,
itemPadding: const EdgeInsets.symmetric(vertical: 8), childCount: state.users.length,
childCount: state.usersMention.length,
placeholder: const _UsersPlaceholder(), placeholder: const _UsersPlaceholder(),
centerEmptyState: _isPage, centerEmptyState: _isPage,
enableEmptyState: state.usersMention.isEmpty, enableEmptyState: state.users.isEmpty,
emptyState: EmptyState( emptyState: EmptyState(
asset: Assets.emptyBookmark, asset: Assets.emptyBookmark,
title: 'لیست خالی است', title: 'لیست خالی است',
), ),
builder: (context, state, index) { builder: (context, state, index) {
return UserMention( return UserMention(
user: state.usersMention[index], user: state.users[index],
index: index, index: index,
); );
}, },
), ),
), ),
], ],
children: [
Row(
children: [
Expanded(
child: TextField(
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.send,
style: Theme.of(context).textTheme.bodyMedium,
onEditingComplete: () {},
onChanged: (val) => _onChange(commentsState, val),
decoration: InputDecoration(
icon: const Icon(DidvanIcons.search_regular),
border: InputBorder.none,
hintText: 'جستجو کنید...',
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(context)
.colorScheme
.disabledText),
),
),
),
DidvanIconButton(
icon: DidvanIcons.close_regular,
onPressed: () {
commentsState.showUsersForMentionsLayout = false;
commentsState.update();
}),
],
),
const SizedBox(
height: 4,
),
Divider(
height: 1,
color: Theme.of(context).colorScheme.splash,
),
const SizedBox(
height: 12,
)
],
), ),
), ),
), ),
@ -197,23 +207,6 @@ class _MentionsState extends State<Mentions> {
), ),
); );
} }
ListView _buildPrivateComments(MentionsState commentsState) {
return ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: commentsState.privateComments.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Comment(
key: ValueKey(
commentsState.privateComments[index].id.toString() +
commentsState.privateComments[index].text,
),
focusNode: _focusNode,
comment: commentsState.privateComments[index],
);
});
}
} }
class _MessageBox extends StatefulWidget { class _MessageBox extends StatefulWidget {
@ -245,31 +238,23 @@ class _MessageBoxState extends State<_MessageBox> {
Container( Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondCTA, color: Theme.of(context).colorScheme.secondCTA,
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topRight: Radius.circular(16), topRight: Radius.circular(16),
topLeft: Radius.circular(16)), topLeft: Radius.circular(16)),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context) color:
.colorScheme Theme.of(context).colorScheme.title.withOpacity(0.2),
.title offset: const Offset(
.withOpacity(0.2), 5.0,
offset: const Offset( 5.0,
5.0, ),
5.0, blurRadius: 10.0,
), spreadRadius: 2.0,
blurRadius: 10.0,
spreadRadius: 2.0,
)
]
// border: Border(
// top: BorderSide(
// color: Theme.of(context).colorScheme.border,
// ),
// ),
), ),
],
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -307,7 +292,7 @@ class _MessageBoxState extends State<_MessageBox> {
), ),
AnimatedVisibility( AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
isVisible: state.usersMentioned.name != null, isVisible: state.mentionedUsers.isNotEmpty,
child: Column( child: Column(
children: [ children: [
Container( Container(
@ -343,7 +328,9 @@ class _MessageBoxState extends State<_MessageBox> {
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(context).textTheme.labelSmall,
), ),
DidvanText( DidvanText(
state.usersMentioned.name.toString(), state.mentionedUsers
.map((user) => user.name)
.join("، "),
color: Theme.of(context).colorScheme.text, color: Theme.of(context).colorScheme.text,
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
@ -359,19 +346,10 @@ class _MessageBoxState extends State<_MessageBox> {
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
icon: DidvanIcons.close_regular, icon: DidvanIcons.close_regular,
onPressed: () { onPressed: () {
state.usersMentioned = UsersMention(); state.mentionedUsers = [];
state.update(); state.update();
}, },
), ),
!state.showReplyBox
? DidvanCheckbox(
title: "پنهان کردن فراخوانی",
value: state.hideMentionedUser,
color: Theme.of(context).colorScheme.primary,
onChanged: onCheckBoxChange,
size: 12,
)
: const SizedBox(),
], ],
) )
], ],
@ -382,23 +360,12 @@ class _MessageBoxState extends State<_MessageBox> {
), ),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
boxShadow: state.showReplyBox && state.usersMentioned.name == null color: Theme.of(context).colorScheme.surface,
? null border: Border(
: [ top: BorderSide(
BoxShadow( color: Theme.of(context).colorScheme.border,
color: ),
Theme.of(context).colorScheme.title.withOpacity(0.2), ),
offset: const Offset(
5.0,
5.0,
),
blurRadius: 10.0,
spreadRadius: 2.0,
)
],
color: state.showReplyBox && state.usersMentioned.name == null
? Theme.of(context).colorScheme.secondCTA
: Theme.of(context).colorScheme.surface,
), ),
child: Row( child: Row(
children: [ children: [
@ -423,9 +390,19 @@ class _MessageBoxState extends State<_MessageBox> {
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.disabledText), color: Theme.of(context).colorScheme.disabledText),
), ),
onChanged: (value) => _onChange(state, value),
), ),
), ),
const Spacer(),
if (!state.showUsersForMentionsLayout)
DidvanIconButton(
onPressed: () {
state.showUsersForMentionsLayout = true;
state.update();
},
icon: DidvanIcons.user_shield_regular,
size: 24,
color: Theme.of(context).colorScheme.focusedBorder,
),
], ],
), ),
), ),
@ -437,30 +414,8 @@ class _MessageBoxState extends State<_MessageBox> {
if (state.commentTextFieldController.text.replaceAll(' ', '').isNotEmpty) { if (state.commentTextFieldController.text.replaceAll(' ', '').isNotEmpty) {
state.addComment(); state.addComment();
state.commentTextFieldController.text = ''; state.commentTextFieldController.text = '';
} state.searchUsers = '';
} state.mentionedUsers.clear();
void _onChange(MentionsState state, value) {
state.commentTextFieldController.text = value;
if (state.usersMentioned.name == null) {
if (state.commentTextFieldController.text.contains("@")) {
int index = state.commentTextFieldController.text.indexOf("@");
if (state.commentTextFieldController.text.length > index + 1) {
if (state.commentTextFieldController.text
.substring(index)
.contains(" ")) {
state.showUsersForMentionsLayout = false;
} else {
state.mentionedText =
state.commentTextFieldController.text.substring(index);
state.getUsersMention();
state.showUsersForMentionsLayout = true;
}
}
} else {
state.showUsersForMentionsLayout = false;
}
state.update();
} }
} }
} }

View File

@ -1,23 +1,19 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/models/comment/comment.dart'; import 'package:didvan/models/mention/mention.dart';
import 'package:didvan/models/comment/feedback.dart';
import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/comment/user.dart'; import 'package:didvan/models/comment/user.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/users_mention.dart'; import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/providers/user.dart'; import 'package:didvan/providers/user.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class MentionsState extends CoreProvier { class MentionsState extends CoreProvier {
TextEditingController commentTextFieldController = TextEditingController(); TextEditingController commentTextFieldController = TextEditingController();
UsersMention usersMentioned = UsersMention(); List<UsersMention> mentionedUsers = [];
String mentionedText = ''; String searchUsers = '';
int? commentId; int? commentId;
UserOverview? replyingTo; UserOverview? replyingTo;
bool showReplyBox = false; bool showReplyBox = false;
@ -27,38 +23,27 @@ class MentionsState extends CoreProvier {
int _count = 0; int _count = 0;
late String type; late String type;
final List<CommentData> comments = []; final List<MentionData> comments = [];
final List<CommentData> privateComments = []; final List<UsersMention> users = [];
final List<UsersMention> usersMention = [];
final Map<int, MapEntry<bool, bool>> _feedbackQueue = {};
int itemId = 0; int itemId = 0;
Future<void> getComments() async { Future<void> getComments() async {
final service = RequestService( final service = RequestService(
RequestHelper.comments(itemId, type), RequestHelper.mention(itemId, type),
); );
await service.httpGet(); await service.httpGet();
if (service.isSuccess) { if (service.isSuccess) {
comments.clear(); comments.clear();
final messagesPublic = service.result['comments']['public']; final messages = service.result['comments'];
for (var i = 0; i < messagesPublic.length; i++) {
comments.add(CommentData.fromJson(messagesPublic[i], false)); for (var i = 0; i < messages.length; i++) {
comments.add(MentionData.fromJson(messages[i]));
_count++; _count++;
for (var j = 0; j < messagesPublic[i]['replies'].length; j++) {
_count++;
}
} }
final messagesPrivate = service.result['comments']['private'];
for (var i = 0; i < messagesPrivate.length; i++) {
privateComments.add(CommentData.fromJson(messagesPrivate[i], true));
_count++;
for (var j = 0; j < messagesPrivate[i]['replies'].length; j++) {
_count++;
}
}
appState = AppState.idle; appState = AppState.idle;
return; return;
} }
appState = AppState.failed; appState = AppState.failed;
@ -66,14 +51,17 @@ class MentionsState extends CoreProvier {
Future<void> getUsersMention() async { Future<void> getUsersMention() async {
final service = RequestService( final service = RequestService(
RequestHelper.usersMentions(mentionedText.replaceAll("@", "")), RequestHelper.usersMentions(searchUsers),
); );
await service.httpGet(); await service.httpGet();
if (service.isSuccess) { if (service.isSuccess) {
usersMention.clear(); users.clear();
final List<dynamic> users = service.data('users'); final List<dynamic> resUsers = service.data('users');
usersMention
.addAll(users.map((users) => UsersMention.fromJson(users)).toList()); users
.addAll(resUsers.map((user) => UsersMention.fromJson(user)).toList());
appState = AppState.idle; appState = AppState.idle;
return; return;
@ -81,169 +69,49 @@ class MentionsState extends CoreProvier {
appState = AppState.failed; appState = AppState.failed;
} }
Future<void> feedback({
required int id,
required bool like,
required bool dislike,
required int likeCount,
required int dislikeCount,
int? replyId,
}) async {
_feedbackQueue.addAll({id: MapEntry(like, dislike)});
dynamic comment;
if (replyId == null) {
comment = comments.firstWhere((comment) => comment.id == id);
} else {
comment = comments
.firstWhere((comment) => comment.id == id)
.replies
.firstWhere((element) => element.id == replyId);
}
if (comment != null) {
comment.feedback.like = likeCount;
comment.feedback.dislike = dislikeCount;
comment.disliked = dislike;
comment.liked = like;
}
Future.delayed(const Duration(milliseconds: 500), () async {
if (!_feedbackQueue.containsKey(id)) return;
final service = RequestService(
RequestHelper.feedback(itemId, id, type),
body: {
'like': _feedbackQueue[id]!.key,
'dislike': _feedbackQueue[id]!.value,
},
);
await service.put();
_feedbackQueue.remove(id);
});
}
Future<void> addComment() async { Future<void> addComment() async {
late List<CommentData> cList = late List<MentionData> cList = comments;
hideMentionedUser ? privateComments : comments;
final user = DesignConfig.context!.read<UserProvider>().user; final user = DesignConfig.context!.read<UserProvider>().user;
if (replyingTo != null) {
comments.firstWhere((comment) => comment.id == commentId).replies.add( cList.insert(
Reply( 0,
id: 0, MentionData(
text: commentTextFieldController.text, id: 0,
createdAt: DateTime.now().toString(), fullName: user.fullName,
liked: false, text: commentTextFieldController.text,
disliked: false, createdAt: DateTime.now().toString(),
feedback: FeedbackData(like: 0, dislike: 0), mentions: mentionedUsers.map((user) => user.name).toList(),
toUser: replyingTo!, ),
user: UserOverview( );
id: user.id,
fullName: user.fullName,
photo: user.photo,
),
status: 2,
mention: usersMentioned.name,
),
);
} else {
cList.insert(
0,
CommentData(
id: 0,
text: commentTextFieldController.text,
createdAt: DateTime.now().toString(),
liked: false,
disliked: false,
feedback: FeedbackData(like: 0, dislike: 0),
user: UserOverview(
id: user.id,
fullName: user.fullName,
photo: user.photo,
),
replies: [],
status: 2,
private: hideMentionedUser,
mention: usersMentioned.name,
),
);
}
final body = {}; final body = {};
if (commentId != null) {
body.addAll({'commentId': commentId});
}
if (replyingTo != null) {
body.addAll({'replyUserId': replyingTo!.id});
}
body.addAll({'status': 2});
showReplyBox = false;
// update();
body.addAll({ body.addAll({
'text': commentTextFieldController.text, 'text': commentTextFieldController.text,
"mention": usersMentioned.id, "mentions": mentionedUsers.map((user) => user.id.toString()).toList(),
"private": hideMentionedUser
}); });
final service = RequestService( final service = RequestService(
RequestHelper.addComment(itemId, type), RequestHelper.mention(itemId, type),
body: body, body: body,
); );
await service.post(); await service.post();
if (service.isSuccess) { if (service.isSuccess) {
if (replyingTo != null) { cList.firstWhere((comment) => comment.id == 0).id =
cList service.result['comment']['id'];
.firstWhere((comment) => comment.id == commentId)
.replies mentionedUsers = [];
.firstWhere((reply) => reply.id == 0) searchUsers = '';
.id = service.result['comment']['id'];
} else {
cList.firstWhere((comment) => comment.id == 0).id =
service.result['comment']['id'];
}
commentId = null;
replyingTo = null;
usersMentioned = UsersMention();
mentionedText = '';
update(); update();
} }
} }
void reportComment(int id) {
final service = RequestService(RequestHelper.reportComment(id));
service.post();
ActionSheetUtils.showAlert(
AlertData(
message: 'گزارش شما با موفقیت ثبت شد و به زودی بررسی میگردد.',
aLertType: ALertType.success,
),
);
}
void deleteComment(int id, int status, int? rootId) async { void deleteComment(int id, int status, int? rootId) async {
final service = RequestService(RequestHelper.deleteComment(id)); final service = RequestService(RequestHelper.deleteComment(id));
service.delete(); await service.delete();
if (rootId == null) {
final comment = comments.firstWhere((element) => element.id == id);
if (comment.replies.isNotEmpty) {
_count = 0;
await getComments();
} else {
comments.remove(comment);
if (status != 2) { if (service.isSuccess) await getComments();
_count--;
}
}
} else {
comments
.firstWhere((element) => element.id == rootId)
.replies
.removeWhere((element) => element.id == id);
if (status != 2) {
_count--;
}
}
notifyListeners(); notifyListeners();
} }

View File

@ -1,17 +1,14 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/comment/comment.dart';
import 'package:didvan/models/comment/reply.dart'; import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/mention/mention.dart'; import 'package:didvan/models/mention/mention.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/providers/user.dart'; import 'package:didvan/providers/user.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart'; import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/comments/comments_state.dart';
import 'package:didvan/views/mentions/mentions_state.dart'; import 'package:didvan/views/mentions/mentions_state.dart';
import 'package:didvan/views/widgets/menu_item.dart'; import 'package:didvan/views/widgets/menu_item.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';

View File

@ -1,4 +1,7 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/views/mentions/mentions_state.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart'; import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -6,7 +9,6 @@ import 'package:provider/provider.dart';
import '../../constants/app_icons.dart'; import '../../constants/app_icons.dart';
import '../../models/users_mention.dart'; import '../../models/users_mention.dart';
import '../comments/comments_state.dart';
import 'didvan/text.dart'; import 'didvan/text.dart';
class UserMention extends StatefulWidget { class UserMention extends StatefulWidget {
@ -26,28 +28,62 @@ class UserMention extends StatefulWidget {
class _UserMentionState extends State<UserMention> { class _UserMentionState extends State<UserMention> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.watch<CommentsState>(); final state = context.watch<MentionsState>();
final userFound = state.mentionedUsers.contains(widget.user);
return InkWrapper( return InkWrapper(
onPressed: () { onPressed: () {
state.usersMentioned = widget.user; if (userFound) {
state.commentTextFieldController.text = state state.mentionedUsers.remove(widget.user);
.commentTextFieldController.text } else {
.replaceAll(state.mentionedText.toString(), "") state.mentionedUsers.add(widget.user);
.replaceAll("@", ""); }
state.showUsersForMentionsLayout = false;
state.update(); state.update();
}, },
child: Column( child: Column(
children: [ children: [
const SizedBox(
height: 8,
),
Row( Row(
children: [ children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(6),
border: Border.all(
width: 1.0,
color: userFound
? Theme.of(context).colorScheme.checkFav
: Theme.of(context).colorScheme.text),
),
margin: const EdgeInsets.all(8),
child: AnimatedVisibility(
isVisible: userFound,
duration: DesignConfig.mediumAnimationDuration,
child: Center(
child: Icon(
Icons.check,
size: 12,
color: userFound
? Theme.of(context).colorScheme.checkFav
: Theme.of(context).colorScheme.text,
),
),
),
),
widget.user.photo == null widget.user.photo == null
? const Icon(DidvanIcons.avatar_light,size: 40,) ? const Icon(
DidvanIcons.avatar_light,
size: 40,
)
: SkeletonImage( : SkeletonImage(
imageUrl: widget.user.photo.toString(), imageUrl: widget.user.photo.toString(),
height: 40, height: 36,
width: 40, width: 36,
borderRadius: BorderRadius.circular(360), borderRadius: BorderRadius.circular(360),
), ),
const SizedBox( const SizedBox(
@ -62,7 +98,7 @@ class _UserMentionState extends State<UserMention> {
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),
widget.index == state.usersMention.length - 1 widget.index == state.users.length - 1
? const SizedBox() ? const SizedBox()
: Divider( : Divider(
height: 2, height: 2,