Compare commits
2 Commits
fe028860e9
...
a97c3bce3a
Author | SHA1 | Date | |
---|---|---|---|
a97c3bce3a | |||
1aa70827dc |
@ -154,9 +154,11 @@
|
|||||||
"fieldPublisherBelongToRealmUnset": "Unset Publisher Belongs to Realm",
|
"fieldPublisherBelongToRealmUnset": "Unset Publisher Belongs to Realm",
|
||||||
"writePostTypeStory": "Post a story",
|
"writePostTypeStory": "Post a story",
|
||||||
"writePostTypeArticle": "Write an article",
|
"writePostTypeArticle": "Write an article",
|
||||||
|
"writePostTypeQuestion": "Ask a question",
|
||||||
"fieldPostPublisher": "Post publisher",
|
"fieldPostPublisher": "Post publisher",
|
||||||
"fieldPostContent": "What happened?!",
|
"fieldPostContent": "What happened?!",
|
||||||
"fieldPostTitle": "Title",
|
"fieldPostTitle": "Title",
|
||||||
|
"fieldPostQuestionReward": "Answer Rewards (Source Points)",
|
||||||
"fieldPostDescription": "Description",
|
"fieldPostDescription": "Description",
|
||||||
"fieldPostTags": "Tags",
|
"fieldPostTags": "Tags",
|
||||||
"fieldPostCategories": "Categories",
|
"fieldPostCategories": "Categories",
|
||||||
@ -610,5 +612,10 @@
|
|||||||
},
|
},
|
||||||
"aiThinkingProcess": "AI Thinking Process",
|
"aiThinkingProcess": "AI Thinking Process",
|
||||||
"accountSettingsApplied": "Account settings have been applied.",
|
"accountSettingsApplied": "Account settings have been applied.",
|
||||||
"trayMenuExit": "Exit"
|
"trayMenuExit": "Exit",
|
||||||
|
"postQuestionUnanswered": "Unanswered Question",
|
||||||
|
"postQuestionUnansweredWithReward": "Unanswered Question, reward source points {}",
|
||||||
|
"postQuestionAnswered": "Answered Question",
|
||||||
|
"postQuestionAnswerSelect": "Select as Answer",
|
||||||
|
"postQuestionAnswerSelected": "Answer has been selected, reward has been applied."
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,11 @@
|
|||||||
"fieldPublisherBelongToRealmUnset": "未设置发布者所属领域",
|
"fieldPublisherBelongToRealmUnset": "未设置发布者所属领域",
|
||||||
"writePostTypeStory": "发动态",
|
"writePostTypeStory": "发动态",
|
||||||
"writePostTypeArticle": "写文章",
|
"writePostTypeArticle": "写文章",
|
||||||
|
"writePostTypeQuestion": "提问题",
|
||||||
"fieldPostPublisher": "帖子发布者",
|
"fieldPostPublisher": "帖子发布者",
|
||||||
"fieldPostContent": "发生什么事了?!",
|
"fieldPostContent": "发生什么事了?!",
|
||||||
"fieldPostTitle": "标题",
|
"fieldPostTitle": "标题",
|
||||||
|
"fieldPostQuestionReward": "回答奖励源点",
|
||||||
"fieldPostDescription": "描述",
|
"fieldPostDescription": "描述",
|
||||||
"fieldPostTags": "标签",
|
"fieldPostTags": "标签",
|
||||||
"fieldPostCategories": "分类",
|
"fieldPostCategories": "分类",
|
||||||
@ -608,5 +610,11 @@
|
|||||||
},
|
},
|
||||||
"aiThinkingProcess": "AI 思考过程",
|
"aiThinkingProcess": "AI 思考过程",
|
||||||
"accountSettingsApplied": "帐号设置已应用。",
|
"accountSettingsApplied": "帐号设置已应用。",
|
||||||
"trayMenuExit": "退出"
|
"trayMenuExit": "退出",
|
||||||
|
"postQuestionUnanswered": "未解答的问题",
|
||||||
|
"postQuestionUnansweredWithReward": "未解答的问题,悬赏源点 {}",
|
||||||
|
"postQuestionAnswered": "已解答的问题",
|
||||||
|
"postQuestionAnswerTitle": "精选解答",
|
||||||
|
"postQuestionAnswerSelect": "选择解答",
|
||||||
|
"postQuestionAnswerSelected": "解答已选择,奖励已发放。"
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,11 @@
|
|||||||
"fieldPublisherBelongToRealmUnset": "未設置發佈者所屬領域",
|
"fieldPublisherBelongToRealmUnset": "未設置發佈者所屬領域",
|
||||||
"writePostTypeStory": "發動態",
|
"writePostTypeStory": "發動態",
|
||||||
"writePostTypeArticle": "寫文章",
|
"writePostTypeArticle": "寫文章",
|
||||||
|
"writePostTypeQuestion": "提問題",
|
||||||
"fieldPostPublisher": "帖子發佈者",
|
"fieldPostPublisher": "帖子發佈者",
|
||||||
"fieldPostContent": "發生什麼事了?!",
|
"fieldPostContent": "發生什麼事了?!",
|
||||||
"fieldPostTitle": "標題",
|
"fieldPostTitle": "標題",
|
||||||
|
"fieldPostQuestionReward": "回答獎勵源點",
|
||||||
"fieldPostDescription": "描述",
|
"fieldPostDescription": "描述",
|
||||||
"fieldPostTags": "標籤",
|
"fieldPostTags": "標籤",
|
||||||
"fieldPostCategories": "分類",
|
"fieldPostCategories": "分類",
|
||||||
@ -607,5 +609,12 @@
|
|||||||
"other": "{} 源點"
|
"other": "{} 源點"
|
||||||
},
|
},
|
||||||
"aiThinkingProcess": "AI 思考過程",
|
"aiThinkingProcess": "AI 思考過程",
|
||||||
"accountSettingsApplied": "帳號設置已應用。"
|
"accountSettingsApplied": "帳號設置已應用。",
|
||||||
|
"trayMenuExit": "退出",
|
||||||
|
"postQuestionUnanswered": "未解答的問題",
|
||||||
|
"postQuestionUnansweredWithReward": "未解答的問題,懸賞源點 {}",
|
||||||
|
"postQuestionAnswered": "已解答的問題",
|
||||||
|
"postQuestionAnswerTitle": "精選解答",
|
||||||
|
"postQuestionAnswerSelect": "選擇解答",
|
||||||
|
"postQuestionAnswerSelected": "解答已選擇,獎勵已發放。"
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,11 @@
|
|||||||
"fieldPublisherBelongToRealmUnset": "未設置發佈者所屬領域",
|
"fieldPublisherBelongToRealmUnset": "未設置發佈者所屬領域",
|
||||||
"writePostTypeStory": "發動態",
|
"writePostTypeStory": "發動態",
|
||||||
"writePostTypeArticle": "寫文章",
|
"writePostTypeArticle": "寫文章",
|
||||||
|
"writePostTypeQuestion": "提問題",
|
||||||
"fieldPostPublisher": "帖子發佈者",
|
"fieldPostPublisher": "帖子發佈者",
|
||||||
"fieldPostContent": "發生什麼事了?!",
|
"fieldPostContent": "發生什麼事了?!",
|
||||||
"fieldPostTitle": "標題",
|
"fieldPostTitle": "標題",
|
||||||
|
"fieldPostQuestionReward": "回答獎勵源點",
|
||||||
"fieldPostDescription": "描述",
|
"fieldPostDescription": "描述",
|
||||||
"fieldPostTags": "標籤",
|
"fieldPostTags": "標籤",
|
||||||
"fieldPostCategories": "分類",
|
"fieldPostCategories": "分類",
|
||||||
@ -607,5 +609,12 @@
|
|||||||
"other": "{} 源點"
|
"other": "{} 源點"
|
||||||
},
|
},
|
||||||
"aiThinkingProcess": "AI 思考過程",
|
"aiThinkingProcess": "AI 思考過程",
|
||||||
"accountSettingsApplied": "帳號設置已應用。"
|
"accountSettingsApplied": "帳號設置已應用。",
|
||||||
|
"trayMenuExit": "退出",
|
||||||
|
"postQuestionUnanswered": "未解答的問題",
|
||||||
|
"postQuestionUnansweredWithReward": "未解答的問題,懸賞源點 {}",
|
||||||
|
"postQuestionAnswered": "已解答的問題",
|
||||||
|
"postQuestionAnswerTitle": "精選解答",
|
||||||
|
"postQuestionAnswerSelect": "選擇解答",
|
||||||
|
"postQuestionAnswerSelected": "解答已選擇,獎勵已發放。"
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
static const Map<String, String> kTitleMap = {
|
static const Map<String, String> kTitleMap = {
|
||||||
'stories': 'writePostTypeStory',
|
'stories': 'writePostTypeStory',
|
||||||
'articles': 'writePostTypeArticle',
|
'articles': 'writePostTypeArticle',
|
||||||
|
'questions': 'writePostTypeQuestion',
|
||||||
};
|
};
|
||||||
|
|
||||||
static const kAttachmentProgressWeight = 0.9;
|
static const kAttachmentProgressWeight = 0.9;
|
||||||
@ -153,6 +154,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
final TextEditingController titleController = TextEditingController();
|
final TextEditingController titleController = TextEditingController();
|
||||||
final TextEditingController descriptionController = TextEditingController();
|
final TextEditingController descriptionController = TextEditingController();
|
||||||
final TextEditingController aliasController = TextEditingController();
|
final TextEditingController aliasController = TextEditingController();
|
||||||
|
final TextEditingController rewardController = TextEditingController();
|
||||||
|
|
||||||
bool _temporarySaveActive = false;
|
bool _temporarySaveActive = false;
|
||||||
|
|
||||||
@ -215,6 +217,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
descriptionController.text = post.body['description'] ?? '';
|
descriptionController.text = post.body['description'] ?? '';
|
||||||
contentController.text = post.body['content'] ?? '';
|
contentController.text = post.body['content'] ?? '';
|
||||||
aliasController.text = post.alias ?? '';
|
aliasController.text = post.alias ?? '';
|
||||||
|
rewardController.text = post.body['reward']?.toString() ?? '';
|
||||||
publishedAt = post.publishedAt;
|
publishedAt = post.publishedAt;
|
||||||
publishedUntil = post.publishedUntil;
|
publishedUntil = post.publishedUntil;
|
||||||
visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
|
visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
|
||||||
@ -348,6 +351,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
if (aliasController.text.isNotEmpty) 'alias': aliasController.text,
|
if (aliasController.text.isNotEmpty) 'alias': aliasController.text,
|
||||||
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
if (titleController.text.isNotEmpty) 'title': titleController.text,
|
||||||
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
if (descriptionController.text.isNotEmpty) 'description': descriptionController.text,
|
||||||
|
if (rewardController.text.isNotEmpty) 'reward': rewardController.text,
|
||||||
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.toJson(),
|
if (thumbnail != null && thumbnail!.attachment != null) 'thumbnail': thumbnail!.attachment!.toJson(),
|
||||||
'attachments':
|
'attachments':
|
||||||
attachments.where((e) => e.attachment != null).map((e) => e.attachment!.toJson()).toList(growable: true),
|
attachments.where((e) => e.attachment != null).map((e) => e.attachment!.toJson()).toList(growable: true),
|
||||||
@ -376,6 +380,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
aliasController.text = data['alias'] ?? '';
|
aliasController.text = data['alias'] ?? '';
|
||||||
titleController.text = data['title'] ?? '';
|
titleController.text = data['title'] ?? '';
|
||||||
descriptionController.text = data['description'] ?? '';
|
descriptionController.text = data['description'] ?? '';
|
||||||
|
rewardController.text = data['reward']?.toString() ?? '';
|
||||||
if (data['thumbnail'] != null) thumbnail = PostWriteMedia(SnAttachment.fromJson(data['thumbnail']));
|
if (data['thumbnail'] != null) thumbnail = PostWriteMedia(SnAttachment.fromJson(data['thumbnail']));
|
||||||
attachments
|
attachments
|
||||||
.addAll(data['attachments'].map((ele) => PostWriteMedia(SnAttachment.fromJson(ele))).cast<PostWriteMedia>());
|
.addAll(data['attachments'].map((ele) => PostWriteMedia(SnAttachment.fromJson(ele))).cast<PostWriteMedia>());
|
||||||
@ -474,6 +479,8 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
progress = kAttachmentProgressWeight;
|
progress = kAttachmentProgressWeight;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
|
final reward = double.tryParse(rewardController.text);
|
||||||
|
|
||||||
// Posting the content
|
// Posting the content
|
||||||
try {
|
try {
|
||||||
final baseProgressVal = progress!;
|
final baseProgressVal = progress!;
|
||||||
@ -499,6 +506,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
if (publishedUntil != null) 'published_until': publishedAt!.toUtc().toIso8601String(),
|
if (publishedUntil != null) 'published_until': publishedAt!.toUtc().toIso8601String(),
|
||||||
if (replyingPost != null) 'reply_to': replyingPost!.id,
|
if (replyingPost != null) 'reply_to': replyingPost!.id,
|
||||||
if (repostingPost != null) 'repost_to': repostingPost!.id,
|
if (repostingPost != null) 'repost_to': repostingPost!.id,
|
||||||
|
if (reward != null) 'reward': reward,
|
||||||
},
|
},
|
||||||
onSendProgress: (count, total) {
|
onSendProgress: (count, total) {
|
||||||
progress = baseProgressVal + (count / total) * (kPostingProgressWeight / 2);
|
progress = baseProgressVal + (count / total) * (kPostingProgressWeight / 2);
|
||||||
|
@ -166,6 +166,27 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text('writePostTypeQuestion').tr(),
|
||||||
|
const Gap(20),
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: null,
|
||||||
|
tooltip: 'writePostTypeQuestion'.tr(),
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).pushNamed('postEditor', pathParameters: {
|
||||||
|
'mode': 'questions',
|
||||||
|
}).then((value) {
|
||||||
|
if (value == true) {
|
||||||
|
_refreshPosts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_fabKey.currentState!.toggle();
|
||||||
|
},
|
||||||
|
child: const Icon(Symbols.question_answer),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
|
@ -183,7 +183,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
if (_data != null)
|
if (_data != null)
|
||||||
PostCommentSliverList(
|
PostCommentSliverList(
|
||||||
key: _childListKey,
|
key: _childListKey,
|
||||||
parentPostId: _data!.id,
|
parentPost: _data!,
|
||||||
maxWidth: 640,
|
maxWidth: 640,
|
||||||
),
|
),
|
||||||
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:pasteboard/pasteboard.dart';
|
import 'package:pasteboard/pasteboard.dart';
|
||||||
@ -23,7 +21,6 @@ import 'package:surface/widgets/account/account_image.dart';
|
|||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/markdown_content.dart';
|
import 'package:surface/widgets/markdown_content.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
|
||||||
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
||||||
import 'package:surface/widgets/post/post_meta_editor.dart';
|
import 'package:surface/widgets/post/post_meta_editor.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
@ -242,6 +239,10 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
controller: _writeController,
|
controller: _writeController,
|
||||||
onTapPublisher: _showPublisherPopup,
|
onTapPublisher: _showPublisherPopup,
|
||||||
),
|
),
|
||||||
|
'questions' => _PostQuestionEditor(
|
||||||
|
controller: _writeController,
|
||||||
|
onTapPublisher: _showPublisherPopup,
|
||||||
|
),
|
||||||
_ => const Placeholder(),
|
_ => const Placeholder(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -438,12 +439,13 @@ class _PostStoryEditor extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
constraints: const BoxConstraints(maxWidth: 640),
|
constraints: const BoxConstraints(maxWidth: 640),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Material(
|
Material(
|
||||||
elevation: 1,
|
elevation: 2,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -455,7 +457,20 @@ class _PostStoryEditor extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Gap(6),
|
||||||
|
TextField(
|
||||||
|
controller: controller.titleController,
|
||||||
|
decoration: InputDecoration.collapsed(
|
||||||
|
hintText: 'fieldPostTitle'.tr(),
|
||||||
|
border: InputBorder.none,
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
).padding(horizontal: 16),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
controller: controller.contentController,
|
controller: controller.contentController,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@ -468,10 +483,12 @@ class _PostStoryEditor extends StatelessWidget {
|
|||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
).padding(top: 4),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(bottom: 8),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -508,21 +525,21 @@ class _PostArticleEditor extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: controller.titleController,
|
controller: controller.titleController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration.collapsed(
|
||||||
labelText: 'fieldPostTitle'.tr(),
|
hintText: 'fieldPostTitle'.tr(),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
const Gap(4),
|
const Gap(8),
|
||||||
TextField(
|
TextField(
|
||||||
controller: controller.descriptionController,
|
controller: controller.descriptionController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration.collapsed(
|
||||||
labelText: 'fieldPostDescription'.tr(),
|
hintText: 'fieldPostDescription'.tr(),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
@ -530,7 +547,7 @@ class _PostArticleEditor extends StatelessWidget {
|
|||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
const Gap(8),
|
const Gap(4),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
|
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
|
||||||
@ -596,3 +613,77 @@ class _PostArticleEditor extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PostQuestionEditor extends StatelessWidget {
|
||||||
|
final PostWriteController controller;
|
||||||
|
final Function? onTapPublisher;
|
||||||
|
|
||||||
|
const _PostQuestionEditor({required this.controller, this.onTapPublisher});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
constraints: const BoxConstraints(maxWidth: 640),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Material(
|
||||||
|
elevation: 1,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
onTapPublisher?.call();
|
||||||
|
},
|
||||||
|
child: AccountImage(
|
||||||
|
content: controller.publisher?.avatar,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Gap(6),
|
||||||
|
TextField(
|
||||||
|
controller: controller.titleController,
|
||||||
|
decoration: InputDecoration.collapsed(
|
||||||
|
hintText: 'fieldPostTitle'.tr(),
|
||||||
|
border: InputBorder.none,
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
).padding(horizontal: 16),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
|
controller: controller.rewardController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'fieldPostQuestionReward'.tr(),
|
||||||
|
suffixText: 'walletCurrencyShort'.tr(),
|
||||||
|
border: InputBorder.none,
|
||||||
|
isCollapsed: true,
|
||||||
|
),
|
||||||
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
).padding(horizontal: 16),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
|
controller: controller.contentController,
|
||||||
|
maxLines: null,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'fieldPostContent'.tr(),
|
||||||
|
hintStyle: TextStyle(fontSize: 14),
|
||||||
|
isCollapsed: true,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
border: InputBorder.none,
|
||||||
|
),
|
||||||
|
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(top: 8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,17 +8,23 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'package:surface/providers/post.dart';
|
import 'package:surface/providers/post.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
import 'package:surface/widgets/post/post_item.dart';
|
||||||
import 'package:surface/widgets/post/post_mini_editor.dart';
|
import 'package:surface/widgets/post/post_mini_editor.dart';
|
||||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
|
|
||||||
|
import '../../providers/sn_network.dart';
|
||||||
|
|
||||||
class PostCommentSliverList extends StatefulWidget {
|
class PostCommentSliverList extends StatefulWidget {
|
||||||
final int parentPostId;
|
final SnPost parentPost;
|
||||||
final double? maxWidth;
|
final double? maxWidth;
|
||||||
|
final Function(SnPost)? onSelectAnswer;
|
||||||
|
|
||||||
const PostCommentSliverList({
|
const PostCommentSliverList({
|
||||||
super.key,
|
super.key,
|
||||||
required this.parentPostId,
|
required this.parentPost,
|
||||||
this.maxWidth,
|
this.maxWidth,
|
||||||
|
this.onSelectAnswer,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -37,7 +43,7 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
|
|||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
final pt = context.read<SnPostContentProvider>();
|
final pt = context.read<SnPostContentProvider>();
|
||||||
final result = await pt.listPostReplies(widget.parentPostId);
|
final result = await pt.listPostReplies(widget.parentPost.id);
|
||||||
final List<SnPost> out = result.$1;
|
final List<SnPost> out = result.$1;
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -48,6 +54,21 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
|
|||||||
if (mounted) setState(() => _isBusy = false);
|
if (mounted) setState(() => _isBusy = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _selectAnswer(SnPost answer) async {
|
||||||
|
try {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
await sn.client.put('/cgi/co/questions/${widget.parentPost.id}/answer', data: {
|
||||||
|
'publisher': answer.publisherId,
|
||||||
|
'answer_id': answer.id,
|
||||||
|
});
|
||||||
|
if (!mounted) return;
|
||||||
|
await refresh();
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
_posts.clear();
|
_posts.clear();
|
||||||
_fetchPosts();
|
_fetchPosts();
|
||||||
@ -71,6 +92,7 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
|
|||||||
child: PostItem(
|
child: PostItem(
|
||||||
data: _posts[idx],
|
data: _posts[idx],
|
||||||
maxWidth: widget.maxWidth,
|
maxWidth: widget.maxWidth,
|
||||||
|
onSelectAnswer: widget.parentPost.type == 'question' ? () => _selectAnswer(_posts[idx]) : null,
|
||||||
onChanged: (data) {
|
onChanged: (data) {
|
||||||
setState(() => _posts[idx] = data);
|
setState(() => _posts[idx] = data);
|
||||||
},
|
},
|
||||||
@ -94,11 +116,12 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PostCommentListPopup extends StatefulWidget {
|
class PostCommentListPopup extends StatefulWidget {
|
||||||
final int postId;
|
final SnPost post;
|
||||||
final int commentCount;
|
final int commentCount;
|
||||||
|
|
||||||
const PostCommentListPopup({
|
const PostCommentListPopup({
|
||||||
super.key,
|
super.key,
|
||||||
required this.postId,
|
required this.post,
|
||||||
this.commentCount = 0,
|
this.commentCount = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -122,9 +145,7 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Symbols.comment, size: 24),
|
const Icon(Symbols.comment, size: 24),
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
Text('postCommentsDetailed')
|
Text('postCommentsDetailed').plural(widget.commentCount).textStyle(Theme.of(context).textTheme.titleLarge!),
|
||||||
.plural(widget.commentCount)
|
|
||||||
.textStyle(Theme.of(context).textTheme.titleLarge!),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, top: 16, bottom: 12),
|
).padding(horizontal: 20, top: 16, bottom: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -143,7 +164,7 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: PostMiniEditor(
|
child: PostMiniEditor(
|
||||||
postReplyId: widget.postId,
|
postReplyId: widget.post.id,
|
||||||
onPost: () {
|
onPost: () {
|
||||||
_childListKey.currentState!.refresh();
|
_childListKey.currentState!.refresh();
|
||||||
},
|
},
|
||||||
@ -151,8 +172,8 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PostCommentSliverList(
|
PostCommentSliverList(
|
||||||
|
parentPost: widget.post,
|
||||||
key: _childListKey,
|
key: _childListKey,
|
||||||
parentPostId: widget.postId,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -47,6 +47,7 @@ class PostItem extends StatelessWidget {
|
|||||||
final double? maxWidth;
|
final double? maxWidth;
|
||||||
final Function(SnPost data)? onChanged;
|
final Function(SnPost data)? onChanged;
|
||||||
final Function()? onDeleted;
|
final Function()? onDeleted;
|
||||||
|
final Function()? onSelectAnswer;
|
||||||
|
|
||||||
const PostItem({
|
const PostItem({
|
||||||
super.key,
|
super.key,
|
||||||
@ -58,6 +59,7 @@ class PostItem extends StatelessWidget {
|
|||||||
this.maxWidth,
|
this.maxWidth,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.onDeleted,
|
this.onDeleted,
|
||||||
|
this.onSelectAnswer,
|
||||||
});
|
});
|
||||||
|
|
||||||
void _onChanged(SnPost data) {
|
void _onChanged(SnPost data) {
|
||||||
@ -142,6 +144,7 @@ class PostItem extends StatelessWidget {
|
|||||||
isRelativeDate: !showFullPost,
|
isRelativeDate: !showFullPost,
|
||||||
onShare: () => _doShare(context),
|
onShare: () => _doShare(context),
|
||||||
onShareImage: () => _doShareViaPicture(context),
|
onShareImage: () => _doShareViaPicture(context),
|
||||||
|
onSelectAnswer: onSelectAnswer,
|
||||||
onDeleted: () {
|
onDeleted: () {
|
||||||
if (onDeleted != null) {}
|
if (onDeleted != null) {}
|
||||||
},
|
},
|
||||||
@ -224,10 +227,12 @@ class PostItem extends StatelessWidget {
|
|||||||
showMenu: showMenu,
|
showMenu: showMenu,
|
||||||
onShare: () => _doShare(context),
|
onShare: () => _doShare(context),
|
||||||
onShareImage: () => _doShareViaPicture(context),
|
onShareImage: () => _doShareViaPicture(context),
|
||||||
|
onSelectAnswer: onSelectAnswer,
|
||||||
onDeleted: () {
|
onDeleted: () {
|
||||||
if (onDeleted != null) onDeleted!();
|
if (onDeleted != null) onDeleted!();
|
||||||
},
|
},
|
||||||
).padding(horizontal: 12, vertical: 8),
|
).padding(horizontal: 12, vertical: 8),
|
||||||
|
if (data.type == 'question') _PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8),
|
||||||
if (data.body['title'] != null || data.body['description'] != null)
|
if (data.body['title'] != null || data.body['description'] != null)
|
||||||
_PostHeadline(
|
_PostHeadline(
|
||||||
data: data,
|
data: data,
|
||||||
@ -333,6 +338,7 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
showMenu: false,
|
showMenu: false,
|
||||||
isRelativeDate: false,
|
isRelativeDate: false,
|
||||||
).padding(horizontal: 16, bottom: 8),
|
).padding(horizontal: 16, bottom: 8),
|
||||||
|
if (data.type == 'question') _PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8),
|
||||||
_PostHeadline(
|
_PostHeadline(
|
||||||
data: data,
|
data: data,
|
||||||
isEnlarge: data.type == 'article',
|
isEnlarge: data.type == 'article',
|
||||||
@ -438,6 +444,30 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PostQuestionHint extends StatelessWidget {
|
||||||
|
final SnPost data;
|
||||||
|
|
||||||
|
const _PostQuestionHint({required this.data});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Icon(data.body['answer'] == null ? Symbols.help : Symbols.check_circle, size: 20),
|
||||||
|
const Gap(4),
|
||||||
|
if (data.body['answer'] == null && data.body['reward']?.toDouble() != null)
|
||||||
|
Text('postQuestionUnansweredWithReward'.tr(args: [
|
||||||
|
'${data.body['reward']}',
|
||||||
|
])).opacity(0.75)
|
||||||
|
else if (data.body['answer'] == null)
|
||||||
|
Text('postQuestionUnanswered'.tr()).opacity(0.75)
|
||||||
|
else
|
||||||
|
Text('postQuestionAnswered'.tr()).opacity(0.75),
|
||||||
|
],
|
||||||
|
).opacity(0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _PostBottomAction extends StatelessWidget {
|
class _PostBottomAction extends StatelessWidget {
|
||||||
final SnPost data;
|
final SnPost data;
|
||||||
final bool showComments;
|
final bool showComments;
|
||||||
@ -529,7 +559,7 @@ class _PostBottomAction extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
builder: (context) => PostCommentListPopup(
|
builder: (context) => PostCommentListPopup(
|
||||||
postId: data.id,
|
post: data,
|
||||||
commentCount: data.metric.replyCount,
|
commentCount: data.metric.replyCount,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -652,6 +682,7 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
final bool showMenu;
|
final bool showMenu;
|
||||||
final Function onDeleted;
|
final Function onDeleted;
|
||||||
final Function() onShare, onShareImage;
|
final Function() onShare, onShareImage;
|
||||||
|
final Function()? onSelectAnswer;
|
||||||
|
|
||||||
const _PostContentHeader({
|
const _PostContentHeader({
|
||||||
required this.data,
|
required this.data,
|
||||||
@ -662,6 +693,7 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
required this.onDeleted,
|
required this.onDeleted,
|
||||||
required this.onShare,
|
required this.onShare,
|
||||||
required this.onShareImage,
|
required this.onShareImage,
|
||||||
|
this.onSelectAnswer,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> _deletePost(BuildContext context) async {
|
Future<void> _deletePost(BuildContext context) async {
|
||||||
@ -760,6 +792,20 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
||||||
),
|
),
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||||
|
if (isAuthor && onSelectAnswer != null)
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.check_circle),
|
||||||
|
const Gap(16),
|
||||||
|
Text('postQuestionAnswerSelect').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onSelectAnswer?.call();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (isAuthor && onSelectAnswer != null) PopupMenuDivider(),
|
||||||
if (isAuthor)
|
if (isAuthor)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -1139,8 +1185,18 @@ class _PostFeaturedComment extends StatefulWidget {
|
|||||||
|
|
||||||
class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
||||||
SnPost? _featuredComment;
|
SnPost? _featuredComment;
|
||||||
|
bool _isAnswer = false;
|
||||||
|
|
||||||
Future<void> _fetchComments() async {
|
Future<void> _fetchComments() async {
|
||||||
|
// If this is a answered question, fetch the answer instead
|
||||||
|
if (widget.data.type == 'question' && widget.data.body['answer'] != null) {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.get('/cgi/co/posts/${widget.data.body['answer']}');
|
||||||
|
_isAnswer = true;
|
||||||
|
setState(() => _featuredComment = SnPost.fromJson(resp.data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/co/posts/${widget.data.id}/replies/featured', queryParameters: {
|
final resp = await sn.client.get('/cgi/co/posts/${widget.data.id}/replies/featured', queryParameters: {
|
||||||
@ -1166,13 +1222,15 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
|||||||
if (widget.data.metric.replyCount == 0) return const SizedBox.shrink();
|
if (widget.data.metric.replyCount == 0) return const SizedBox.shrink();
|
||||||
if (_featuredComment == null) return const SizedBox.shrink();
|
if (_featuredComment == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AnimateWidgetExtensions(Container(
|
return AnimateWidgetExtensions(Container(
|
||||||
constraints: BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
|
constraints: BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
|
||||||
margin: const EdgeInsets.only(top: 8),
|
margin: const EdgeInsets.only(top: 8),
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Material(
|
child: Material(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
color: _isAnswer ? Colors.green.withOpacity(0.5) : Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -1180,7 +1238,7 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
|||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
builder: (context) => PostCommentListPopup(
|
builder: (context) => PostCommentListPopup(
|
||||||
postId: widget.data.id,
|
post: widget.data,
|
||||||
commentCount: widget.data.metric.replyCount,
|
commentCount: widget.data.metric.replyCount,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1188,7 +1246,18 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('postFeaturedComment', style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 16)).tr(),
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Gap(2),
|
||||||
|
Icon(_isAnswer ? Symbols.task_alt : Symbols.prompt_suggestion, size: 20),
|
||||||
|
const Gap(10),
|
||||||
|
Text(
|
||||||
|
_isAnswer ? 'postQuestionAnswerTitle' : 'postFeaturedComment',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 15),
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@ -1196,7 +1265,7 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 12,
|
radius: 12,
|
||||||
backgroundImage: UniversalImage.provider(
|
backgroundImage: UniversalImage.provider(
|
||||||
_featuredComment!.publisher.avatar,
|
sn.getAttachmentUrl(_featuredComment!.publisher.avatar),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
@ -88,16 +88,6 @@ class PostMetaEditor extends StatelessWidget {
|
|||||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 8),
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 8),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (controller.mode == 'stories')
|
|
||||||
TextField(
|
|
||||||
controller: controller.titleController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'fieldPostTitle'.tr(),
|
|
||||||
border: UnderlineInputBorder(),
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
).padding(horizontal: 24),
|
|
||||||
const Gap(4),
|
|
||||||
PostTagsField(
|
PostTagsField(
|
||||||
initialTags: controller.tags,
|
initialTags: controller.tags,
|
||||||
labelText: 'fieldPostTags'.tr(),
|
labelText: 'fieldPostTags'.tr(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user