Browse Source

neon_news: get blocs via context

pull/377/head
Nikolas Rimikis 2 years ago
parent
commit
d54fa66e70
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 8
      packages/neon/neon_news/lib/dialogs/add_feed.dart
  2. 8
      packages/neon/neon_news/lib/pages/article.dart
  3. 37
      packages/neon/neon_news/lib/pages/feed.dart
  4. 4
      packages/neon/neon_news/lib/pages/folder.dart
  5. 21
      packages/neon/neon_news/lib/pages/main.dart
  6. 50
      packages/neon/neon_news/lib/widgets/articles_view.dart
  7. 37
      packages/neon/neon_news/lib/widgets/feed_floating_action_button.dart
  8. 282
      packages/neon/neon_news/lib/widgets/feeds_view.dart
  9. 33
      packages/neon/neon_news/lib/widgets/folder_floating_action_button.dart
  10. 22
      packages/neon/neon_news/lib/widgets/folder_view.dart
  11. 66
      packages/neon/neon_news/lib/widgets/folders_view.dart

8
packages/neon/neon_news/lib/dialogs/add_feed.dart

@ -2,12 +2,10 @@ part of '../neon_news.dart';
class NewsAddFeedDialog extends StatefulWidget {
const NewsAddFeedDialog({
required this.bloc,
this.folderID,
super.key,
});
final NewsBloc bloc;
final int? folderID;
@override
@ -17,6 +15,7 @@ class NewsAddFeedDialog extends StatefulWidget {
class _NewsAddFeedDialogState extends State<NewsAddFeedDialog> {
final formKey = GlobalKey<FormState>();
final controller = TextEditingController();
late NewsBloc bloc;
NextcloudNewsFolder? folder;
@ -30,6 +29,7 @@ class _NewsAddFeedDialogState extends State<NewsAddFeedDialog> {
void initState() {
super.initState();
bloc = Provider.of<NewsBloc>(context, listen: false);
unawaited(
Clipboard.getData(Clipboard.kTextPlain).then((final clipboardContent) {
if (clipboardContent != null && clipboardContent.text != null) {
@ -44,7 +44,7 @@ class _NewsAddFeedDialogState extends State<NewsAddFeedDialog> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFolder>>(
stream: widget.bloc.folders,
stream: bloc.folders,
builder: (final context, final folders) => NeonDialog(
title: Text(AppLocalizations.of(context).feedAdd),
children: [
@ -69,7 +69,7 @@ class _NewsAddFeedDialogState extends State<NewsAddFeedDialog> {
Center(
child: NeonException(
folders.error,
onRetry: widget.bloc.refresh,
onRetry: bloc.refresh,
),
),
Center(

8
packages/neon/neon_news/lib/pages/article.dart

@ -3,7 +3,6 @@ part of '../neon_news.dart';
class NewsArticlePage extends StatefulWidget {
const NewsArticlePage({
required this.bloc,
required this.articlesBloc,
required this.useWebView,
this.bodyData,
this.url,
@ -12,7 +11,6 @@ class NewsArticlePage extends StatefulWidget {
assert(!useWebView || url != null, 'url has to be set when using a WebView');
final NewsArticleBloc bloc;
final NewsArticlesBloc articlesBloc;
final bool useWebView;
final String? bodyData;
final String? url;
@ -22,6 +20,8 @@ class NewsArticlePage extends StatefulWidget {
}
class _NewsArticlePageState extends State<NewsArticlePage> {
late NewsArticlesBloc articlesBloc;
WebViewController? _webviewController;
Timer? _markAsReadTimer;
@ -29,6 +29,8 @@ class _NewsArticlePageState extends State<NewsArticlePage> {
void initState() {
super.initState();
articlesBloc = Provider.of<NewsBloc>(context, listen: false).mainArticlesBloc;
widget.bloc.errors.listen((final error) {
NeonException.showSnackbar(context, error);
});
@ -67,7 +69,7 @@ class _NewsArticlePageState extends State<NewsArticlePage> {
Future _startMarkAsReadTimer() async {
if (await widget.bloc.unread.first) {
if (widget.articlesBloc.options.articleDisableMarkAsReadTimeoutOption.value) {
if (articlesBloc.options.articleDisableMarkAsReadTimeoutOption.value) {
widget.bloc.markArticleAsRead();
} else {
_markAsReadTimer = Timer(const Duration(seconds: 3), () async {

37
packages/neon/neon_news/lib/pages/feed.dart

@ -2,30 +2,31 @@ part of '../neon_news.dart';
class NewsFeedPage extends StatelessWidget {
const NewsFeedPage({
required this.bloc,
required this.feed,
super.key,
});
final NewsBloc bloc;
final NextcloudNewsFeed feed;
@override
Widget build(final BuildContext context) => Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(feed.title),
),
body: NewsArticlesView(
bloc: NewsArticlesBloc(
bloc,
bloc.options,
bloc.requestManager,
bloc.client,
id: feed.id,
listType: ListType.feed,
),
newsBloc: bloc,
Widget build(final BuildContext context) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(feed.title),
),
body: NewsArticlesView(
bloc: NewsArticlesBloc(
bloc,
bloc.options,
bloc.requestManager,
bloc.client,
id: feed.id,
listType: ListType.feed,
),
);
),
);
}
}

4
packages/neon/neon_news/lib/pages/folder.dart

@ -2,12 +2,10 @@ part of '../neon_news.dart';
class NewsFolderPage extends StatelessWidget {
const NewsFolderPage({
required this.bloc,
required this.folder,
super.key,
});
final NewsBloc bloc;
final NextcloudNewsFolder folder;
@override
@ -17,11 +15,9 @@ class NewsFolderPage extends StatelessWidget {
title: Text(folder.name),
),
body: NewsFolderView(
bloc: bloc,
folder: folder,
),
floatingActionButton: NewsFeedFloatingActionButton(
bloc: bloc,
folderID: folder.id,
),
);

21
packages/neon/neon_news/lib/pages/main.dart

@ -25,23 +25,16 @@ class _NewsMainPageState extends State<NewsMainPage> {
@override
Widget build(final BuildContext context) {
final views = [
NewsArticlesView(
bloc: bloc.mainArticlesBloc,
newsBloc: bloc,
),
NewsFoldersView(
bloc: bloc,
),
NewsFeedsView(
bloc: bloc,
),
const views = [
NewsArticlesView(),
NewsFoldersView(),
NewsFeedsView(),
];
final floatingActionButtons = [
const floatingActionButtons = [
null,
NewsFolderFloatingActionButton(bloc: bloc),
NewsFeedFloatingActionButton(bloc: bloc),
NewsFolderFloatingActionButton(),
NewsFeedFloatingActionButton(),
];
return Scaffold(

50
packages/neon/neon_news/lib/widgets/articles_view.dart

@ -2,37 +2,41 @@ part of '../neon_news.dart';
class NewsArticlesView extends StatefulWidget {
const NewsArticlesView({
required this.bloc,
required this.newsBloc,
this.bloc,
super.key,
});
final NewsArticlesBloc bloc;
final NewsBloc newsBloc;
final NewsArticlesBloc? bloc;
@override
State<NewsArticlesView> createState() => _NewsArticlesViewState();
}
class _NewsArticlesViewState extends State<NewsArticlesView> {
late NewsBloc newsBloc;
late NewsArticlesBloc bloc;
@override
void initState() {
super.initState();
widget.bloc.errors.listen((final error) {
newsBloc = Provider.of<NewsBloc>(context, listen: false);
bloc = widget.bloc ?? newsBloc.mainArticlesBloc;
bloc.errors.listen((final error) {
NeonException.showSnackbar(context, error);
});
}
@override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: widget.newsBloc.feeds,
stream: newsBloc.feeds,
builder: (final context, final feeds) => ResultBuilder<List<NextcloudNewsArticle>>(
stream: widget.bloc.articles,
stream: bloc.articles,
builder: (final context, final articles) => SortBoxBuilder<ArticlesSortProperty, NextcloudNewsArticle>(
sortBox: articlesSortBox,
sortPropertyOption: widget.newsBloc.options.articlesSortPropertyOption,
sortBoxOrderOption: widget.newsBloc.options.articlesSortBoxOrderOption,
sortPropertyOption: newsBloc.options.articlesSortPropertyOption,
sortBoxOrderOption: newsBloc.options.articlesSortBoxOrderOption,
input: articles.data,
builder: (final context, final sorted) => NeonListView<NextcloudNewsArticle>(
scrollKey: 'news-articles',
@ -41,8 +45,8 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
error: articles.error ?? feeds.error,
onRefresh: () async {
await Future.wait([
widget.bloc.refresh(),
widget.newsBloc.refresh(),
bloc.refresh(),
newsBloc.refresh(),
]);
},
builder: (final context, final article) => _buildArticle(
@ -52,7 +56,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
),
topFixedChildren: [
StreamBuilder<FilterType>(
stream: widget.bloc.filterType,
stream: bloc.filterType,
builder: (final context, final selectedFilterTypeSnapshot) => Container(
margin: const EdgeInsets.symmetric(horizontal: 15),
child: DropdownButton<FilterType>(
@ -61,7 +65,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
items: [
FilterType.all,
FilterType.unread,
if (widget.bloc.listType == null) ...[
if (bloc.listType == null) ...[
FilterType.starred,
],
].map<DropdownMenuItem<FilterType>>(
@ -87,7 +91,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
},
).toList(),
onChanged: (final value) {
widget.bloc.setFilterType(value!);
bloc.setFilterType(value!);
},
),
),
@ -160,9 +164,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
trailing: IconButton(
onPressed: () {
if (article.starred) {
widget.bloc.unstarArticle(article);
bloc.unstarArticle(article);
} else {
widget.bloc.starArticle(article);
bloc.starArticle(article);
}
},
tooltip:
@ -174,13 +178,13 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
),
onLongPress: () {
if (article.unread) {
widget.bloc.markArticleAsRead(article);
bloc.markArticleAsRead(article);
} else {
widget.bloc.markArticleAsUnread(article);
bloc.markArticleAsUnread(article);
}
},
onTap: () async {
final viewType = widget.newsBloc.options.articleViewTypeOption.value;
final viewType = newsBloc.options.articleViewTypeOption.value;
String? bodyData;
try {
bodyData = _fixArticleBody(article.body);
@ -195,10 +199,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
builder: (final context) => NewsArticlePage(
bloc: NewsArticleBloc(
Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!.client,
widget.bloc,
bloc,
article,
),
articlesBloc: widget.bloc,
useWebView: false,
bodyData: bodyData,
url: article.url,
@ -213,10 +216,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
builder: (final context) => NewsArticlePage(
bloc: NewsArticleBloc(
Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!.client,
widget.bloc,
bloc,
article,
),
articlesBloc: widget.bloc,
useWebView: true,
url: article.url,
),
@ -224,7 +226,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
);
} else {
if (article.unread) {
widget.bloc.markArticleAsRead(article);
bloc.markArticleAsRead(article);
}
if (article.url != null) {
await launchUrlString(

37
packages/neon/neon_news/lib/widgets/feed_floating_action_button.dart

@ -2,29 +2,30 @@ part of '../neon_news.dart';
class NewsFeedFloatingActionButton extends StatelessWidget {
const NewsFeedFloatingActionButton({
required this.bloc,
this.folderID,
super.key,
});
final NewsBloc bloc;
final int? folderID;
@override
Widget build(final BuildContext context) => FloatingActionButton(
onPressed: () async {
final result = await showDialog<List>(
context: context,
builder: (final context) => NewsAddFeedDialog(
bloc: bloc,
folderID: folderID,
),
);
if (result != null) {
bloc.addFeed(result[0] as String, result[1] as int?);
}
},
tooltip: AppLocalizations.of(context).feedAdd,
child: const Icon(Icons.add),
);
Widget build(final BuildContext context) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
return FloatingActionButton(
onPressed: () async {
final result = await showDialog<List>(
context: context,
builder: (final context) => NewsAddFeedDialog(
folderID: folderID,
),
);
if (result != null) {
bloc.addFeed(result[0] as String, result[1] as int?);
}
},
tooltip: AppLocalizations.of(context).feedAdd,
child: const Icon(Icons.add),
);
}
}

282
packages/neon/neon_news/lib/widgets/feeds_view.dart

@ -2,168 +2,172 @@ part of '../neon_news.dart';
class NewsFeedsView extends StatelessWidget {
const NewsFeedsView({
required this.bloc,
this.folderID,
super.key,
});
final NewsBloc bloc;
final int? folderID;
@override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFolder>>(
stream: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FeedsSortProperty, NextcloudNewsFeed>(
sortBox: feedsSortBox,
sortPropertyOption: bloc.options.feedsSortPropertyOption,
sortBoxOrderOption: bloc.options.feedsSortBoxOrderOption,
input: folders.data == null
? null
: feeds.data?.where((final f) => folderID == null || f.folderId == folderID).toList(),
builder: (final context, final sorted) => NeonListView<NextcloudNewsFeed>(
scrollKey: 'news-feeds',
withFloatingActionButton: true,
items: sorted,
isLoading: feeds.loading || folders.loading,
error: feeds.error ?? folders.error,
onRefresh: bloc.refresh,
builder: (final context, final feed) => _buildFeed(
context,
feed,
folders.data!,
),
Widget build(final BuildContext context) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
return ResultBuilder<List<NextcloudNewsFolder>>(
stream: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FeedsSortProperty, NextcloudNewsFeed>(
sortBox: feedsSortBox,
sortPropertyOption: bloc.options.feedsSortPropertyOption,
sortBoxOrderOption: bloc.options.feedsSortBoxOrderOption,
input: folders.data == null
? null
: feeds.data?.where((final f) => folderID == null || f.folderId == folderID).toList(),
builder: (final context, final sorted) => NeonListView<NextcloudNewsFeed>(
scrollKey: 'news-feeds',
withFloatingActionButton: true,
items: sorted,
isLoading: feeds.loading || folders.loading,
error: feeds.error ?? folders.error,
onRefresh: bloc.refresh,
builder: (final context, final feed) => _buildFeed(
context,
feed,
folders.data!,
),
),
),
);
),
);
}
Widget _buildFeed(
final BuildContext context,
final NextcloudNewsFeed feed,
final List<NextcloudNewsFolder> folders,
) =>
ListTile(
title: Text(
feed.title,
style: feed.unreadCount! == 0
? Theme.of(context).textTheme.titleMedium!.copyWith(color: Theme.of(context).disabledColor)
: null,
),
subtitle:
feed.unreadCount! > 0 ? Text(AppLocalizations.of(context).articlesUnread(feed.unreadCount!)) : Container(),
leading: NewsFeedIcon(
feed: feed,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (feed.updateErrorCount > 0) ...[
IconButton(
onPressed: () async {
await showDialog(
context: context,
builder: (final context) => NewsFeedUpdateErrorDialog(
feed: feed,
),
);
},
tooltip: AppLocalizations.of(context).feedShowErrorMessage,
iconSize: 30,
icon: Text(
feed.updateErrorCount.toString(),
style: const TextStyle(
color: Colors.red,
) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
return ListTile(
title: Text(
feed.title,
style: feed.unreadCount! == 0
? Theme.of(context).textTheme.titleMedium!.copyWith(color: Theme.of(context).disabledColor)
: null,
),
subtitle:
feed.unreadCount! > 0 ? Text(AppLocalizations.of(context).articlesUnread(feed.unreadCount!)) : Container(),
leading: NewsFeedIcon(
feed: feed,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (feed.updateErrorCount > 0) ...[
IconButton(
onPressed: () async {
await showDialog(
context: context,
builder: (final context) => NewsFeedUpdateErrorDialog(
feed: feed,
),
);
},
tooltip: AppLocalizations.of(context).feedShowErrorMessage,
iconSize: 30,
icon: Text(
feed.updateErrorCount.toString(),
style: const TextStyle(
color: Colors.red,
),
),
],
PopupMenuButton<NewsFeedAction>(
itemBuilder: (final context) => [
PopupMenuItem(
value: NewsFeedAction.showURL,
child: Text(AppLocalizations.of(context).feedShowURL),
),
PopupMenuItem(
value: NewsFeedAction.delete,
child: Text(AppLocalizations.of(context).actionDelete),
),
),
],
PopupMenuButton<NewsFeedAction>(
itemBuilder: (final context) => [
PopupMenuItem(
value: NewsFeedAction.showURL,
child: Text(AppLocalizations.of(context).feedShowURL),
),
PopupMenuItem(
value: NewsFeedAction.delete,
child: Text(AppLocalizations.of(context).actionDelete),
),
PopupMenuItem(
value: NewsFeedAction.rename,
child: Text(AppLocalizations.of(context).actionRename),
),
if (folders.isNotEmpty) ...[
PopupMenuItem(
value: NewsFeedAction.rename,
child: Text(AppLocalizations.of(context).actionRename),
value: NewsFeedAction.move,
child: Text(AppLocalizations.of(context).actionMove),
),
if (folders.isNotEmpty) ...[
PopupMenuItem(
value: NewsFeedAction.move,
child: Text(AppLocalizations.of(context).actionMove),
),
],
],
onSelected: (final action) async {
switch (action) {
case NewsFeedAction.showURL:
await showDialog(
context: context,
builder: (final context) => NewsFeedShowURLDialog(
feed: feed,
),
);
break;
case NewsFeedAction.delete:
],
onSelected: (final action) async {
switch (action) {
case NewsFeedAction.showURL:
await showDialog(
context: context,
builder: (final context) => NewsFeedShowURLDialog(
feed: feed,
),
);
break;
case NewsFeedAction.delete:
// ignore: use_build_context_synchronously
if (await showConfirmationDialog(
context,
// ignore: use_build_context_synchronously
if (await showConfirmationDialog(
context,
// ignore: use_build_context_synchronously
AppLocalizations.of(context).feedRemoveConfirm(feed.title),
)) {
bloc.removeFeed(feed.id);
}
break;
case NewsFeedAction.rename:
final result = await showRenameDialog(
context: context,
title: AppLocalizations.of(context).feedRename,
value: feed.title,
);
if (result != null) {
bloc.renameFeed(feed.id, result);
}
break;
case NewsFeedAction.move:
final result = await showDialog<List<int?>>(
context: context,
builder: (final context) => NewsMoveFeedDialog(
folders: folders,
feed: feed,
),
);
if (result != null) {
bloc.moveFeed(feed.id, result[0]);
}
break;
}
},
),
],
),
onLongPress: () {
if (feed.unreadCount! > 0) {
bloc.markFeedAsRead(feed.id);
}
},
onTap: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => NewsFeedPage(
bloc: bloc,
feed: feed,
),
AppLocalizations.of(context).feedRemoveConfirm(feed.title),
)) {
bloc.removeFeed(feed.id);
}
break;
case NewsFeedAction.rename:
final result = await showRenameDialog(
context: context,
title: AppLocalizations.of(context).feedRename,
value: feed.title,
);
if (result != null) {
bloc.renameFeed(feed.id, result);
}
break;
case NewsFeedAction.move:
final result = await showDialog<List<int?>>(
context: context,
builder: (final context) => NewsMoveFeedDialog(
folders: folders,
feed: feed,
),
);
if (result != null) {
bloc.moveFeed(feed.id, result[0]);
}
break;
}
},
),
],
),
onLongPress: () {
if (feed.unreadCount! > 0) {
bloc.markFeedAsRead(feed.id);
}
},
onTap: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => NewsFeedPage(
feed: feed,
),
);
},
);
),
);
},
);
}
}
enum NewsFeedAction {

33
packages/neon/neon_news/lib/widgets/folder_floating_action_button.dart

@ -2,24 +2,25 @@ part of '../neon_news.dart';
class NewsFolderFloatingActionButton extends StatelessWidget {
const NewsFolderFloatingActionButton({
required this.bloc,
super.key,
});
final NewsBloc bloc;
@override
Widget build(final BuildContext context) => FloatingActionButton(
onPressed: () async {
final result = await showDialog<String>(
context: context,
builder: (final context) => const NewsCreateFolderDialog(),
);
if (result != null) {
bloc.createFolder(result);
}
},
tooltip: AppLocalizations.of(context).folderCreate,
child: const Icon(Icons.add),
);
Widget build(final BuildContext context) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
return FloatingActionButton(
onPressed: () async {
final result = await showDialog<String>(
context: context,
builder: (final context) => const NewsCreateFolderDialog(),
);
if (result != null) {
bloc.createFolder(result);
}
},
tooltip: AppLocalizations.of(context).folderCreate,
child: const Icon(Icons.add),
);
}
}

22
packages/neon/neon_news/lib/widgets/folder_view.dart

@ -2,12 +2,10 @@ part of '../neon_news.dart';
class NewsFolderView extends StatefulWidget {
const NewsFolderView({
required this.bloc,
required this.folder,
super.key,
});
final NewsBloc bloc;
final NextcloudNewsFolder folder;
@override
@ -15,9 +13,17 @@ class NewsFolderView extends StatefulWidget {
}
class _NewsFolderViewState extends State<NewsFolderView> {
late final option = widget.bloc.options.defaultFolderViewTypeOption;
late NewsBloc bloc;
late final option = bloc.options.defaultFolderViewTypeOption;
late DefaultFolderViewType _viewType = option.value;
@override
void initState() {
bloc = Provider.of<NewsBloc>(context, listen: false);
super.initState();
}
@override
Widget build(final BuildContext context) => Column(
children: [
@ -45,17 +51,15 @@ class _NewsFolderViewState extends State<NewsFolderView> {
child: _viewType == DefaultFolderViewType.articles
? NewsArticlesView(
bloc: NewsArticlesBloc(
widget.bloc,
widget.bloc.options,
widget.bloc.requestManager,
widget.bloc.client,
bloc,
bloc.options,
bloc.requestManager,
bloc.client,
id: widget.folder.id,
listType: ListType.folder,
),
newsBloc: widget.bloc,
)
: NewsFeedsView(
bloc: widget.bloc,
folderID: widget.folder.id,
),
),

66
packages/neon/neon_news/lib/widgets/folders_view.dart

@ -2,49 +2,52 @@ part of '../neon_news.dart';
class NewsFoldersView extends StatelessWidget {
const NewsFoldersView({
required this.bloc,
super.key,
});
final NewsBloc bloc;
@override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFolder>>(
stream: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FoldersSortProperty, FolderFeedsWrapper>(
sortBox: foldersSortBox,
sortPropertyOption: bloc.options.foldersSortPropertyOption,
sortBoxOrderOption: bloc.options.foldersSortBoxOrderOption,
input: feeds.data == null
? null
: folders.data
?.map(
(final folder) => FolderFeedsWrapper(
folder,
feeds.data!.where((final feed) => feed.folderId == folder.id).toList(),
),
)
.toList(),
builder: (final context, final sorted) => NeonListView<FolderFeedsWrapper>(
scrollKey: 'news-folders',
withFloatingActionButton: true,
items: sorted,
isLoading: feeds.loading || folders.loading,
error: feeds.error ?? folders.error,
onRefresh: bloc.refresh,
builder: _buildFolder,
),
Widget build(final BuildContext context) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
return ResultBuilder<List<NextcloudNewsFolder>>(
stream: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FoldersSortProperty, FolderFeedsWrapper>(
sortBox: foldersSortBox,
sortPropertyOption: bloc.options.foldersSortPropertyOption,
sortBoxOrderOption: bloc.options.foldersSortBoxOrderOption,
input: feeds.data == null
? null
: folders.data
?.map(
(final folder) => FolderFeedsWrapper(
folder,
feeds.data!.where((final feed) => feed.folderId == folder.id).toList(),
),
)
.toList(),
builder: (final context, final sorted) => NeonListView<FolderFeedsWrapper>(
scrollKey: 'news-folders',
withFloatingActionButton: true,
items: sorted,
isLoading: feeds.loading || folders.loading,
error: feeds.error ?? folders.error,
onRefresh: bloc.refresh,
builder: _buildFolder,
),
),
);
),
);
}
Widget _buildFolder(
final BuildContext context,
final FolderFeedsWrapper folderFeedsWrapper,
) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
final unreadCount = feedsUnreadCountSum(folderFeedsWrapper.feeds);
return ListTile(
title: Text(
folderFeedsWrapper.folder.name,
@ -122,7 +125,6 @@ class NewsFoldersView extends StatelessWidget {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => NewsFolderPage(
bloc: bloc,
folder: folderFeedsWrapper.folder,
),
),

Loading…
Cancel
Save