Articles writing

This commit is contained in:
LittleSheep 2024-07-09 23:06:55 +08:00
parent fa600d6c69
commit 065cda27e9
5 changed files with 176 additions and 168 deletions

View File

@ -12,6 +12,7 @@ import 'package:solian/widgets/attachments/attachment_publish.dart';
import 'package:solian/widgets/feed/feed_tags_field.dart'; import 'package:solian/widgets/feed/feed_tags_field.dart';
import 'package:solian/widgets/prev_page.dart'; import 'package:solian/widgets/prev_page.dart';
import 'package:textfield_tags/textfield_tags.dart'; import 'package:textfield_tags/textfield_tags.dart';
import 'package:badges/badges.dart' as badges;
class ArticlePublishArguments { class ArticlePublishArguments {
final Post? edit; final Post? edit;
@ -44,6 +45,8 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
List<int> _attachments = List.empty(); List<int> _attachments = List.empty();
bool _isDraft = false;
void showAttachments() { void showAttachments() {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -51,7 +54,9 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
builder: (context) => AttachmentPublishPopup( builder: (context) => AttachmentPublishPopup(
usage: 'i.attachment', usage: 'i.attachment',
current: _attachments, current: _attachments,
onUpdate: (value) => _attachments = value, onUpdate: (value) {
setState(() => _attachments = value);
},
), ),
); );
} }
@ -72,6 +77,7 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
'tags': _tagsController.getTags?.map((x) => {'alias': x}).toList() ?? 'tags': _tagsController.getTags?.map((x) => {'alias': x}).toList() ??
List.empty(), List.empty(),
'attachments': _attachments, 'attachments': _attachments,
'is_draft': _isDraft,
if (widget.edit != null) 'alias': widget.edit!.alias, if (widget.edit != null) 'alias': widget.edit!.alias,
if (widget.realm != null) 'realm': widget.realm!.alias, if (widget.realm != null) 'realm': widget.realm!.alias,
}; };
@ -95,6 +101,7 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
if (widget.edit != null) { if (widget.edit != null) {
_contentController.text = widget.edit!.content; _contentController.text = widget.edit!.content;
_attachments = widget.edit!.attachments ?? List.empty(); _attachments = widget.edit!.attachments ?? List.empty();
_isDraft = widget.edit!.isDraft ?? false;
} }
} }
@ -128,23 +135,23 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
actions: [ actions: [
TextButton( TextButton(
onPressed: _isBusy ? null : () => applyPost(), onPressed: _isBusy ? null : () => applyPost(),
child: Text('postAction'.tr.toUpperCase()), child: Text(
_isDraft
? 'draftSave'.tr.toUpperCase()
: 'postAction'.tr.toUpperCase(),
),
) )
], ],
), ),
body: SafeArea( body: Stack(
top: false,
child: Stack(
children: [ children: [
ListView( ListView(
children: [ children: [
if (_isBusy) if (_isBusy) const LinearProgressIndicator().animate().scaleX(),
const LinearProgressIndicator().animate().scaleX(),
if (widget.edit != null) if (widget.edit != null)
MaterialBanner( MaterialBanner(
leading: const Icon(Icons.edit), leading: const Icon(Icons.edit),
leadingPadding: leadingPadding: const EdgeInsets.only(left: 10, right: 20),
const EdgeInsets.only(left: 10, right: 20),
dividerColor: Colors.transparent, dividerColor: Colors.transparent,
content: Text('postEditingNotify'.tr), content: Text('postEditingNotify'.tr),
actions: notifyBannerActions, actions: notifyBannerActions,
@ -152,8 +159,7 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
if (widget.realm != null) if (widget.realm != null)
MaterialBanner( MaterialBanner(
leading: const Icon(Icons.group), leading: const Icon(Icons.group),
leadingPadding: leadingPadding: const EdgeInsets.only(left: 10, right: 20),
const EdgeInsets.only(left: 10, right: 20),
dividerColor: Colors.transparent, dividerColor: Colors.transparent,
content: Text( content: Text(
'postInRealmNotify' 'postInRealmNotify'
@ -170,7 +176,6 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
maxLines: null, maxLines: null,
autofocus: true, autofocus: true,
autocorrect: true, autocorrect: true,
keyboardType: TextInputType.multiline,
controller: _titleController, controller: _titleController,
decoration: InputDecoration.collapsed( decoration: InputDecoration.collapsed(
hintText: 'articleTitlePlaceholder'.tr, hintText: 'articleTitlePlaceholder'.tr,
@ -179,6 +184,25 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
), ),
), ),
const Divider(thickness: 0.3, height: 0.3)
.paddingOnly(bottom: 6),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: TextField(
minLines: 1,
maxLines: 3,
autofocus: true,
autocorrect: true,
keyboardType: TextInputType.multiline,
controller: _descriptionController,
decoration: InputDecoration.collapsed(
hintText: 'articleDescriptionPlaceholder'.tr,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Divider(thickness: 0.3, height: 0.3) const Divider(thickness: 0.3, height: 0.3)
.paddingSymmetric(vertical: 6), .paddingSymmetric(vertical: 6),
Container( Container(
@ -197,12 +221,16 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
), ),
), ),
const SizedBox(height: 120),
], ],
), ),
Positioned( Positioned(
bottom: 0, bottom: 0,
left: 0, left: 0,
right: 0, right: 0,
child: Material(
elevation: 8,
color: Theme.of(context).colorScheme.surface,
child: Column( child: Column(
children: [ children: [
TagsField( TagsField(
@ -214,25 +242,46 @@ class _ArticlePublishScreenState extends State<ArticlePublishScreen> {
const Divider(thickness: 0.3, height: 0.3), const Divider(thickness: 0.3, height: 0.3),
SizedBox( SizedBox(
height: 56, height: 56,
child: Row( child: ListView(
scrollDirection: Axis.horizontal,
children: [ children: [
TextButton( IconButton(
style: TextButton.styleFrom( icon: _isDraft
shape: const CircleBorder(), ? const Icon(Icons.drive_file_rename_outline)
: const Icon(Icons.public),
color: _isDraft
? Colors.grey.shade600
: Colors.green.shade700,
onPressed: () {
setState(() => _isDraft = !_isDraft);
},
),
IconButton(
icon: badges.Badge(
badgeContent: Text(
_attachments.length.toString(),
style: const TextStyle(color: Colors.white),
),
showBadge: _attachments.isNotEmpty,
position: badges.BadgePosition.topEnd(
top: -12,
end: -8,
), ),
child: const Icon(Icons.camera_alt), child: const Icon(Icons.camera_alt),
),
color: Theme.of(context).colorScheme.primary,
onPressed: () => showAttachments(), onPressed: () => showAttachments(),
) ),
], ],
).paddingSymmetric(horizontal: 6, vertical: 8),
),
],
).paddingOnly(bottom: MediaQuery.of(context).padding.bottom),
), ),
), ),
], ],
), ),
), ),
],
),
),
),
); );
} }

View File

@ -60,7 +60,9 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
builder: (context) => AttachmentPublishPopup( builder: (context) => AttachmentPublishPopup(
usage: 'i.attachment', usage: 'i.attachment',
current: _attachments, current: _attachments,
onUpdate: (value) => _attachments = value, onUpdate: (value) {
setState(() => _attachments = value);
},
), ),
); );
} }
@ -261,14 +263,22 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
setState(() => _isDraft = !_isDraft); setState(() => _isDraft = !_isDraft);
}, },
), ),
badges.Badge( IconButton(
badgeContent: Text(_attachments.length.toString()), icon: badges.Badge(
badgeContent: Text(
_attachments.length.toString(),
style: const TextStyle(color: Colors.white),
),
showBadge: _attachments.isNotEmpty, showBadge: _attachments.isNotEmpty,
child: IconButton( position: badges.BadgePosition.topEnd(
icon: const Icon(Icons.camera_alt), top: -12,
end: -8,
),
child: const Icon(Icons.camera_alt),
),
color: Theme.of(context).colorScheme.primary,
onPressed: () => showAttachments(), onPressed: () => showAttachments(),
), ),
)
], ],
).paddingSymmetric(horizontal: 6, vertical: 8), ).paddingSymmetric(horizontal: 6, vertical: 8),
), ),

View File

@ -91,6 +91,7 @@ const messagesEnglish = {
'postPublish': 'Post a post', 'postPublish': 'Post a post',
'articlePublish': 'Write an article', 'articlePublish': 'Write an article',
'articleTitlePlaceholder': 'Title', 'articleTitlePlaceholder': 'Title',
'articleDescriptionPlaceholder': 'Description',
'articleContentPlaceholder': 'Content', 'articleContentPlaceholder': 'Content',
'postIdentityNotify': 'You will post this post as', 'postIdentityNotify': 'You will post this post as',
'postContentPlaceholder': 'What\'s happened?!', 'postContentPlaceholder': 'What\'s happened?!',

View File

@ -275,6 +275,7 @@ class _PostItemState extends State<PostItem> {
attachmentsId: item.attachments ?? List.empty(), attachmentsId: item.attachments ?? List.empty(),
divided: true, divided: true,
), ),
if (!widget.isShowReply && widget.isReactable)
PostQuickAction( PostQuickAction(
isShowReply: widget.isShowReply, isShowReply: widget.isShowReply,
isReactable: widget.isReactable, isReactable: widget.isReactable,
@ -290,7 +291,9 @@ class _PostItemState extends State<PostItem> {
left: hasAttachment ? 24 : 60, left: hasAttachment ? 24 : 60,
right: 16, right: 16,
bottom: 10, bottom: 10,
), )
else
const SizedBox(height: 10),
], ],
); );
} }

View File

@ -1,9 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:solian/models/post.dart'; import 'package:solian/models/post.dart';
import 'package:solian/widgets/feed/feed_content.dart'; import 'package:solian/widgets/posts/post_item.dart';
import 'package:solian/widgets/feed/feed_tags.dart';
class PostOwnedListEntry extends StatelessWidget { class PostOwnedListEntry extends StatelessWidget {
final Post item; final Post item;
@ -15,61 +13,6 @@ class PostOwnedListEntry extends StatelessWidget {
required this.onTap, required this.onTap,
}); });
Widget buildFooter(BuildContext context) {
List<String> labels = List.empty(growable: true);
if (item.createdAt == item.updatedAt) {
labels.add('postNewCreated'.trParams({
'date': DateFormat('yyyy/MM/dd HH:mm').format(item.updatedAt.toLocal()),
}));
} else {
labels.add('postEdited'.trParams({
'date': DateFormat('yyyy/MM/dd HH:mm').format(item.updatedAt.toLocal()),
}));
}
if (item.realm != null) {
labels.add('postInRealm'.trParams({
'realm': '#${item.realm!.alias}',
}));
}
final color = Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
List<Widget> widgets = List.from([
Row(
children: [
Text(
'post'.tr,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 12,
color: color,
),
),
Icon(Icons.text_snippet, size: 14, color: color).paddingOnly(left: 4),
],
),
], growable: true);
if (item.tags?.isNotEmpty ?? false) {
widgets.add(FeedTagsList(tags: item.tags!));
}
if (labels.isNotEmpty) {
widgets.add(Text(
labels.join(' · '),
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 12,
color: color,
),
));
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widgets,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
@ -78,12 +21,14 @@ class PostOwnedListEntry extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
FeedContent(content: item.content).paddingOnly( PostItem(
left: 12, key: Key('p${item.alias}'),
right: 12, item: item,
top: 8, isShowEmbed: false,
), isClickable: false,
buildFooter(context).paddingOnly(left: 12, top: 6, bottom: 8), isShowReply: false,
isReactable: false,
).paddingSymmetric(vertical: 8),
], ],
), ),
onTap: () => onTap(), onTap: () => onTap(),