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)? final Widget Function(BuildContext context, Widget filterBar, Widget child)?
mainPageBuilder; 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)? final Widget Function(BuildContext context, Widget child)?
postCreationScreenBuilder; postCreationScreenBuilder;

View file

@ -60,6 +60,30 @@ class FirebaseTimelineService with ChangeNotifier implements TimelineService {
notifyListeners(); 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 @override
Future<TimelinePost> fetchPostDetails(TimelinePost post) async { Future<TimelinePost> fetchPostDetails(TimelinePost post) async {
var reactions = post.reactions ?? []; 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 { abstract class TimelineService with ChangeNotifier {
Future<void> deletePost(TimelinePost post); Future<void> deletePost(TimelinePost post);
Future<TimelinePost> deletePostReaction(TimelinePost post, String reactionId);
Future<TimelinePost> createPost(TimelinePost post); Future<TimelinePost> createPost(TimelinePost post);
Future<List<TimelinePost>> fetchPosts(String? category); Future<List<TimelinePost>> fetchPosts(String? category);
Future<TimelinePost> fetchPost(TimelinePost post); Future<TimelinePost> fetchPost(TimelinePost post);

View file

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

View file

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