✨ Display post's tag
This commit is contained in:
parent
75c753ef63
commit
f231fc9ec0
@ -21,3 +21,87 @@ class FeedRecord {
|
|||||||
'created_at': createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Tag {
|
||||||
|
int id;
|
||||||
|
String alias;
|
||||||
|
String name;
|
||||||
|
String description;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
|
||||||
|
Tag({
|
||||||
|
required this.id,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Tag.fromJson(Map<String, dynamic> json) => Tag(
|
||||||
|
id: json['id'],
|
||||||
|
alias: json['alias'],
|
||||||
|
name: json['name'],
|
||||||
|
description: json['description'],
|
||||||
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
|
deletedAt: json['deleted_at'] != null
|
||||||
|
? DateTime.parse(json['deleted_at'])
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'alias': alias,
|
||||||
|
'description': description,
|
||||||
|
'name': name,
|
||||||
|
'created_at': createdAt.toIso8601String(),
|
||||||
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Category {
|
||||||
|
int id;
|
||||||
|
String alias;
|
||||||
|
String name;
|
||||||
|
String description;
|
||||||
|
DateTime createdAt;
|
||||||
|
DateTime updatedAt;
|
||||||
|
DateTime? deletedAt;
|
||||||
|
|
||||||
|
Category({
|
||||||
|
required this.id,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Category.fromJson(Map<String, dynamic> json) => Category(
|
||||||
|
id: json['id'],
|
||||||
|
alias: json['alias'],
|
||||||
|
name: json['name'],
|
||||||
|
description: json['description'],
|
||||||
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
|
deletedAt: json['deleted_at'] != null
|
||||||
|
? DateTime.parse(json['deleted_at'])
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'alias': alias,
|
||||||
|
'description': description,
|
||||||
|
'name': name,
|
||||||
|
'created_at': createdAt.toIso8601String(),
|
||||||
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
|
import 'package:solian/models/feed.dart';
|
||||||
import 'package:solian/models/realm.dart';
|
import 'package:solian/models/realm.dart';
|
||||||
|
|
||||||
class Post {
|
class Post {
|
||||||
@ -8,9 +9,8 @@ class Post {
|
|||||||
DateTime? deletedAt;
|
DateTime? deletedAt;
|
||||||
String alias;
|
String alias;
|
||||||
String content;
|
String content;
|
||||||
dynamic tags;
|
List<Tag>? tags;
|
||||||
dynamic categories;
|
List<Category>? categories;
|
||||||
dynamic reactions;
|
|
||||||
List<Post>? replies;
|
List<Post>? replies;
|
||||||
List<int>? attachments;
|
List<int>? attachments;
|
||||||
int? replyId;
|
int? replyId;
|
||||||
@ -35,7 +35,6 @@ class Post {
|
|||||||
required this.content,
|
required this.content,
|
||||||
required this.tags,
|
required this.tags,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
required this.reactions,
|
|
||||||
required this.replies,
|
required this.replies,
|
||||||
required this.attachments,
|
required this.attachments,
|
||||||
required this.replyId,
|
required this.replyId,
|
||||||
@ -61,9 +60,11 @@ class Post {
|
|||||||
: null,
|
: null,
|
||||||
alias: json['alias'],
|
alias: json['alias'],
|
||||||
content: json['content'],
|
content: json['content'],
|
||||||
tags: json['tags'],
|
tags: json['tags']?.map((x) => Tag.fromJson(x)).toList().cast<Tag>(),
|
||||||
categories: json['categories'],
|
categories: json['categories']
|
||||||
reactions: json['reactions'],
|
?.map((x) => Category.fromJson(x))
|
||||||
|
.toList()
|
||||||
|
.cast<Category>(),
|
||||||
replies: json['replies'],
|
replies: json['replies'],
|
||||||
attachments: json['attachments'] != null
|
attachments: json['attachments'] != null
|
||||||
? List<int>.from(json['attachments'])
|
? List<int>.from(json['attachments'])
|
||||||
@ -76,7 +77,9 @@ class Post {
|
|||||||
repostTo:
|
repostTo:
|
||||||
json['repost_to'] != null ? Post.fromJson(json['repost_to']) : null,
|
json['repost_to'] != null ? Post.fromJson(json['repost_to']) : null,
|
||||||
realm: json['realm'] != null ? Realm.fromJson(json['realm']) : null,
|
realm: json['realm'] != null ? Realm.fromJson(json['realm']) : null,
|
||||||
publishedAt: json['published_at'] != null ? DateTime.parse(json['published_at']) : null,
|
publishedAt: json['published_at'] != null
|
||||||
|
? DateTime.parse(json['published_at'])
|
||||||
|
: null,
|
||||||
authorId: json['author_id'],
|
authorId: json['author_id'],
|
||||||
author: Account.fromJson(json['author']),
|
author: Account.fromJson(json['author']),
|
||||||
replyCount: json['reply_count'],
|
replyCount: json['reply_count'],
|
||||||
@ -100,7 +103,6 @@ class Post {
|
|||||||
'content': content,
|
'content': content,
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
'categories': categories,
|
'categories': categories,
|
||||||
'reactions': reactions,
|
|
||||||
'replies': replies,
|
'replies': replies,
|
||||||
'attachments': attachments,
|
'attachments': attachments,
|
||||||
'reply_id': replyId,
|
'reply_id': replyId,
|
||||||
|
@ -248,6 +248,8 @@ class _PostPublishingScreenState extends State<PostPublishingScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
TagsField(
|
TagsField(
|
||||||
|
initialTags:
|
||||||
|
widget.edit?.tags?.map((x) => x.alias).toList(),
|
||||||
tagsController: _tagsController,
|
tagsController: _tagsController,
|
||||||
hintText: 'postTagsPlaceholder'.tr,
|
hintText: 'postTagsPlaceholder'.tr,
|
||||||
),
|
),
|
||||||
|
45
lib/widgets/posts/feed_tags.dart
Normal file
45
lib/widgets/posts/feed_tags.dart
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:solian/models/feed.dart';
|
||||||
|
|
||||||
|
class FeedTagsList extends StatelessWidget {
|
||||||
|
final List<Tag> tags;
|
||||||
|
|
||||||
|
const FeedTagsList({
|
||||||
|
super.key,
|
||||||
|
required this.tags,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const borderRadius = BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
spacing: 6,
|
||||||
|
children: tags
|
||||||
|
.map(
|
||||||
|
(x) => GestureDetector(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4.0),
|
||||||
|
child: Text(
|
||||||
|
'#${x.alias}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import 'package:solian/router.dart';
|
|||||||
import 'package:solian/widgets/account/account_avatar.dart';
|
import 'package:solian/widgets/account/account_avatar.dart';
|
||||||
import 'package:solian/widgets/account/account_profile_popup.dart';
|
import 'package:solian/widgets/account/account_profile_popup.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_list.dart';
|
import 'package:solian/widgets/attachments/attachment_list.dart';
|
||||||
|
import 'package:solian/widgets/posts/feed_tags.dart';
|
||||||
import 'package:solian/widgets/posts/post_quick_action.dart';
|
import 'package:solian/widgets/posts/post_quick_action.dart';
|
||||||
import 'package:timeago/timeago.dart' show format;
|
import 'package:timeago/timeago.dart' show format;
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
@ -118,7 +119,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
|
||||||
),
|
),
|
||||||
).paddingOnly(top: 8);
|
).paddingOnly(top: 2);
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
@ -210,6 +211,9 @@ class _PostItemState extends State<PostItem> {
|
|||||||
bottom: hasAttachment ? 4 : 0,
|
bottom: hasAttachment ? 4 : 0,
|
||||||
),
|
),
|
||||||
buildFooter().paddingOnly(left: 16),
|
buildFooter().paddingOnly(left: 16),
|
||||||
|
if (widget.item.tags?.isNotEmpty ?? false)
|
||||||
|
FeedTagsList(tags: widget.item.tags!)
|
||||||
|
.paddingOnly(left: 12, top: 6, bottom: 2),
|
||||||
AttachmentList(
|
AttachmentList(
|
||||||
parentId: widget.overrideAttachmentParent ?? widget.item.alias,
|
parentId: widget.overrideAttachmentParent ?? widget.item.alias,
|
||||||
attachmentsId: item.attachments ?? List.empty(),
|
attachmentsId: item.attachments ?? List.empty(),
|
||||||
@ -220,6 +224,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -271,6 +276,9 @@ class _PostItemState extends State<PostItem> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
buildFooter().paddingOnly(left: 12),
|
buildFooter().paddingOnly(left: 12),
|
||||||
|
if (widget.item.tags?.isNotEmpty ?? false)
|
||||||
|
FeedTagsList(tags: widget.item.tags!)
|
||||||
|
.paddingOnly(left: 4, top: 6, bottom: 2),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:textfield_tags/textfield_tags.dart';
|
import 'package:textfield_tags/textfield_tags.dart';
|
||||||
|
|
||||||
class TagsField extends StatelessWidget {
|
class TagsField extends StatelessWidget {
|
||||||
|
final List<String>? initialTags;
|
||||||
final String hintText;
|
final String hintText;
|
||||||
|
|
||||||
const TagsField({
|
const TagsField({
|
||||||
super.key,
|
super.key,
|
||||||
|
this.initialTags,
|
||||||
required this.hintText,
|
required this.hintText,
|
||||||
required StringTagController<String> tagsController,
|
required StringTagController<String> tagsController,
|
||||||
}) : _tagsController = tagsController;
|
}) : _tagsController = tagsController;
|
||||||
@ -20,6 +22,7 @@ class TagsField extends StatelessWidget {
|
|||||||
vertical: 8,
|
vertical: 8,
|
||||||
),
|
),
|
||||||
child: TextFieldTags<String>(
|
child: TextFieldTags<String>(
|
||||||
|
initialTags: initialTags,
|
||||||
letterCase: LetterCase.small,
|
letterCase: LetterCase.small,
|
||||||
textfieldTagsController: _tagsController,
|
textfieldTagsController: _tagsController,
|
||||||
textSeparators: const [' ', ','],
|
textSeparators: const [' ', ','],
|
||||||
@ -39,44 +42,45 @@ class TagsField extends StatelessWidget {
|
|||||||
controller: inputFieldValues.tagScrollController,
|
controller: inputFieldValues.tagScrollController,
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: inputFieldValues.tags.map((String tag) {
|
children: inputFieldValues.tags.map((String tag) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.all(
|
borderRadius: const BorderRadius.all(
|
||||||
Radius.circular(20.0),
|
Radius.circular(20.0),
|
||||||
),
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.only(right: 10.0),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10.0, vertical: 4.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
InkWell(
|
|
||||||
child: Text(
|
|
||||||
'#$tag',
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
//print("$tag selected");
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4.0),
|
color: Theme.of(context).colorScheme.primary,
|
||||||
InkWell(
|
),
|
||||||
child: const Icon(
|
margin: const EdgeInsets.only(right: 10.0),
|
||||||
Icons.cancel,
|
padding: const EdgeInsets.symmetric(
|
||||||
size: 14.0,
|
horizontal: 10.0, vertical: 4.0),
|
||||||
color: Color.fromARGB(255, 233, 233, 233),
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
child: Text(
|
||||||
|
'#$tag',
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
//print("$tag selected");
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onTap: () {
|
const SizedBox(width: 4.0),
|
||||||
inputFieldValues.onTagRemoved(tag);
|
InkWell(
|
||||||
},
|
child: const Icon(
|
||||||
)
|
Icons.cancel,
|
||||||
],
|
size: 14.0,
|
||||||
),
|
color: Color.fromARGB(255, 233, 233, 233),
|
||||||
);
|
),
|
||||||
}).toList()),
|
onTap: () {
|
||||||
|
inputFieldValues.onTagRemoved(tag);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user