mirror of
https://github.com/Iconica-Development/flutter_timeline.git
synced 2025-05-19 10:33:44 +02:00
Merge pull request #8 from Iconica-Development/feature/buddy_merge
feat: buddy merge
This commit is contained in:
commit
e61da6873d
6 changed files with 424 additions and 100 deletions
|
@ -14,12 +14,14 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
go_router: any
|
go_router: any
|
||||||
|
|
||||||
flutter_timeline_view:
|
flutter_timeline_view:
|
||||||
path: ../flutter_timeline_view
|
path: ../flutter_timeline_view
|
||||||
# git:
|
# git:
|
||||||
# url: https://github.com/Iconica-Development/flutter_timeline
|
# url: https://github.com/Iconica-Development/flutter_timeline
|
||||||
# path: packages/flutter_timeline_view
|
# path: packages/flutter_timeline_view
|
||||||
# ref: 1.0.0
|
# ref: 1.0.0
|
||||||
|
|
||||||
flutter_timeline_interface:
|
flutter_timeline_interface:
|
||||||
path: ../flutter_timeline_interface
|
path: ../flutter_timeline_interface
|
||||||
# git:
|
# git:
|
||||||
|
|
|
@ -19,10 +19,21 @@ class TimelineOptions {
|
||||||
this.allowAllDeletion = false,
|
this.allowAllDeletion = false,
|
||||||
this.sortCommentsAscending = true,
|
this.sortCommentsAscending = true,
|
||||||
this.sortPostsAscending = false,
|
this.sortPostsAscending = false,
|
||||||
|
this.doubleTapTolike = false,
|
||||||
|
this.iconsWithValues = false,
|
||||||
|
this.likeAndDislikeIconsForDoubleTap = const (
|
||||||
|
Icon(
|
||||||
|
Icons.favorite_rounded,
|
||||||
|
color: Color(0xFFC3007A),
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
),
|
||||||
|
this.itemInfoBuilder,
|
||||||
this.dateFormat,
|
this.dateFormat,
|
||||||
this.timeFormat,
|
this.timeFormat,
|
||||||
this.buttonBuilder,
|
this.buttonBuilder,
|
||||||
this.textInputBuilder,
|
this.textInputBuilder,
|
||||||
|
this.dividerBuilder,
|
||||||
this.userAvatarBuilder,
|
this.userAvatarBuilder,
|
||||||
this.anonymousAvatarBuilder,
|
this.anonymousAvatarBuilder,
|
||||||
this.nameBuilder,
|
this.nameBuilder,
|
||||||
|
@ -74,6 +85,21 @@ class TimelineOptions {
|
||||||
/// size and quality for the uploaded image.
|
/// size and quality for the uploaded image.
|
||||||
final ImagePickerConfig imagePickerConfig;
|
final ImagePickerConfig imagePickerConfig;
|
||||||
|
|
||||||
|
/// Whether to allow double tap to like
|
||||||
|
final bool doubleTapTolike;
|
||||||
|
|
||||||
|
/// The icons to display when double tap to like is enabled
|
||||||
|
final (Icon?, Icon?) likeAndDislikeIconsForDoubleTap;
|
||||||
|
|
||||||
|
/// Whether to display the icons with values
|
||||||
|
final bool iconsWithValues;
|
||||||
|
|
||||||
|
/// The builder for the item info, all below the like and comment buttons
|
||||||
|
final Widget Function({required TimelinePost post})? itemInfoBuilder;
|
||||||
|
|
||||||
|
/// The builder for the divider
|
||||||
|
final Widget Function()? dividerBuilder;
|
||||||
|
|
||||||
/// The padding between posts in the timeline
|
/// The padding between posts in the timeline
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ 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:flutter_timeline_view/src/widgets/tappable_image.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class TimelinePostScreen extends StatefulWidget {
|
class TimelinePostScreen extends StatefulWidget {
|
||||||
|
@ -225,7 +226,36 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: CachedNetworkImage(
|
child: widget.options.doubleTapTolike
|
||||||
|
? TappableImage(
|
||||||
|
likeAndDislikeIcon: widget
|
||||||
|
.options.likeAndDislikeIconsForDoubleTap,
|
||||||
|
post: post,
|
||||||
|
userId: widget.userId,
|
||||||
|
onLike: ({required bool liked}) async {
|
||||||
|
var userId = widget.userId;
|
||||||
|
|
||||||
|
late TimelinePost result;
|
||||||
|
|
||||||
|
if (!liked) {
|
||||||
|
result = await widget.service.likePost(
|
||||||
|
userId,
|
||||||
|
post,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = await widget.service.unlikePost(
|
||||||
|
userId,
|
||||||
|
post,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadPostDetails();
|
||||||
|
|
||||||
|
return result.likedBy?.contains(userId) ??
|
||||||
|
false;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: CachedNetworkImage(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
imageUrl: post.imageUrl!,
|
imageUrl: post.imageUrl!,
|
||||||
fit: BoxFit.fitHeight,
|
fit: BoxFit.fitHeight,
|
||||||
|
|
|
@ -97,6 +97,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
|
||||||
(post) => Padding(
|
(post) => Padding(
|
||||||
padding: widget.options.padding,
|
padding: widget.options.padding,
|
||||||
child: TimelinePostWidget(
|
child: TimelinePostWidget(
|
||||||
|
service: widget.service,
|
||||||
userId: widget.userId,
|
userId: widget.userId,
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
post: post,
|
post: post,
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
|
||||||
|
|
||||||
|
class TappableImage extends StatefulWidget {
|
||||||
|
const TappableImage({
|
||||||
|
required this.post,
|
||||||
|
required this.onLike,
|
||||||
|
required this.userId,
|
||||||
|
required this.likeAndDislikeIcon,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TimelinePost post;
|
||||||
|
final String userId;
|
||||||
|
final Future<bool> Function({required bool liked}) onLike;
|
||||||
|
final (Icon?, Icon?) likeAndDislikeIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TappableImage> createState() => _TappableImageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TappableImageState extends State<TappableImage>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController animationController;
|
||||||
|
late Animation<double> animation;
|
||||||
|
bool loading = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 350),
|
||||||
|
);
|
||||||
|
|
||||||
|
animation = CurvedAnimation(
|
||||||
|
parent: animationController,
|
||||||
|
curve: Curves.ease,
|
||||||
|
);
|
||||||
|
|
||||||
|
animationController.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void listener() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
animationController.removeListener(listener);
|
||||||
|
animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void startAnimation() {
|
||||||
|
animationController.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverseAnimation() {
|
||||||
|
animationController.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => InkWell(
|
||||||
|
onDoubleTap: () async {
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading = true;
|
||||||
|
await animationController.forward();
|
||||||
|
|
||||||
|
var liked = await widget.onLike(
|
||||||
|
liked: widget.post.likedBy?.contains(
|
||||||
|
widget.userId,
|
||||||
|
) ??
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
await showDialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
barrierColor: Colors.transparent,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => HeartAnimation(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
liked: liked,
|
||||||
|
likeAndDislikeIcon: widget.likeAndDislikeIcon,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await animationController.reverse();
|
||||||
|
loading = false;
|
||||||
|
},
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: Offset(0, animation.value * -32),
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: 1 + animation.value * 0.1,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: widget.post.imageUrl ?? '',
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.fitHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class HeartAnimation extends StatefulWidget {
|
||||||
|
const HeartAnimation({
|
||||||
|
required this.duration,
|
||||||
|
required this.liked,
|
||||||
|
required this.likeAndDislikeIcon,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Duration duration;
|
||||||
|
final bool liked;
|
||||||
|
final (Icon?, Icon?) likeAndDislikeIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HeartAnimation> createState() => _HeartAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HeartAnimationState extends State<HeartAnimation> {
|
||||||
|
late bool active;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
active = widget.liked;
|
||||||
|
unawaited(
|
||||||
|
Future.delayed(const Duration(milliseconds: 100)).then((value) async {
|
||||||
|
active = widget.liked;
|
||||||
|
var navigator = Navigator.of(context);
|
||||||
|
await Future.delayed(widget.duration);
|
||||||
|
navigator.pop();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => AnimatedOpacity(
|
||||||
|
opacity: widget.likeAndDislikeIcon.$1 != null &&
|
||||||
|
widget.likeAndDislikeIcon.$2 != null
|
||||||
|
? 1
|
||||||
|
: active
|
||||||
|
? 1
|
||||||
|
: 0,
|
||||||
|
duration: widget.duration,
|
||||||
|
curve: Curves.decelerate,
|
||||||
|
child: AnimatedScale(
|
||||||
|
scale: widget.likeAndDislikeIcon.$1 != null &&
|
||||||
|
widget.likeAndDislikeIcon.$2 != null
|
||||||
|
? 10
|
||||||
|
: active
|
||||||
|
? 10
|
||||||
|
: 1,
|
||||||
|
duration: widget.duration,
|
||||||
|
child: active
|
||||||
|
? widget.likeAndDislikeIcon.$1
|
||||||
|
: widget.likeAndDislikeIcon.$2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,8 +6,9 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/tappable_image.dart';
|
||||||
|
|
||||||
class TimelinePostWidget extends StatelessWidget {
|
class TimelinePostWidget extends StatefulWidget {
|
||||||
const TimelinePostWidget({
|
const TimelinePostWidget({
|
||||||
required this.userId,
|
required this.userId,
|
||||||
required this.options,
|
required this.options,
|
||||||
|
@ -17,6 +18,7 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
required this.onTapLike,
|
required this.onTapLike,
|
||||||
required this.onTapUnlike,
|
required this.onTapUnlike,
|
||||||
required this.onPostDelete,
|
required this.onPostDelete,
|
||||||
|
required this.service,
|
||||||
this.onUserTap,
|
this.onUserTap,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
@ -33,44 +35,51 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
final VoidCallback onTapLike;
|
final VoidCallback onTapLike;
|
||||||
final VoidCallback onTapUnlike;
|
final VoidCallback onTapUnlike;
|
||||||
final VoidCallback onPostDelete;
|
final VoidCallback onPostDelete;
|
||||||
|
final TimelineService service;
|
||||||
|
|
||||||
/// If this is not null, the user can tap on the user avatar or name
|
/// If this is not null, the user can tap on the user avatar or name
|
||||||
final Function(String userId)? onUserTap;
|
final Function(String userId)? onUserTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TimelinePostWidget> createState() => _TimelinePostWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TimelinePostWidgetState extends State<TimelinePostWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: widget.onTap,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: post.imageUrl != null ? height : null,
|
height: widget.post.imageUrl != null ? widget.height : null,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (post.creator != null)
|
if (widget.post.creator != null)
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: onUserTap != null
|
onTap: widget.onUserTap != null
|
||||||
? () => onUserTap?.call(post.creator!.userId)
|
? () =>
|
||||||
|
widget.onUserTap?.call(widget.post.creator!.userId)
|
||||||
: null,
|
: null,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (post.creator!.imageUrl != null) ...[
|
if (widget.post.creator!.imageUrl != null) ...[
|
||||||
options.userAvatarBuilder?.call(
|
widget.options.userAvatarBuilder?.call(
|
||||||
post.creator!,
|
widget.post.creator!,
|
||||||
40,
|
40,
|
||||||
) ??
|
) ??
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 20,
|
radius: 20,
|
||||||
backgroundImage: CachedNetworkImageProvider(
|
backgroundImage: CachedNetworkImageProvider(
|
||||||
post.creator!.imageUrl!,
|
widget.post.creator!.imageUrl!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
options.anonymousAvatarBuilder?.call(
|
widget.options.anonymousAvatarBuilder?.call(
|
||||||
post.creator!,
|
widget.post.creator!,
|
||||||
40,
|
40,
|
||||||
) ??
|
) ??
|
||||||
const CircleAvatar(
|
const CircleAvatar(
|
||||||
|
@ -82,22 +91,24 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(
|
Text(
|
||||||
options.nameBuilder?.call(post.creator) ??
|
widget.options.nameBuilder
|
||||||
post.creator?.fullName ??
|
?.call(widget.post.creator) ??
|
||||||
options.translations.anonymousUser,
|
widget.post.creator?.fullName ??
|
||||||
style:
|
widget.options.translations.anonymousUser,
|
||||||
options.theme.textStyles.postCreatorTitleStyle ??
|
style: widget.options.theme.textStyles
|
||||||
|
.postCreatorTitleStyle ??
|
||||||
theme.textTheme.titleMedium,
|
theme.textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (options.allowAllDeletion || post.creator?.userId == userId)
|
if (widget.options.allowAllDeletion ||
|
||||||
|
widget.post.creator?.userId == widget.userId)
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
if (value == 'delete') {
|
if (value == 'delete') {
|
||||||
onPostDelete();
|
widget.onPostDelete();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) =>
|
||||||
|
@ -107,38 +118,65 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
options.translations.deletePost,
|
widget.options.translations.deletePost,
|
||||||
style: options.theme.textStyles.deletePostStyle ??
|
style: widget.options.theme.textStyles
|
||||||
|
.deletePostStyle ??
|
||||||
theme.textTheme.bodyMedium,
|
theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
options.theme.deleteIcon ??
|
widget.options.theme.deleteIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.delete,
|
Icons.delete,
|
||||||
color: options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: options.theme.moreIcon ??
|
child: widget.options.theme.moreIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.more_horiz_rounded,
|
Icons.more_horiz_rounded,
|
||||||
color: options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// image of the post
|
// image of the post
|
||||||
if (post.imageUrl != null) ...[
|
if (widget.post.imageUrl != null) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: height != null ? 1 : 0,
|
flex: widget.height != null ? 1 : 0,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: CachedNetworkImage(
|
child: widget.options.doubleTapTolike
|
||||||
|
? TappableImage(
|
||||||
|
likeAndDislikeIcon:
|
||||||
|
widget.options.likeAndDislikeIconsForDoubleTap,
|
||||||
|
post: widget.post,
|
||||||
|
userId: widget.userId,
|
||||||
|
onLike: ({required bool liked}) async {
|
||||||
|
var userId = widget.userId;
|
||||||
|
|
||||||
|
late TimelinePost result;
|
||||||
|
|
||||||
|
if (!liked) {
|
||||||
|
result = await widget.service.likePost(
|
||||||
|
userId,
|
||||||
|
widget.post,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = await widget.service.unlikePost(
|
||||||
|
userId,
|
||||||
|
widget.post,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.likedBy?.contains(userId) ?? false;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: CachedNetworkImage(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
imageUrl: post.imageUrl!,
|
imageUrl: widget.post.imageUrl!,
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -148,67 +186,123 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
// post information
|
// post information
|
||||||
|
if (widget.options.iconsWithValues)
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (post.likedBy?.contains(userId) ?? false) ...[
|
TextButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
var userId = widget.userId;
|
||||||
|
|
||||||
|
var liked =
|
||||||
|
widget.post.likedBy?.contains(userId) ?? false;
|
||||||
|
|
||||||
|
if (!liked) {
|
||||||
|
await widget.service.likePost(
|
||||||
|
userId,
|
||||||
|
widget.post,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await widget.service.unlikePost(
|
||||||
|
userId,
|
||||||
|
widget.post,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: widget.options.theme.likeIcon ??
|
||||||
|
Icon(
|
||||||
|
widget.post.likedBy?.contains(widget.userId) ?? false
|
||||||
|
? Icons.favorite
|
||||||
|
: Icons.favorite_outline_outlined,
|
||||||
|
),
|
||||||
|
label: Text('${widget.post.likes}'),
|
||||||
|
),
|
||||||
|
if (widget.post.reactionEnabled)
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: widget.onTap,
|
||||||
|
icon: widget.options.theme.commentIcon ??
|
||||||
|
const Icon(
|
||||||
|
Icons.chat_bubble_outline_outlined,
|
||||||
|
),
|
||||||
|
label: Text('${widget.post.reaction}'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (widget.post.likedBy?.contains(widget.userId) ??
|
||||||
|
false) ...[
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: onTapUnlike,
|
onTap: widget.onTapUnlike,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: options.theme.likedIcon ??
|
child: widget.options.theme.likedIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.thumb_up_rounded,
|
Icons.thumb_up_rounded,
|
||||||
color: options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
size: options.iconSize,
|
size: widget.options.iconSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: onTapLike,
|
onTap: widget.onTapLike,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: options.theme.likeIcon ??
|
child: widget.options.theme.likedIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.thumb_up_alt_outlined,
|
Icons.thumb_up_rounded,
|
||||||
color: options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
size: options.iconSize,
|
size: widget.options.iconSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
if (post.reactionEnabled)
|
if (widget.post.reactionEnabled) ...[
|
||||||
options.theme.commentIcon ??
|
Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: widget.options.theme.commentIcon ??
|
||||||
Icon(
|
Icon(
|
||||||
Icons.chat_bubble_outline_rounded,
|
Icons.chat_bubble_outline_rounded,
|
||||||
color: options.theme.iconColor,
|
color: widget.options.theme.iconColor,
|
||||||
size: options.iconSize,
|
size: widget.options.iconSize,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (widget.options.itemInfoBuilder != null) ...[
|
||||||
|
widget.options.itemInfoBuilder!(
|
||||||
|
post: widget.post,
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
Text(
|
Text(
|
||||||
'${post.likes} ${options.translations.likesTitle}',
|
'${widget.post.likes} '
|
||||||
style: options.theme.textStyles.listPostLikeTitleAndAmount ??
|
'${widget.options.translations.likesTitle}',
|
||||||
|
style: widget
|
||||||
|
.options.theme.textStyles.listPostLikeTitleAndAmount ??
|
||||||
theme.textTheme.titleSmall,
|
theme.textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: options.nameBuilder?.call(post.creator) ??
|
text: widget.options.nameBuilder?.call(widget.post.creator) ??
|
||||||
post.creator?.fullName ??
|
widget.post.creator?.fullName ??
|
||||||
options.translations.anonymousUser,
|
widget.options.translations.anonymousUser,
|
||||||
style: options.theme.textStyles.listCreatorNameStyle ??
|
style: widget.options.theme.textStyles.listCreatorNameStyle ??
|
||||||
theme.textTheme.titleSmall,
|
theme.textTheme.titleSmall,
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: ' '),
|
const TextSpan(text: ' '),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: post.title,
|
text: widget.post.title,
|
||||||
style: options.theme.textStyles.listPostTitleStyle ??
|
style:
|
||||||
|
widget.options.theme.textStyles.listPostTitleStyle ??
|
||||||
theme.textTheme.bodyMedium,
|
theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -216,11 +310,14 @@ class TimelinePostWidget extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
options.translations.viewPost,
|
widget.options.translations.viewPost,
|
||||||
style: options.theme.textStyles.viewPostStyle ??
|
style: widget.options.theme.textStyles.viewPostStyle ??
|
||||||
theme.textTheme.bodySmall,
|
theme.textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
if (widget.options.dividerBuilder != null)
|
||||||
|
widget.options.dividerBuilder!(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue