✨ Draft box
This commit is contained in:
@ -315,4 +315,15 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_usernameController.dispose();
|
||||
_nicknameController.dispose();
|
||||
_firstNameController.dispose();
|
||||
_lastNameController.dispose();
|
||||
_descriptionController.dispose();
|
||||
_birthdayController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import 'package:solian/router.dart';
|
||||
import 'package:solian/theme.dart';
|
||||
import 'package:solian/widgets/app_bar_title.dart';
|
||||
import 'package:solian/widgets/attachments/attachment_publish.dart';
|
||||
import 'package:solian/widgets/posts/tags_field.dart';
|
||||
import 'package:solian/widgets/feed/feed_tags_field.dart';
|
||||
import 'package:solian/widgets/prev_page.dart';
|
||||
import 'package:textfield_tags/textfield_tags.dart';
|
||||
|
||||
|
@ -4,13 +4,13 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:solian/models/feed.dart';
|
||||
import 'package:solian/models/pagination.dart';
|
||||
import 'package:solian/providers/auth.dart';
|
||||
import 'package:solian/providers/content/post.dart';
|
||||
import 'package:solian/providers/content/feed.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:solian/screens/account/notification.dart';
|
||||
import 'package:solian/theme.dart';
|
||||
import 'package:solian/widgets/app_bar_title.dart';
|
||||
import 'package:solian/widgets/current_state_action.dart';
|
||||
import 'package:solian/widgets/posts/feed_list.dart';
|
||||
import 'package:solian/widgets/feed/feed_list.dart';
|
||||
|
||||
class FeedScreen extends StatefulWidget {
|
||||
const FeedScreen({super.key});
|
||||
@ -24,7 +24,7 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
PagingController(firstPageKey: 0);
|
||||
|
||||
getPosts(int pageKey) async {
|
||||
final PostProvider provider = Get.find();
|
||||
final FeedProvider provider = Get.find();
|
||||
|
||||
Response resp;
|
||||
try {
|
||||
@ -46,7 +46,6 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_pagingController.addPageRequestListener(getPosts);
|
||||
}
|
||||
|
||||
@ -85,12 +84,23 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class FeedCreationButton extends StatelessWidget {
|
||||
final bool hideDraftBox;
|
||||
final Function? onCreated;
|
||||
|
||||
const FeedCreationButton({super.key, this.onCreated});
|
||||
const FeedCreationButton({
|
||||
super.key,
|
||||
this.hideDraftBox = false,
|
||||
this.onCreated,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -131,16 +141,17 @@ class FeedCreationButton extends StatelessWidget {
|
||||
});
|
||||
},
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: ListTile(
|
||||
title: Text('draftBoxOpen'.tr),
|
||||
leading: const Icon(Icons.drafts),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
if (!hideDraftBox)
|
||||
PopupMenuItem(
|
||||
child: ListTile(
|
||||
title: Text('draftBoxOpen'.tr),
|
||||
leading: const Icon(Icons.drafts),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
),
|
||||
onTap: () {
|
||||
AppRouter.instance.pushNamed('draftBox');
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('draftBox');
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:solian/models/feed.dart';
|
||||
import 'package:solian/models/pagination.dart';
|
||||
import 'package:solian/models/post.dart';
|
||||
import 'package:solian/providers/content/feed.dart';
|
||||
import 'package:solian/screens/feed.dart';
|
||||
import 'package:solian/theme.dart';
|
||||
import 'package:solian/widgets/app_bar_title.dart';
|
||||
import 'package:solian/widgets/posts/post_action.dart';
|
||||
import 'package:solian/widgets/posts/post_owned_list.dart';
|
||||
import 'package:solian/widgets/prev_page.dart';
|
||||
|
||||
class DraftBoxScreen extends StatelessWidget {
|
||||
class DraftBoxScreen extends StatefulWidget {
|
||||
const DraftBoxScreen({super.key});
|
||||
|
||||
@override
|
||||
State<DraftBoxScreen> createState() => _DraftBoxScreenState();
|
||||
}
|
||||
|
||||
class _DraftBoxScreenState extends State<DraftBoxScreen> {
|
||||
final PagingController<int, FeedRecord> _pagingController =
|
||||
PagingController(firstPageKey: 0);
|
||||
|
||||
getPosts(int pageKey) async {
|
||||
final FeedProvider provider = Get.find();
|
||||
|
||||
Response resp;
|
||||
try {
|
||||
resp = await provider.listDraft(pageKey);
|
||||
} catch (e) {
|
||||
_pagingController.error = e;
|
||||
return;
|
||||
}
|
||||
|
||||
final PaginationResult result = PaginationResult.fromJson(resp.body);
|
||||
if (result.count == 0) {
|
||||
_pagingController.appendLastPage([]);
|
||||
return;
|
||||
}
|
||||
|
||||
final parsed = result.data?.map((e) => FeedRecord.fromJson(e)).toList();
|
||||
if (parsed != null && parsed.length >= 10) {
|
||||
_pagingController.appendPage(parsed, pageKey + parsed.length);
|
||||
} else if (parsed != null) {
|
||||
_pagingController.appendLastPage(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pagingController.addPageRequestListener(getPosts);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
@ -20,6 +66,7 @@ class DraftBoxScreen extends StatelessWidget {
|
||||
leading: const PrevPageButton(),
|
||||
actions: [
|
||||
FeedCreationButton(
|
||||
hideDraftBox: true,
|
||||
onCreated: () {},
|
||||
),
|
||||
SizedBox(
|
||||
@ -27,7 +74,44 @@ class DraftBoxScreen extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
||||
child: PagedListView<int, FeedRecord>(
|
||||
pagingController: _pagingController,
|
||||
builderDelegate: PagedChildBuilderDelegate(
|
||||
itemBuilder: (context, item, index) {
|
||||
switch (item.type) {
|
||||
case 'post':
|
||||
final data = Post.fromJson(item.data);
|
||||
return PostOwnedListEntry(
|
||||
item: data,
|
||||
onTap: () async {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) => PostAction(
|
||||
item: data,
|
||||
noReact: true,
|
||||
),
|
||||
).then((value) {
|
||||
if (value != null) _pagingController.refresh();
|
||||
});
|
||||
},
|
||||
).paddingOnly(left: 12, right: 12, bottom: 4);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ import 'package:get/get.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:solian/models/feed.dart';
|
||||
import 'package:solian/models/pagination.dart';
|
||||
import 'package:solian/providers/content/post.dart';
|
||||
import 'package:solian/widgets/posts/feed_list.dart';
|
||||
import 'package:solian/providers/content/feed.dart';
|
||||
import 'package:solian/widgets/feed/feed_list.dart';
|
||||
|
||||
class FeedSearchScreen extends StatefulWidget {
|
||||
final String? tag;
|
||||
@ -21,7 +21,7 @@ class _FeedSearchScreenState extends State<FeedSearchScreen> {
|
||||
PagingController(firstPageKey: 0);
|
||||
|
||||
getPosts(int pageKey) async {
|
||||
final PostProvider provider = Get.find();
|
||||
final FeedProvider provider = Get.find();
|
||||
|
||||
Response resp;
|
||||
try {
|
||||
@ -68,7 +68,8 @@ class _FeedSearchScreenState extends State<FeedSearchScreen> {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.category),
|
||||
tileColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
title: Text('feedSearchWithCategory'.trParams({'key': widget.category!})),
|
||||
title: Text('feedSearchWithCategory'
|
||||
.trParams({'key': widget.category!})),
|
||||
),
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
@ -85,4 +86,10 @@ class _FeedSearchScreenState extends State<FeedSearchScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:solian/exts.dart';
|
||||
import 'package:solian/models/post.dart';
|
||||
import 'package:solian/providers/content/post.dart';
|
||||
import 'package:solian/providers/content/feed.dart';
|
||||
import 'package:solian/widgets/centered_container.dart';
|
||||
import 'package:solian/widgets/posts/post_item.dart';
|
||||
import 'package:solian/widgets/posts/post_replies.dart';
|
||||
@ -20,7 +20,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
||||
Post? item;
|
||||
|
||||
Future<Post?> getDetail() async {
|
||||
final PostProvider provider = Get.find();
|
||||
final FeedProvider provider = Get.find();
|
||||
|
||||
try {
|
||||
final resp = await provider.getPost(widget.alias);
|
||||
|
@ -11,7 +11,7 @@ import 'package:solian/theme.dart';
|
||||
import 'package:solian/widgets/app_bar_title.dart';
|
||||
import 'package:solian/widgets/attachments/attachment_publish.dart';
|
||||
import 'package:solian/widgets/posts/post_item.dart';
|
||||
import 'package:solian/widgets/posts/tags_field.dart';
|
||||
import 'package:solian/widgets/feed/feed_tags_field.dart';
|
||||
import 'package:solian/widgets/prev_page.dart';
|
||||
import 'package:textfield_tags/textfield_tags.dart';
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
@ -147,95 +147,94 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
|
||||
)
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: Stack(
|
||||
children: [
|
||||
ListView(
|
||||
children: [
|
||||
if (_isBusy)
|
||||
const LinearProgressIndicator().animate().scaleX(),
|
||||
if (widget.edit != null)
|
||||
MaterialBanner(
|
||||
leading: const Icon(Icons.edit),
|
||||
leadingPadding:
|
||||
const EdgeInsets.only(left: 10, right: 20),
|
||||
dividerColor: Colors.transparent,
|
||||
content: Text('postEditingNotify'.tr),
|
||||
actions: notifyBannerActions,
|
||||
),
|
||||
if (widget.reply != null)
|
||||
ExpansionTile(
|
||||
leading: const FaIcon(
|
||||
FontAwesomeIcons.reply,
|
||||
size: 18,
|
||||
).paddingOnly(left: 2),
|
||||
title: Text('postReplyingNotify'.trParams(
|
||||
{'username': '@${widget.reply!.author.name}'},
|
||||
)),
|
||||
collapsedBackgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceContainer,
|
||||
children: [
|
||||
PostItem(
|
||||
item: widget.reply!,
|
||||
isReactable: false,
|
||||
).paddingOnly(bottom: 8),
|
||||
],
|
||||
),
|
||||
if (widget.repost != null)
|
||||
ExpansionTile(
|
||||
leading: const FaIcon(
|
||||
FontAwesomeIcons.retweet,
|
||||
size: 18,
|
||||
).paddingOnly(left: 2),
|
||||
title: Text('postRepostingNotify'.trParams(
|
||||
{'username': '@${widget.repost!.author.name}'},
|
||||
)),
|
||||
collapsedBackgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceContainer,
|
||||
children: [
|
||||
PostItem(
|
||||
item: widget.repost!,
|
||||
isReactable: false,
|
||||
).paddingOnly(bottom: 8),
|
||||
],
|
||||
),
|
||||
if (widget.realm != null)
|
||||
MaterialBanner(
|
||||
leading: const Icon(Icons.group),
|
||||
leadingPadding:
|
||||
const EdgeInsets.only(left: 10, right: 20),
|
||||
dividerColor: Colors.transparent,
|
||||
content: Text(
|
||||
'postInRealmNotify'
|
||||
.trParams({'realm': '#${widget.realm!.alias}'}),
|
||||
),
|
||||
actions: notifyBannerActions,
|
||||
),
|
||||
const Divider(thickness: 0.3, height: 0.3)
|
||||
.paddingOnly(bottom: 8),
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
autofocus: true,
|
||||
autocorrect: true,
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: _contentController,
|
||||
decoration: InputDecoration.collapsed(
|
||||
hintText: 'postContentPlaceholder'.tr,
|
||||
),
|
||||
onTapOutside: (_) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
ListView(
|
||||
children: [
|
||||
if (_isBusy) const LinearProgressIndicator().animate().scaleX(),
|
||||
if (widget.edit != null && widget.edit!.isDraft != true)
|
||||
MaterialBanner(
|
||||
leading: const Icon(Icons.edit),
|
||||
leadingPadding: const EdgeInsets.only(left: 10, right: 20),
|
||||
dividerColor: Colors.transparent,
|
||||
content: Text('postEditingNotify'.tr),
|
||||
actions: notifyBannerActions,
|
||||
),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
if (widget.reply != null)
|
||||
ExpansionTile(
|
||||
leading: const FaIcon(
|
||||
FontAwesomeIcons.reply,
|
||||
size: 18,
|
||||
).paddingOnly(left: 2),
|
||||
title: Text('postReplyingNotify'.trParams(
|
||||
{'username': '@${widget.reply!.author.name}'},
|
||||
)),
|
||||
collapsedBackgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceContainer,
|
||||
children: [
|
||||
PostItem(
|
||||
item: widget.reply!,
|
||||
isReactable: false,
|
||||
).paddingOnly(bottom: 8),
|
||||
],
|
||||
),
|
||||
if (widget.repost != null)
|
||||
ExpansionTile(
|
||||
leading: const FaIcon(
|
||||
FontAwesomeIcons.retweet,
|
||||
size: 18,
|
||||
).paddingOnly(left: 2),
|
||||
title: Text('postRepostingNotify'.trParams(
|
||||
{'username': '@${widget.repost!.author.name}'},
|
||||
)),
|
||||
collapsedBackgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceContainer,
|
||||
children: [
|
||||
PostItem(
|
||||
item: widget.repost!,
|
||||
isReactable: false,
|
||||
).paddingOnly(bottom: 8),
|
||||
],
|
||||
),
|
||||
if (widget.realm != null)
|
||||
MaterialBanner(
|
||||
leading: const Icon(Icons.group),
|
||||
leadingPadding: const EdgeInsets.only(left: 10, right: 20),
|
||||
dividerColor: Colors.transparent,
|
||||
content: Text(
|
||||
'postInRealmNotify'
|
||||
.trParams({'realm': '#${widget.realm!.alias}'}),
|
||||
),
|
||||
actions: notifyBannerActions,
|
||||
),
|
||||
const Divider(thickness: 0.3, height: 0.3)
|
||||
.paddingOnly(bottom: 8),
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
autofocus: true,
|
||||
autocorrect: true,
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: _contentController,
|
||||
decoration: InputDecoration.collapsed(
|
||||
hintText: 'postContentPlaceholder'.tr,
|
||||
),
|
||||
onTapOutside: (_) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 120)
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Material(
|
||||
elevation: 8,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -274,10 +273,10 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
|
||||
).paddingSymmetric(horizontal: 6, vertical: 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
).paddingOnly(bottom: MediaQuery.of(context).padding.bottom),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ import 'package:solian/models/post.dart';
|
||||
import 'package:solian/models/realm.dart';
|
||||
import 'package:solian/providers/auth.dart';
|
||||
import 'package:solian/providers/content/channel.dart';
|
||||
import 'package:solian/providers/content/post.dart';
|
||||
import 'package:solian/providers/content/feed.dart';
|
||||
import 'package:solian/providers/content/realm.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:solian/screens/channel/channel_organize.dart';
|
||||
@ -165,7 +165,7 @@ class _RealmPostListWidgetState extends State<RealmPostListWidget> {
|
||||
PagingController(firstPageKey: 0);
|
||||
|
||||
getPosts(int pageKey) async {
|
||||
final PostProvider provider = Get.find();
|
||||
final FeedProvider provider = Get.find();
|
||||
|
||||
Response resp;
|
||||
try {
|
||||
|
Reference in New Issue
Block a user