mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
feat: add timeline reactions
This commit is contained in:
parent
e8822d92a3
commit
4f2aba4cc4
7 changed files with 274 additions and 87 deletions
|
@ -7,7 +7,6 @@ import 'dart:typed_data';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_timeline_firebase/src/config/firebase_timeline_options.dart';
|
import 'package:flutter_timeline_firebase/src/config/firebase_timeline_options.dart';
|
||||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
@ -35,7 +34,8 @@ class FirebaseTimelineService implements TimelineService {
|
||||||
@override
|
@override
|
||||||
Future<TimelinePost> createPost(TimelinePost post) async {
|
Future<TimelinePost> createPost(TimelinePost post) async {
|
||||||
var postId = const Uuid().v4();
|
var postId = const Uuid().v4();
|
||||||
var imageRef = _storage.ref().child('timeline/$postId');
|
var imageRef =
|
||||||
|
_storage.ref().child('${_options.timelineCollectionName}/$postId');
|
||||||
var result = await imageRef.putData(post.image!);
|
var result = await imageRef.putData(post.image!);
|
||||||
var imageUrl = await result.ref.getDownloadURL();
|
var imageUrl = await result.ref.getDownloadURL();
|
||||||
var updatedPost = post.copyWith(imageUrl: imageUrl, id: postId);
|
var updatedPost = post.copyWith(imageUrl: imageUrl, id: postId);
|
||||||
|
@ -54,8 +54,17 @@ class FirebaseTimelineService implements TimelineService {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TimelinePost> fetchPostDetails(TimelinePost post) async {
|
Future<TimelinePost> fetchPostDetails(TimelinePost post) async {
|
||||||
debugPrint('fetchPostDetails');
|
var reactions = post.reactions ?? [];
|
||||||
return post;
|
var updatedReactions = <TimelinePostReaction>[];
|
||||||
|
for (var reaction in reactions) {
|
||||||
|
var user = await _userService.getUser(reaction.creatorId);
|
||||||
|
if (user != null) {
|
||||||
|
updatedReactions.add(reaction.copyWith(creator: user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var updatedPost = post.copyWith(reactions: updatedReactions);
|
||||||
|
_posts = _posts.map((p) => (p.id == post.id) ? updatedPost : p).toList();
|
||||||
|
return updatedPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -124,26 +133,36 @@ class FirebaseTimelineService implements TimelineService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> reactToPost(
|
Future<TimelinePost> reactToPost(
|
||||||
TimelinePost post,
|
TimelinePost post,
|
||||||
TimelinePostReaction reaction, {
|
TimelinePostReaction reaction, {
|
||||||
Uint8List? image,
|
Uint8List? image,
|
||||||
}) {
|
}) async {
|
||||||
// update the post with the new reaction
|
var reactionId = const Uuid().v4();
|
||||||
_posts = _posts
|
// also fetch the user information and add it to the reaction
|
||||||
.map(
|
var user = await _userService.getUser(reaction.creatorId);
|
||||||
(p) => (p.id == post.id)
|
var updatedReaction = reaction.copyWith(id: reactionId, creator: user);
|
||||||
? p.copyWith(
|
if (image != null) {
|
||||||
reaction: p.reaction + 1,
|
var imageRef = _storage
|
||||||
reactions: p.reactions?..add(reaction),
|
.ref()
|
||||||
)
|
.child('${_options.timelineCollectionName}/${post.id}/$reactionId}');
|
||||||
: p,
|
var result = await imageRef.putData(image);
|
||||||
)
|
var imageUrl = await result.ref.getDownloadURL();
|
||||||
.toList();
|
updatedReaction = updatedReaction.copyWith(imageUrl: imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedPost = post.copyWith(
|
||||||
|
reaction: post.reaction + 1,
|
||||||
|
reactions: post.reactions?..add(updatedReaction),
|
||||||
|
);
|
||||||
|
|
||||||
var postRef = _db.collection(_options.timelineCollectionName).doc(post.id);
|
var postRef = _db.collection(_options.timelineCollectionName).doc(post.id);
|
||||||
return postRef.update({
|
await postRef.update({
|
||||||
'reaction': FieldValue.increment(1),
|
'reaction': FieldValue.increment(1),
|
||||||
'reactions': FieldValue.arrayUnion([reaction.toJson()]),
|
// 'reactions' is a map of reactions, so we need to add the new reaction
|
||||||
|
// to the map
|
||||||
|
'reactions': FieldValue.arrayUnion([updatedReaction.toJson()]),
|
||||||
});
|
});
|
||||||
|
return updatedPost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,18 +39,14 @@ class TimelinePost {
|
||||||
likes: json['likes'] as int,
|
likes: json['likes'] as int,
|
||||||
likedBy: (json['liked_by'] as List<dynamic>?)?.cast<String>(),
|
likedBy: (json['liked_by'] as List<dynamic>?)?.cast<String>(),
|
||||||
reaction: json['reaction'] as int,
|
reaction: json['reaction'] as int,
|
||||||
reactions: (json['reactions'] as Map<String, dynamic>?)
|
reactions: (json['reactions'] as List<dynamic>?)
|
||||||
?.map(
|
?.map(
|
||||||
(key, value) => MapEntry(
|
(e) => TimelinePostReaction.fromJson(
|
||||||
key,
|
(e as Map).keys.first,
|
||||||
TimelinePostReaction.fromJson(
|
|
||||||
key,
|
|
||||||
id,
|
id,
|
||||||
value as Map<String, dynamic>,
|
e.values.first as Map<String, dynamic>,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.values
|
|
||||||
.toList(),
|
.toList(),
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
reactionEnabled: json['reaction_enabled'] as bool,
|
reactionEnabled: json['reaction_enabled'] as bool,
|
||||||
|
@ -140,7 +136,8 @@ class TimelinePost {
|
||||||
'likes': likes,
|
'likes': likes,
|
||||||
'liked_by': likedBy,
|
'liked_by': likedBy,
|
||||||
'reaction': reaction,
|
'reaction': reaction,
|
||||||
'reactions': reactions?.map((e) => e.toJson()).toList(),
|
// reactions is a list of maps so we need to convert it to a map
|
||||||
|
'reactions': reactions?.map((e) => e.toJson()).toList() ?? {},
|
||||||
'created_at': createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
'reaction_enabled': reactionEnabled,
|
'reaction_enabled': reactionEnabled,
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,6 +52,25 @@ class TimelinePostReaction {
|
||||||
/// Reaction creation date.
|
/// Reaction creation date.
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
TimelinePostReaction copyWith({
|
||||||
|
String? id,
|
||||||
|
String? postId,
|
||||||
|
String? creatorId,
|
||||||
|
TimelinePosterUserModel? creator,
|
||||||
|
String? reaction,
|
||||||
|
String? imageUrl,
|
||||||
|
DateTime? createdAt,
|
||||||
|
}) =>
|
||||||
|
TimelinePostReaction(
|
||||||
|
id: id ?? this.id,
|
||||||
|
postId: postId ?? this.postId,
|
||||||
|
creatorId: creatorId ?? this.creatorId,
|
||||||
|
creator: creator ?? this.creator,
|
||||||
|
reaction: reaction ?? this.reaction,
|
||||||
|
imageUrl: imageUrl ?? this.imageUrl,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||||
id: {
|
id: {
|
||||||
'creator_id': creatorId,
|
'creator_id': creatorId,
|
||||||
|
|
|
@ -13,7 +13,7 @@ abstract class TimelineService {
|
||||||
Future<List<TimelinePost>> fetchPosts(String? category);
|
Future<List<TimelinePost>> fetchPosts(String? category);
|
||||||
List<TimelinePost> getPosts(String? category);
|
List<TimelinePost> getPosts(String? category);
|
||||||
Future<TimelinePost> fetchPostDetails(TimelinePost post);
|
Future<TimelinePost> fetchPostDetails(TimelinePost post);
|
||||||
Future<void> reactToPost(
|
Future<TimelinePost> reactToPost(
|
||||||
TimelinePost post,
|
TimelinePost post,
|
||||||
TimelinePostReaction reaction, {
|
TimelinePostReaction reaction, {
|
||||||
Uint8List image,
|
Uint8List image,
|
||||||
|
|
|
@ -15,6 +15,7 @@ class TimelineOptions {
|
||||||
this.translations = const TimelineTranslations(),
|
this.translations = const TimelineTranslations(),
|
||||||
this.imagePickerConfig = const ImagePickerConfig(),
|
this.imagePickerConfig = const ImagePickerConfig(),
|
||||||
this.imagePickerTheme = const ImagePickerTheme(),
|
this.imagePickerTheme = const ImagePickerTheme(),
|
||||||
|
this.sortCommentsAscending = false,
|
||||||
this.dateformat,
|
this.dateformat,
|
||||||
this.timeFormat,
|
this.timeFormat,
|
||||||
this.buttonBuilder,
|
this.buttonBuilder,
|
||||||
|
@ -31,6 +32,9 @@ class TimelineOptions {
|
||||||
/// The format to display the post time in
|
/// The format to display the post time in
|
||||||
final DateFormat? timeFormat;
|
final DateFormat? timeFormat;
|
||||||
|
|
||||||
|
/// Whether to sort comments ascending or descending
|
||||||
|
final bool sortCommentsAscending;
|
||||||
|
|
||||||
final TimelineTranslations translations;
|
final TimelineTranslations translations;
|
||||||
|
|
||||||
final ButtonBuilder? buttonBuilder;
|
final ButtonBuilder? buttonBuilder;
|
||||||
|
|
|
@ -7,6 +7,9 @@ import 'package:flutter/material.dart';
|
||||||
@immutable
|
@immutable
|
||||||
class TimelineTranslations {
|
class TimelineTranslations {
|
||||||
const TimelineTranslations({
|
const TimelineTranslations({
|
||||||
|
this.anonymousUser = 'Anonymous user',
|
||||||
|
this.noPosts = 'No posts yet',
|
||||||
|
this.noPostsWithFilter = 'No posts with this filter',
|
||||||
this.title = 'Title',
|
this.title = 'Title',
|
||||||
this.content = 'Content',
|
this.content = 'Content',
|
||||||
this.contentDescription = 'What do you want to share?',
|
this.contentDescription = 'What do you want to share?',
|
||||||
|
@ -20,10 +23,16 @@ class TimelineTranslations {
|
||||||
this.viewPost = 'View post',
|
this.viewPost = 'View post',
|
||||||
this.likesTitle = 'Likes',
|
this.likesTitle = 'Likes',
|
||||||
this.commentsTitle = 'Comments',
|
this.commentsTitle = 'Comments',
|
||||||
|
this.firstComment = 'Be the first to comment',
|
||||||
this.writeComment = 'Write your comment here...',
|
this.writeComment = 'Write your comment here...',
|
||||||
this.postAt = 'at',
|
this.postAt = 'at',
|
||||||
|
this.postLoadingError = 'Something went wrong while loading the post',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final String noPosts;
|
||||||
|
final String noPostsWithFilter;
|
||||||
|
final String anonymousUser;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final String content;
|
final String content;
|
||||||
final String contentDescription;
|
final String contentDescription;
|
||||||
|
@ -39,4 +48,6 @@ class TimelineTranslations {
|
||||||
final String likesTitle;
|
final String likesTitle;
|
||||||
final String commentsTitle;
|
final String commentsTitle;
|
||||||
final String writeComment;
|
final String writeComment;
|
||||||
|
final String firstComment;
|
||||||
|
final String postLoadingError;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,39 +2,111 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_image_picker/flutter_image_picker.dart';
|
||||||
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
import 'package:flutter_timeline_view/src/config/timeline_options.dart';
|
||||||
import 'package:flutter_timeline_view/src/widgets/reaction_bottom.dart';
|
import 'package:flutter_timeline_view/src/widgets/reaction_bottom.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class TimelinePostScreen extends StatelessWidget {
|
class TimelinePostScreen extends StatefulWidget {
|
||||||
const TimelinePostScreen({
|
const TimelinePostScreen({
|
||||||
|
required this.userId,
|
||||||
|
required this.service,
|
||||||
|
required this.userService,
|
||||||
required this.options,
|
required this.options,
|
||||||
required this.post,
|
required this.post,
|
||||||
this.padding = const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
this.padding = const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The user id of the current user
|
||||||
|
final String userId;
|
||||||
|
|
||||||
|
/// The timeline service to fetch the post details
|
||||||
|
final TimelineService service;
|
||||||
|
|
||||||
|
/// The user service to fetch the profile picture of the user
|
||||||
|
final TimelineUserService userService;
|
||||||
|
|
||||||
|
/// Options to configure the timeline screens
|
||||||
final TimelineOptions options;
|
final TimelineOptions options;
|
||||||
|
|
||||||
|
/// The post to show
|
||||||
final TimelinePost post;
|
final TimelinePost post;
|
||||||
|
|
||||||
/// The padding around the screen
|
/// The padding around the screen
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TimelinePostScreen> createState() => _TimelinePostScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
|
TimelinePost? post;
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
unawaited(loadPostDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadPostDetails() async {
|
||||||
|
try {
|
||||||
|
// Assuming fetchPostDetails is an async function returning a TimelinePost
|
||||||
|
var loadedPost = await widget.service.fetchPostDetails(widget.post);
|
||||||
|
setState(() {
|
||||||
|
post = loadedPost;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
} on Exception catch (e) {
|
||||||
|
// Handle any errors here
|
||||||
|
debugPrint('Error loading post: $e');
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePost(TimelinePost newPost) {
|
||||||
|
setState(() {
|
||||||
|
post = newPost;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var dateFormat = options.dateformat ??
|
var dateFormat = widget.options.dateformat ??
|
||||||
DateFormat('dd/MM/yyyy', Localizations.localeOf(context).languageCode);
|
DateFormat('dd/MM/yyyy', Localizations.localeOf(context).languageCode);
|
||||||
var timeFormat = options.timeFormat ?? DateFormat('HH:mm');
|
var timeFormat = widget.options.timeFormat ?? DateFormat('HH:mm');
|
||||||
|
if (isLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.post == null) {
|
||||||
|
return Center(
|
||||||
|
child: Text(widget.options.translations.postLoadingError),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var post = this.post!;
|
||||||
|
post.reactions?.sort(
|
||||||
|
(a, b) => widget.options.sortCommentsAscending
|
||||||
|
? b.createdAt.compareTo(a.createdAt)
|
||||||
|
: a.createdAt.compareTo(b.createdAt),
|
||||||
|
);
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding,
|
padding: widget.padding,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
@ -42,7 +114,7 @@ class TimelinePostScreen extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (post.creator!.imageUrl != null) ...[
|
if (post.creator!.imageUrl != null) ...[
|
||||||
options.userAvatarBuilder?.call(
|
widget.options.userAvatarBuilder?.call(
|
||||||
post.creator!,
|
post.creator!,
|
||||||
40,
|
40,
|
||||||
) ??
|
) ??
|
||||||
|
@ -93,7 +165,7 @@ class TimelinePostScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${post.likes} ${options.translations.likesTitle}',
|
'${post.likes} ${widget.options.translations.likesTitle}',
|
||||||
style: theme.textTheme.titleSmall,
|
style: theme.textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
|
@ -118,25 +190,27 @@ class TimelinePostScreen extends StatelessWidget {
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'${dateFormat.format(post.createdAt)} '
|
'${dateFormat.format(post.createdAt)} '
|
||||||
'${options.translations.postAt} '
|
'${widget.options.translations.postAt} '
|
||||||
'${timeFormat.format(post.createdAt)}',
|
'${timeFormat.format(post.createdAt)}',
|
||||||
style: theme.textTheme.bodySmall,
|
style: theme.textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
if (post.reactionEnabled) ...[
|
||||||
Text(
|
Text(
|
||||||
options.translations.commentsTitle,
|
widget.options.translations.commentsTitle,
|
||||||
style: theme.textTheme.displaySmall,
|
style: theme.textTheme.displaySmall,
|
||||||
),
|
),
|
||||||
for (var reaction
|
for (var reaction
|
||||||
in post.reactions ?? <TimelinePostReaction>[]) ...[
|
in post.reactions ?? <TimelinePostReaction>[]) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: reaction.imageUrl != null
|
||||||
|
? CrossAxisAlignment.start
|
||||||
|
: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (reaction.creator?.imageUrl != null &&
|
if (reaction.creator?.imageUrl != null &&
|
||||||
reaction.creator!.imageUrl!.isNotEmpty) ...[
|
reaction.creator!.imageUrl!.isNotEmpty) ...[
|
||||||
options.userAvatarBuilder?.call(
|
widget.options.userAvatarBuilder?.call(
|
||||||
reaction.creator!,
|
reaction.creator!,
|
||||||
25,
|
25,
|
||||||
) ??
|
) ??
|
||||||
|
@ -148,38 +222,101 @@ class TimelinePostScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (reaction.creator?.fullName != null) ...[
|
if (reaction.imageUrl != null) ...[
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
Text(
|
Text(
|
||||||
reaction.creator!.fullName!,
|
reaction.creator?.fullName ??
|
||||||
|
widget.options.translations.anonymousUser,
|
||||||
style: theme.textTheme.titleSmall,
|
style: theme.textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: reaction.imageUrl!,
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
),
|
||||||
// TODO(anyone): show image if the user send one
|
),
|
||||||
|
] else ...[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text.rich(
|
||||||
reaction.reaction ?? '',
|
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,
|
style: theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
// text should go to new line
|
// text should go to new line
|
||||||
softWrap: true,
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 100),
|
],
|
||||||
|
const SizedBox(height: 120),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (post.reactionEnabled)
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: ReactionBottom(
|
child: ReactionBottom(
|
||||||
messageInputBuilder: options.textInputBuilder!,
|
messageInputBuilder: widget.options.textInputBuilder!,
|
||||||
onPressSelectImage: () async {},
|
onPressSelectImage: () async {
|
||||||
onReactionSubmit: (reaction) async {},
|
// open the image picker
|
||||||
translations: options.translations,
|
var result = await showModalBottomSheet<Uint8List?>(
|
||||||
iconColor: options.theme.iconColor,
|
context: context,
|
||||||
|
builder: (context) => Container(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
color: Colors.black,
|
||||||
|
child: ImagePicker(
|
||||||
|
imagePickerConfig: widget.options.imagePickerConfig,
|
||||||
|
imagePickerTheme: widget.options.imagePickerTheme,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
updatePost(
|
||||||
|
await widget.service.reactToPost(
|
||||||
|
post,
|
||||||
|
TimelinePostReaction(
|
||||||
|
id: '',
|
||||||
|
postId: post.id,
|
||||||
|
creatorId: widget.userId,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
),
|
||||||
|
image: result,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onReactionSubmit: (reaction) async => updatePost(
|
||||||
|
await widget.service.reactToPost(
|
||||||
|
post,
|
||||||
|
TimelinePostReaction(
|
||||||
|
id: '',
|
||||||
|
postId: post.id,
|
||||||
|
reaction: reaction,
|
||||||
|
creatorId: widget.userId,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
translations: widget.options.translations,
|
||||||
|
iconColor: widget.options.theme.iconColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue