feat:add namebuilder

This commit is contained in:
Tim 2023-12-14 11:37:03 +01:00
parent 03b14924a2
commit d60244fa3e
5 changed files with 180 additions and 108 deletions

View file

@ -26,6 +26,7 @@ class TimelinePost {
this.reactions, this.reactions,
this.imageUrl, this.imageUrl,
this.image, this.image,
this.data = const {},
}); });
factory TimelinePost.fromJson(String id, Map<String, dynamic> json) => factory TimelinePost.fromJson(String id, Map<String, dynamic> json) =>
@ -50,6 +51,7 @@ class TimelinePost {
.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,
data: json['data'] ?? {},
); );
/// The unique identifier of the post. /// The unique identifier of the post.
@ -94,6 +96,9 @@ class TimelinePost {
/// If reacting is enabled on the post. /// If reacting is enabled on the post.
final bool reactionEnabled; final bool reactionEnabled;
/// Option to add extra data to a timelinepost that won't be shown anywhere
final Map<String, dynamic> data;
TimelinePost copyWith({ TimelinePost copyWith({
String? id, String? id,
String? creatorId, String? creatorId,
@ -109,6 +114,7 @@ class TimelinePost {
List<TimelinePostReaction>? reactions, List<TimelinePostReaction>? reactions,
DateTime? createdAt, DateTime? createdAt,
bool? reactionEnabled, bool? reactionEnabled,
Map<String, dynamic>? data,
}) => }) =>
TimelinePost( TimelinePost(
id: id ?? this.id, id: id ?? this.id,
@ -125,6 +131,7 @@ class TimelinePost {
reactions: reactions ?? this.reactions, reactions: reactions ?? this.reactions,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
reactionEnabled: reactionEnabled ?? this.reactionEnabled, reactionEnabled: reactionEnabled ?? this.reactionEnabled,
data: data ?? this.data,
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -140,5 +147,6 @@ class TimelinePost {
'reactions': reactions?.map((e) => e.toJson()).toList() ?? [], 'reactions': reactions?.map((e) => e.toJson()).toList() ?? [],
'created_at': createdAt.toIso8601String(), 'created_at': createdAt.toIso8601String(),
'reaction_enabled': reactionEnabled, 'reaction_enabled': reactionEnabled,
'data': data,
}; };
} }

View file

@ -24,6 +24,8 @@ class TimelineOptions {
this.buttonBuilder, this.buttonBuilder,
this.textInputBuilder, this.textInputBuilder,
this.userAvatarBuilder, this.userAvatarBuilder,
this.anonymousAvatarBuilder,
this.nameBuilder,
}); });
/// Theming options for the timeline /// Theming options for the timeline
@ -56,6 +58,12 @@ class TimelineOptions {
final UserAvatarBuilder? userAvatarBuilder; final UserAvatarBuilder? userAvatarBuilder;
/// When the imageUrl is null this anonymousAvatarBuilder will be used
/// You can use it to display a default avatarW
final UserAvatarBuilder? anonymousAvatarBuilder;
final String Function(TimelinePosterUserModel?)? nameBuilder;
/// ImagePickerTheme can be used to change the UI of the /// ImagePickerTheme can be used to change the UI of the
/// Image Picker Widget to change the text/icons to your liking. /// Image Picker Widget to change the text/icons to your liking.
final ImagePickerTheme imagePickerTheme; final ImagePickerTheme imagePickerTheme;
@ -78,7 +86,7 @@ typedef TextInputBuilder = Widget Function(
String hintText, String hintText,
); );
typedef UserAvatarBuilder = Widget Function( typedef UserAvatarBuilder = Widget? Function(
TimelinePosterUserModel user, TimelinePosterUserModel user,
double size, double size,
); );

View file

@ -123,6 +123,7 @@ class _TimelinePostCreationScreenState
height: 100, height: 100,
child: TextField( child: TextField(
controller: contentController, controller: contentController,
textCapitalization: TextCapitalization.sentences,
expands: true, expands: true,
maxLines: null, maxLines: null,
minLines: null, minLines: null,

View file

@ -153,16 +153,28 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
post.creator!.imageUrl!, post.creator!.imageUrl!,
), ),
), ),
] else ...[
widget.options.anonymousAvatarBuilder?.call(
post.creator!,
40,
) ??
const CircleAvatar(
radius: 20,
child: Icon(
Icons.person,
),
),
], ],
const SizedBox(width: 10), const SizedBox(width: 10),
if (post.creator!.fullName != null) ...[ Text(
Text( widget.options.nameBuilder
post.creator!.fullName!, ?.call(post.creator) ??
style: widget.options.theme.textStyles post.creator?.fullName ??
.postCreatorTitleStyle ?? widget.options.translations.anonymousUser,
theme.textTheme.titleMedium, style: widget.options.theme.textStyles
), .postCreatorTitleStyle ??
], theme.textTheme.titleMedium,
),
], ],
), ),
), ),
@ -206,63 +218,67 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
), ),
], ],
), ),
const SizedBox(height: 8),
// image of the post // image of the post
if (post.imageUrl != null) ...[ if (post.imageUrl != null) ...[
CachedNetworkImage( const SizedBox(height: 8),
imageUrl: post.imageUrl!, ClipRRect(
width: double.infinity, borderRadius: const BorderRadius.all(Radius.circular(8)),
fit: BoxFit.fitHeight, child: CachedNetworkImage(
width: double.infinity,
imageUrl: post.imageUrl!,
fit: BoxFit.fitHeight,
),
), ),
], ],
const SizedBox(
height: 8,
),
// post information // post information
Padding( Row(
padding: const EdgeInsets.symmetric(vertical: 4.0), children: [
child: Row( if (post.likedBy?.contains(widget.userId) ?? false) ...[
children: [ InkWell(
if (post.likedBy?.contains(widget.userId) ?? false) ...[ onTap: () async {
InkWell( updatePost(
onTap: () async { await widget.service.unlikePost(
updatePost( widget.userId,
await widget.service.unlikePost( post,
widget.userId, ),
post, );
), },
); child: widget.options.theme.likedIcon ??
},
child: widget.options.theme.likedIcon ??
Icon(
Icons.thumb_up_rounded,
color: widget.options.theme.iconColor,
),
),
] else ...[
InkWell(
onTap: () async {
updatePost(
await widget.service.likePost(
widget.userId,
post,
),
);
},
child: widget.options.theme.likeIcon ??
Icon(
Icons.thumb_up_alt_outlined,
color: widget.options.theme.iconColor,
),
),
],
const SizedBox(width: 8),
if (post.reactionEnabled)
widget.options.theme.commentIcon ??
Icon( Icon(
Icons.chat_bubble_outline_rounded, Icons.thumb_up_rounded,
color: widget.options.theme.iconColor, color: widget.options.theme.iconColor,
), ),
),
] else ...[
InkWell(
onTap: () async {
updatePost(
await widget.service.likePost(
widget.userId,
post,
),
);
},
child: widget.options.theme.likeIcon ??
Icon(
Icons.thumb_up_alt_outlined,
color: widget.options.theme.iconColor,
),
),
], ],
), const SizedBox(width: 8),
if (post.reactionEnabled)
widget.options.theme.commentIcon ??
Icon(
Icons.chat_bubble_outline_rounded,
color: widget.options.theme.iconColor,
),
],
), ),
const SizedBox(height: 8),
Text( Text(
'${post.likes} ${widget.options.translations.likesTitle}', '${post.likes} ${widget.options.translations.likesTitle}',
style: widget style: widget
@ -272,7 +288,8 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
const SizedBox(height: 4), const SizedBox(height: 4),
Text.rich( Text.rich(
TextSpan( TextSpan(
text: post.creator?.fullName ?? text: widget.options.nameBuilder?.call(post.creator) ??
post.creator?.fullName ??
widget.options.translations.anonymousUser, widget.options.translations.anonymousUser,
style: widget style: widget
.options.theme.textStyles.postCreatorNameStyle ?? .options.theme.textStyles.postCreatorNameStyle ??
@ -287,9 +304,8 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
), ),
], ],
), ),
overflow: TextOverflow.ellipsis,
), ),
const SizedBox(height: 4), const SizedBox(height: 20),
Html( Html(
data: post.content, data: post.content,
style: { style: {
@ -300,15 +316,20 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
'#': Style( '#': Style(
maxLines: 3, maxLines: 3,
textOverflow: TextOverflow.ellipsis, textOverflow: TextOverflow.ellipsis,
padding: HtmlPaddings.zero,
margin: Margins.zero,
), ),
'H1': Style( 'H1': Style(
margin: Margins.all(0), padding: HtmlPaddings.zero,
margin: Margins.zero,
), ),
'H2': Style( 'H2': Style(
margin: Margins.all(0), padding: HtmlPaddings.zero,
margin: Margins.zero,
), ),
'H3': Style( 'H3': Style(
margin: Margins.all(0), padding: HtmlPaddings.zero,
margin: Margins.zero,
), ),
}, },
), ),
@ -319,7 +340,7 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
'${timeFormat.format(post.createdAt)}', '${timeFormat.format(post.createdAt)}',
style: theme.textTheme.bodySmall, style: theme.textTheme.bodySmall,
), ),
const SizedBox(height: 12), const SizedBox(height: 20),
if (post.reactionEnabled) ...[ if (post.reactionEnabled) ...[
Text( Text(
widget.options.translations.commentsTitle, widget.options.translations.commentsTitle,
@ -381,6 +402,17 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
reaction.creator!.imageUrl!, reaction.creator!.imageUrl!,
), ),
), ),
] else ...[
widget.options.anonymousAvatarBuilder?.call(
reaction.creator!,
25,
) ??
const CircleAvatar(
radius: 20,
child: Icon(
Icons.person,
),
),
], ],
const SizedBox(width: 10), const SizedBox(width: 10),
if (reaction.imageUrl != null) ...[ if (reaction.imageUrl != null) ...[
@ -389,7 +421,9 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
reaction.creator?.fullName ?? widget.options.nameBuilder
?.call(post.creator) ??
reaction.creator?.fullName ??
widget.options.translations widget.options.translations
.anonymousUser, .anonymousUser,
style: theme.textTheme.titleSmall, style: theme.textTheme.titleSmall,
@ -408,7 +442,9 @@ class _TimelinePostScreenState extends State<TimelinePostScreen> {
Expanded( Expanded(
child: Text.rich( child: Text.rich(
TextSpan( TextSpan(
text: reaction.creator?.fullName ?? text: widget.options.nameBuilder
?.call(post.creator) ??
reaction.creator?.fullName ??
widget widget
.options.translations.anonymousUser, .options.translations.anonymousUser,
style: theme.textTheme.titleSmall, style: theme.textTheme.titleSmall,

View file

@ -68,16 +68,27 @@ class TimelinePostWidget extends StatelessWidget {
post.creator!.imageUrl!, post.creator!.imageUrl!,
), ),
), ),
] else ...[
options.anonymousAvatarBuilder?.call(
post.creator!,
40,
) ??
const CircleAvatar(
radius: 20,
child: Icon(
Icons.person,
),
),
], ],
const SizedBox(width: 10), const SizedBox(width: 10),
if (post.creator!.fullName != null) ...[ Text(
Text( options.nameBuilder?.call(post.creator) ??
post.creator!.fullName!, post.creator?.fullName ??
style: options.theme.textStyles options.translations.anonymousUser,
.listPostCreatorTitleStyle ?? style:
theme.textTheme.titleMedium, options.theme.textStyles.postCreatorTitleStyle ??
), theme.textTheme.titleMedium,
], ),
], ],
), ),
), ),
@ -118,52 +129,59 @@ class TimelinePostWidget extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(height: 8),
// image of the post // image of the post
if (post.imageUrl != null) ...[ if (post.imageUrl != null) ...[
const SizedBox(height: 8),
Flexible( Flexible(
flex: height != null ? 1 : 0, flex: height != null ? 1 : 0,
child: CachedNetworkImage( child: ClipRRect(
imageUrl: post.imageUrl!, borderRadius: const BorderRadius.all(Radius.circular(8)),
width: double.infinity, child: CachedNetworkImage(
fit: BoxFit.fitWidth, width: double.infinity,
imageUrl: post.imageUrl!,
fit: BoxFit.fitWidth,
),
), ),
), ),
], ],
const SizedBox(
height: 8,
),
// post information // post information
Padding( Row(
padding: const EdgeInsets.symmetric(vertical: 4.0), children: [
child: Row( if (post.likedBy?.contains(userId) ?? false) ...[
children: [ InkWell(
if (post.likedBy?.contains(userId) ?? false) ...[ onTap: onTapUnlike,
InkWell( child: options.theme.likedIcon ??
onTap: onTapUnlike,
child: options.theme.likedIcon ??
Icon(
Icons.thumb_up_rounded,
color: options.theme.iconColor,
),
),
] else ...[
InkWell(
onTap: onTapLike,
child: options.theme.likeIcon ??
Icon(
Icons.thumb_up_alt_outlined,
color: options.theme.iconColor,
),
),
],
const SizedBox(width: 8),
if (post.reactionEnabled)
options.theme.commentIcon ??
Icon( Icon(
Icons.chat_bubble_outline_rounded, Icons.thumb_up_rounded,
color: options.theme.iconColor, color: options.theme.iconColor,
), ),
),
] else ...[
InkWell(
onTap: onTapLike,
child: options.theme.likeIcon ??
Icon(
Icons.thumb_up_alt_outlined,
color: options.theme.iconColor,
),
),
], ],
), const SizedBox(width: 8),
if (post.reactionEnabled)
options.theme.commentIcon ??
Icon(
Icons.chat_bubble_outline_rounded,
color: options.theme.iconColor,
),
],
), ),
const SizedBox(
height: 8,
),
Text( Text(
'${post.likes} ${options.translations.likesTitle}', '${post.likes} ${options.translations.likesTitle}',
style: options.theme.textStyles.listPostLikeTitleAndAmount ?? style: options.theme.textStyles.listPostLikeTitleAndAmount ??
@ -172,7 +190,8 @@ class TimelinePostWidget extends StatelessWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
Text.rich( Text.rich(
TextSpan( TextSpan(
text: post.creator?.fullName ?? text: options.nameBuilder?.call(post.creator) ??
post.creator?.fullName ??
options.translations.anonymousUser, options.translations.anonymousUser,
style: options.theme.textStyles.listCreatorNameStyle ?? style: options.theme.textStyles.listCreatorNameStyle ??
theme.textTheme.titleSmall, theme.textTheme.titleSmall,
@ -185,8 +204,8 @@ class TimelinePostWidget extends StatelessWidget {
), ),
], ],
), ),
overflow: TextOverflow.ellipsis,
), ),
const SizedBox(height: 4),
Text( Text(
options.translations.viewPost, options.translations.viewPost,
style: options.theme.textStyles.viewPostStyle ?? style: options.theme.textStyles.viewPostStyle ??