feat: option to delete reactions

This commit is contained in:
Freek van de Ven 2023-12-06 07:01:40 +01:00
parent e523f52118
commit 7e89ba9c85
5 changed files with 115 additions and 51 deletions

View file

@ -28,7 +28,11 @@ class TimelineUserStoryConfiguration {
final Widget Function(BuildContext context, Widget filterBar, Widget child)?
mainPageBuilder;
final Widget Function(BuildContext context, Widget child, TimelineCategory category)? postScreenBuilder;
final Widget Function(
BuildContext context,
Widget child,
TimelineCategory category,
)? postScreenBuilder;
final Widget Function(BuildContext context, Widget child)?
postCreationScreenBuilder;

View file

@ -60,6 +60,30 @@ class FirebaseTimelineService with ChangeNotifier implements TimelineService {
notifyListeners();
}
@override
Future<TimelinePost> deletePostReaction(
TimelinePost post,
String reactionId,
) async {
var updatedPost = post.copyWith(
reaction: post.reaction - 1,
reactions: (post.reactions ?? [])
..removeWhere((element) => element.id == reactionId),
);
_posts = _posts
.map(
(p) => p.id == post.id ? updatedPost : p,
)
.toList();
var postRef = _db.collection(_options.timelineCollectionName).doc(post.id);
await postRef.update({
'reaction': FieldValue.increment(-1),
'reactions': FieldValue.arrayRemove([reactionId]),
});
notifyListeners();
return updatedPost;
}
@override
Future<TimelinePost> fetchPostDetails(TimelinePost post) async {
var reactions = post.reactions ?? [];

View file

@ -10,6 +10,7 @@ import 'package:flutter_timeline_interface/src/model/timeline_reaction.dart';
abstract class TimelineService with ChangeNotifier {
Future<void> deletePost(TimelinePost post);
Future<TimelinePost> deletePostReaction(TimelinePost post, String reactionId);
Future<TimelinePost> createPost(TimelinePost post);
Future<List<TimelinePost>> fetchPosts(String? category);
Future<TimelinePost> fetchPost(TimelinePost post);

View file

@ -20,6 +20,7 @@ class TimelineTranslations {
'Indicate whether people are allowed to respond',
this.checkPost = 'Check post overview',
this.deletePost = 'Delete post',
this.deleteReaction = 'Delete Reaction',
this.viewPost = 'View post',
this.likesTitle = 'Likes',
this.commentsTitle = 'Comments',
@ -45,6 +46,7 @@ class TimelineTranslations {
final String postAt;
final String deletePost;
final String deleteReaction;
final String viewPost;
final String likesTitle;
final String commentsTitle;

View file

@ -312,66 +312,99 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
for (var reaction
in post.reactions ?? <TimelinePostReaction>[]) ...[
const SizedBox(height: 16),
Row(
crossAxisAlignment: reaction.imageUrl != null
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [
if (reaction.creator?.imageUrl != null &&
reaction.creator!.imageUrl!.isNotEmpty) ...[
widget.options.userAvatarBuilder?.call(
reaction.creator!,
25,
) ??
CircleAvatar(
radius: 20,
backgroundImage: CachedNetworkImageProvider(
reaction.creator!.imageUrl!,
GestureDetector(
onLongPress: () async {
if (reaction.creatorId == widget.userId ||
widget.options.allowAllDeletion) {
// Show popup menu for deletion
var value = await showMenu<String>(
context: context,
position: const RelativeRect.fromLTRB(
100.0,
200.0,
100.0,
100.0,
),
items: [
PopupMenuItem<String>(
value: 'delete',
child: Text(
widget.options.translations.deleteReaction,
),
),
],
const SizedBox(width: 10),
if (reaction.imageUrl != null) ...[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
reaction.creator?.fullName ??
widget
.options.translations.anonymousUser,
style: theme.textTheme.titleSmall,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: CachedNetworkImage(
imageUrl: reaction.imageUrl!,
fit: BoxFit.fitWidth,
],
);
if (value == 'delete') {
// Call service to delete reaction
updatePost(
await widget.service
.deletePostReaction(post, reaction.id),
);
}
}
},
child: Row(
crossAxisAlignment: reaction.imageUrl != null
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [
if (reaction.creator?.imageUrl != null &&
reaction.creator!.imageUrl!.isNotEmpty) ...[
widget.options.userAvatarBuilder?.call(
reaction.creator!,
25,
) ??
CircleAvatar(
radius: 20,
backgroundImage: CachedNetworkImageProvider(
reaction.creator!.imageUrl!,
),
),
],
),
),
] else ...[
Expanded(
child: Text.rich(
TextSpan(
text: reaction.creator?.fullName ??
widget.options.translations.anonymousUser,
style: theme.textTheme.titleSmall,
],
const SizedBox(width: 10),
if (reaction.imageUrl != null) ...[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TextSpan(text: ' '),
TextSpan(
text: reaction.reaction ?? '',
style: theme.textTheme.bodyMedium,
Text(
reaction.creator?.fullName ??
widget.options.translations
.anonymousUser,
style: theme.textTheme.titleSmall,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: CachedNetworkImage(
imageUrl: reaction.imageUrl!,
fit: BoxFit.fitWidth,
),
),
// text should go to new line
],
),
),
),
] else ...[
Expanded(
child: Text.rich(
TextSpan(
text: reaction.creator?.fullName ??
widget
.options.translations.anonymousUser,
style: theme.textTheme.titleSmall,
children: [
const TextSpan(text: ' '),
TextSpan(
text: reaction.reaction ?? '',
style: theme.textTheme.bodyMedium,
),
// text should go to new line
],
),
),
),
],
],
],
),
),
],
if (post.reactions?.isEmpty ?? true) ...[