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

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

@ -3,7 +3,6 @@ part of '../neon_news.dart';
class NewsArticlePage extends StatefulWidget { class NewsArticlePage extends StatefulWidget {
const NewsArticlePage({ const NewsArticlePage({
required this.bloc, required this.bloc,
required this.articlesBloc,
required this.useWebView, required this.useWebView,
this.bodyData, this.bodyData,
this.url, this.url,
@ -12,7 +11,6 @@ class NewsArticlePage extends StatefulWidget {
assert(!useWebView || url != null, 'url has to be set when using a WebView'); assert(!useWebView || url != null, 'url has to be set when using a WebView');
final NewsArticleBloc bloc; final NewsArticleBloc bloc;
final NewsArticlesBloc articlesBloc;
final bool useWebView; final bool useWebView;
final String? bodyData; final String? bodyData;
final String? url; final String? url;
@ -22,6 +20,8 @@ class NewsArticlePage extends StatefulWidget {
} }
class _NewsArticlePageState extends State<NewsArticlePage> { class _NewsArticlePageState extends State<NewsArticlePage> {
late NewsArticlesBloc articlesBloc;
WebViewController? _webviewController; WebViewController? _webviewController;
Timer? _markAsReadTimer; Timer? _markAsReadTimer;
@ -29,6 +29,8 @@ class _NewsArticlePageState extends State<NewsArticlePage> {
void initState() { void initState() {
super.initState(); super.initState();
articlesBloc = Provider.of<NewsBloc>(context, listen: false).mainArticlesBloc;
widget.bloc.errors.listen((final error) { widget.bloc.errors.listen((final error) {
NeonException.showSnackbar(context, error); NeonException.showSnackbar(context, error);
}); });
@ -67,7 +69,7 @@ class _NewsArticlePageState extends State<NewsArticlePage> {
Future _startMarkAsReadTimer() async { Future _startMarkAsReadTimer() async {
if (await widget.bloc.unread.first) { if (await widget.bloc.unread.first) {
if (widget.articlesBloc.options.articleDisableMarkAsReadTimeoutOption.value) { if (articlesBloc.options.articleDisableMarkAsReadTimeoutOption.value) {
widget.bloc.markArticleAsRead(); widget.bloc.markArticleAsRead();
} else { } else {
_markAsReadTimer = Timer(const Duration(seconds: 3), () async { _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 { class NewsFeedPage extends StatelessWidget {
const NewsFeedPage({ const NewsFeedPage({
required this.bloc,
required this.feed, required this.feed,
super.key, super.key,
}); });
final NewsBloc bloc;
final NextcloudNewsFeed feed; final NextcloudNewsFeed feed;
@override @override
Widget build(final BuildContext context) => Scaffold( Widget build(final BuildContext context) {
resizeToAvoidBottomInset: false, final bloc = Provider.of<NewsBloc>(context, listen: false);
appBar: AppBar(
title: Text(feed.title), return Scaffold(
), resizeToAvoidBottomInset: false,
body: NewsArticlesView( appBar: AppBar(
bloc: NewsArticlesBloc( title: Text(feed.title),
bloc, ),
bloc.options, body: NewsArticlesView(
bloc.requestManager, bloc: NewsArticlesBloc(
bloc.client, bloc,
id: feed.id, bloc.options,
listType: ListType.feed, bloc.requestManager,
), bloc.client,
newsBloc: bloc, 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 { class NewsFolderPage extends StatelessWidget {
const NewsFolderPage({ const NewsFolderPage({
required this.bloc,
required this.folder, required this.folder,
super.key, super.key,
}); });
final NewsBloc bloc;
final NextcloudNewsFolder folder; final NextcloudNewsFolder folder;
@override @override
@ -17,11 +15,9 @@ class NewsFolderPage extends StatelessWidget {
title: Text(folder.name), title: Text(folder.name),
), ),
body: NewsFolderView( body: NewsFolderView(
bloc: bloc,
folder: folder, folder: folder,
), ),
floatingActionButton: NewsFeedFloatingActionButton( floatingActionButton: NewsFeedFloatingActionButton(
bloc: bloc,
folderID: folder.id, folderID: folder.id,
), ),
); );

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

@ -25,23 +25,16 @@ class _NewsMainPageState extends State<NewsMainPage> {
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
final views = [ const views = [
NewsArticlesView( NewsArticlesView(),
bloc: bloc.mainArticlesBloc, NewsFoldersView(),
newsBloc: bloc, NewsFeedsView(),
),
NewsFoldersView(
bloc: bloc,
),
NewsFeedsView(
bloc: bloc,
),
]; ];
final floatingActionButtons = [ const floatingActionButtons = [
null, null,
NewsFolderFloatingActionButton(bloc: bloc), NewsFolderFloatingActionButton(),
NewsFeedFloatingActionButton(bloc: bloc), NewsFeedFloatingActionButton(),
]; ];
return Scaffold( 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 { class NewsArticlesView extends StatefulWidget {
const NewsArticlesView({ const NewsArticlesView({
required this.bloc, this.bloc,
required this.newsBloc,
super.key, super.key,
}); });
final NewsArticlesBloc bloc; final NewsArticlesBloc? bloc;
final NewsBloc newsBloc;
@override @override
State<NewsArticlesView> createState() => _NewsArticlesViewState(); State<NewsArticlesView> createState() => _NewsArticlesViewState();
} }
class _NewsArticlesViewState extends State<NewsArticlesView> { class _NewsArticlesViewState extends State<NewsArticlesView> {
late NewsBloc newsBloc;
late NewsArticlesBloc bloc;
@override @override
void initState() { void initState() {
super.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); NeonException.showSnackbar(context, error);
}); });
} }
@override @override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFeed>>( Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: widget.newsBloc.feeds, stream: newsBloc.feeds,
builder: (final context, final feeds) => ResultBuilder<List<NextcloudNewsArticle>>( builder: (final context, final feeds) => ResultBuilder<List<NextcloudNewsArticle>>(
stream: widget.bloc.articles, stream: bloc.articles,
builder: (final context, final articles) => SortBoxBuilder<ArticlesSortProperty, NextcloudNewsArticle>( builder: (final context, final articles) => SortBoxBuilder<ArticlesSortProperty, NextcloudNewsArticle>(
sortBox: articlesSortBox, sortBox: articlesSortBox,
sortPropertyOption: widget.newsBloc.options.articlesSortPropertyOption, sortPropertyOption: newsBloc.options.articlesSortPropertyOption,
sortBoxOrderOption: widget.newsBloc.options.articlesSortBoxOrderOption, sortBoxOrderOption: newsBloc.options.articlesSortBoxOrderOption,
input: articles.data, input: articles.data,
builder: (final context, final sorted) => NeonListView<NextcloudNewsArticle>( builder: (final context, final sorted) => NeonListView<NextcloudNewsArticle>(
scrollKey: 'news-articles', scrollKey: 'news-articles',
@ -41,8 +45,8 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
error: articles.error ?? feeds.error, error: articles.error ?? feeds.error,
onRefresh: () async { onRefresh: () async {
await Future.wait([ await Future.wait([
widget.bloc.refresh(), bloc.refresh(),
widget.newsBloc.refresh(), newsBloc.refresh(),
]); ]);
}, },
builder: (final context, final article) => _buildArticle( builder: (final context, final article) => _buildArticle(
@ -52,7 +56,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
), ),
topFixedChildren: [ topFixedChildren: [
StreamBuilder<FilterType>( StreamBuilder<FilterType>(
stream: widget.bloc.filterType, stream: bloc.filterType,
builder: (final context, final selectedFilterTypeSnapshot) => Container( builder: (final context, final selectedFilterTypeSnapshot) => Container(
margin: const EdgeInsets.symmetric(horizontal: 15), margin: const EdgeInsets.symmetric(horizontal: 15),
child: DropdownButton<FilterType>( child: DropdownButton<FilterType>(
@ -61,7 +65,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
items: [ items: [
FilterType.all, FilterType.all,
FilterType.unread, FilterType.unread,
if (widget.bloc.listType == null) ...[ if (bloc.listType == null) ...[
FilterType.starred, FilterType.starred,
], ],
].map<DropdownMenuItem<FilterType>>( ].map<DropdownMenuItem<FilterType>>(
@ -87,7 +91,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
}, },
).toList(), ).toList(),
onChanged: (final value) { onChanged: (final value) {
widget.bloc.setFilterType(value!); bloc.setFilterType(value!);
}, },
), ),
), ),
@ -160,9 +164,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
trailing: IconButton( trailing: IconButton(
onPressed: () { onPressed: () {
if (article.starred) { if (article.starred) {
widget.bloc.unstarArticle(article); bloc.unstarArticle(article);
} else { } else {
widget.bloc.starArticle(article); bloc.starArticle(article);
} }
}, },
tooltip: tooltip:
@ -174,13 +178,13 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
), ),
onLongPress: () { onLongPress: () {
if (article.unread) { if (article.unread) {
widget.bloc.markArticleAsRead(article); bloc.markArticleAsRead(article);
} else { } else {
widget.bloc.markArticleAsUnread(article); bloc.markArticleAsUnread(article);
} }
}, },
onTap: () async { onTap: () async {
final viewType = widget.newsBloc.options.articleViewTypeOption.value; final viewType = newsBloc.options.articleViewTypeOption.value;
String? bodyData; String? bodyData;
try { try {
bodyData = _fixArticleBody(article.body); bodyData = _fixArticleBody(article.body);
@ -195,10 +199,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
builder: (final context) => NewsArticlePage( builder: (final context) => NewsArticlePage(
bloc: NewsArticleBloc( bloc: NewsArticleBloc(
Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!.client, Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!.client,
widget.bloc, bloc,
article, article,
), ),
articlesBloc: widget.bloc,
useWebView: false, useWebView: false,
bodyData: bodyData, bodyData: bodyData,
url: article.url, url: article.url,
@ -213,10 +216,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
builder: (final context) => NewsArticlePage( builder: (final context) => NewsArticlePage(
bloc: NewsArticleBloc( bloc: NewsArticleBloc(
Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!.client, Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!.client,
widget.bloc, bloc,
article, article,
), ),
articlesBloc: widget.bloc,
useWebView: true, useWebView: true,
url: article.url, url: article.url,
), ),
@ -224,7 +226,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
); );
} else { } else {
if (article.unread) { if (article.unread) {
widget.bloc.markArticleAsRead(article); bloc.markArticleAsRead(article);
} }
if (article.url != null) { if (article.url != null) {
await launchUrlString( 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 { class NewsFeedFloatingActionButton extends StatelessWidget {
const NewsFeedFloatingActionButton({ const NewsFeedFloatingActionButton({
required this.bloc,
this.folderID, this.folderID,
super.key, super.key,
}); });
final NewsBloc bloc;
final int? folderID; final int? folderID;
@override @override
Widget build(final BuildContext context) => FloatingActionButton( Widget build(final BuildContext context) {
onPressed: () async { final bloc = Provider.of<NewsBloc>(context, listen: false);
final result = await showDialog<List>(
context: context, return FloatingActionButton(
builder: (final context) => NewsAddFeedDialog( onPressed: () async {
bloc: bloc, final result = await showDialog<List>(
folderID: folderID, context: context,
), builder: (final context) => NewsAddFeedDialog(
); folderID: folderID,
if (result != null) { ),
bloc.addFeed(result[0] as String, result[1] as int?); );
} if (result != null) {
}, bloc.addFeed(result[0] as String, result[1] as int?);
tooltip: AppLocalizations.of(context).feedAdd, }
child: const Icon(Icons.add), },
); 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 { class NewsFeedsView extends StatelessWidget {
const NewsFeedsView({ const NewsFeedsView({
required this.bloc,
this.folderID, this.folderID,
super.key, super.key,
}); });
final NewsBloc bloc;
final int? folderID; final int? folderID;
@override @override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFolder>>( Widget build(final BuildContext context) {
stream: bloc.folders, final bloc = Provider.of<NewsBloc>(context, listen: false);
builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: bloc.feeds, return ResultBuilder<List<NextcloudNewsFolder>>(
builder: (final context, final feeds) => SortBoxBuilder<FeedsSortProperty, NextcloudNewsFeed>( stream: bloc.folders,
sortBox: feedsSortBox, builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
sortPropertyOption: bloc.options.feedsSortPropertyOption, stream: bloc.feeds,
sortBoxOrderOption: bloc.options.feedsSortBoxOrderOption, builder: (final context, final feeds) => SortBoxBuilder<FeedsSortProperty, NextcloudNewsFeed>(
input: folders.data == null sortBox: feedsSortBox,
? null sortPropertyOption: bloc.options.feedsSortPropertyOption,
: feeds.data?.where((final f) => folderID == null || f.folderId == folderID).toList(), sortBoxOrderOption: bloc.options.feedsSortBoxOrderOption,
builder: (final context, final sorted) => NeonListView<NextcloudNewsFeed>( input: folders.data == null
scrollKey: 'news-feeds', ? null
withFloatingActionButton: true, : feeds.data?.where((final f) => folderID == null || f.folderId == folderID).toList(),
items: sorted, builder: (final context, final sorted) => NeonListView<NextcloudNewsFeed>(
isLoading: feeds.loading || folders.loading, scrollKey: 'news-feeds',
error: feeds.error ?? folders.error, withFloatingActionButton: true,
onRefresh: bloc.refresh, items: sorted,
builder: (final context, final feed) => _buildFeed( isLoading: feeds.loading || folders.loading,
context, error: feeds.error ?? folders.error,
feed, onRefresh: bloc.refresh,
folders.data!, builder: (final context, final feed) => _buildFeed(
), context,
feed,
folders.data!,
), ),
), ),
), ),
); ),
);
}
Widget _buildFeed( Widget _buildFeed(
final BuildContext context, final BuildContext context,
final NextcloudNewsFeed feed, final NextcloudNewsFeed feed,
final List<NextcloudNewsFolder> folders, final List<NextcloudNewsFolder> folders,
) => ) {
ListTile( final bloc = Provider.of<NewsBloc>(context, listen: false);
title: Text(
feed.title, return ListTile(
style: feed.unreadCount! == 0 title: Text(
? Theme.of(context).textTheme.titleMedium!.copyWith(color: Theme.of(context).disabledColor) feed.title,
: null, style: feed.unreadCount! == 0
), ? Theme.of(context).textTheme.titleMedium!.copyWith(color: Theme.of(context).disabledColor)
subtitle: : null,
feed.unreadCount! > 0 ? Text(AppLocalizations.of(context).articlesUnread(feed.unreadCount!)) : Container(), ),
leading: NewsFeedIcon( subtitle:
feed: feed, feed.unreadCount! > 0 ? Text(AppLocalizations.of(context).articlesUnread(feed.unreadCount!)) : Container(),
borderRadius: const BorderRadius.all(Radius.circular(8)), leading: NewsFeedIcon(
), feed: feed,
trailing: Row( borderRadius: const BorderRadius.all(Radius.circular(8)),
mainAxisSize: MainAxisSize.min, ),
children: [ trailing: Row(
if (feed.updateErrorCount > 0) ...[ mainAxisSize: MainAxisSize.min,
IconButton( children: [
onPressed: () async { if (feed.updateErrorCount > 0) ...[
await showDialog( IconButton(
context: context, onPressed: () async {
builder: (final context) => NewsFeedUpdateErrorDialog( await showDialog(
feed: feed, 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,
), ),
);
},
tooltip: AppLocalizations.of(context).feedShowErrorMessage,
iconSize: 30,
icon: Text(
feed.updateErrorCount.toString(),
style: const TextStyle(
color: Colors.red,
), ),
), ),
], ),
PopupMenuButton<NewsFeedAction>( ],
itemBuilder: (final context) => [ PopupMenuButton<NewsFeedAction>(
PopupMenuItem( itemBuilder: (final context) => [
value: NewsFeedAction.showURL, PopupMenuItem(
child: Text(AppLocalizations.of(context).feedShowURL), value: NewsFeedAction.showURL,
), child: Text(AppLocalizations.of(context).feedShowURL),
PopupMenuItem( ),
value: NewsFeedAction.delete, PopupMenuItem(
child: Text(AppLocalizations.of(context).actionDelete), value: NewsFeedAction.delete,
), child: Text(AppLocalizations.of(context).actionDelete),
),
PopupMenuItem(
value: NewsFeedAction.rename,
child: Text(AppLocalizations.of(context).actionRename),
),
if (folders.isNotEmpty) ...[
PopupMenuItem( PopupMenuItem(
value: NewsFeedAction.rename, value: NewsFeedAction.move,
child: Text(AppLocalizations.of(context).actionRename), 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) { onSelected: (final action) async {
case NewsFeedAction.showURL: switch (action) {
await showDialog( case NewsFeedAction.showURL:
context: context, await showDialog(
builder: (final context) => NewsFeedShowURLDialog( context: context,
feed: feed, builder: (final context) => NewsFeedShowURLDialog(
), feed: feed,
); ),
break; );
case NewsFeedAction.delete: break;
case NewsFeedAction.delete:
// ignore: use_build_context_synchronously
if (await showConfirmationDialog(
context,
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
if (await showConfirmationDialog( AppLocalizations.of(context).feedRemoveConfirm(feed.title),
context, )) {
// ignore: use_build_context_synchronously bloc.removeFeed(feed.id);
AppLocalizations.of(context).feedRemoveConfirm(feed.title), }
)) { break;
bloc.removeFeed(feed.id); case NewsFeedAction.rename:
} final result = await showRenameDialog(
break; context: context,
case NewsFeedAction.rename: title: AppLocalizations.of(context).feedRename,
final result = await showRenameDialog( value: feed.title,
context: context, );
title: AppLocalizations.of(context).feedRename, if (result != null) {
value: feed.title, bloc.renameFeed(feed.id, result);
); }
if (result != null) { break;
bloc.renameFeed(feed.id, result); case NewsFeedAction.move:
} final result = await showDialog<List<int?>>(
break; context: context,
case NewsFeedAction.move: builder: (final context) => NewsMoveFeedDialog(
final result = await showDialog<List<int?>>( folders: folders,
context: context, feed: feed,
builder: (final context) => NewsMoveFeedDialog( ),
folders: folders, );
feed: feed, if (result != null) {
), bloc.moveFeed(feed.id, result[0]);
); }
if (result != null) { break;
bloc.moveFeed(feed.id, result[0]); }
} },
break; ),
} ],
}, ),
), onLongPress: () {
], if (feed.unreadCount! > 0) {
), bloc.markFeedAsRead(feed.id);
onLongPress: () { }
if (feed.unreadCount! > 0) { },
bloc.markFeedAsRead(feed.id); onTap: () async {
} await Navigator.of(context).push(
}, MaterialPageRoute(
onTap: () async { builder: (final context) => NewsFeedPage(
await Navigator.of(context).push( feed: feed,
MaterialPageRoute(
builder: (final context) => NewsFeedPage(
bloc: bloc,
feed: feed,
),
), ),
); ),
}, );
); },
);
}
} }
enum NewsFeedAction { 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 { class NewsFolderFloatingActionButton extends StatelessWidget {
const NewsFolderFloatingActionButton({ const NewsFolderFloatingActionButton({
required this.bloc,
super.key, super.key,
}); });
final NewsBloc bloc;
@override @override
Widget build(final BuildContext context) => FloatingActionButton( Widget build(final BuildContext context) {
onPressed: () async { final bloc = Provider.of<NewsBloc>(context, listen: false);
final result = await showDialog<String>(
context: context, return FloatingActionButton(
builder: (final context) => const NewsCreateFolderDialog(), onPressed: () async {
); final result = await showDialog<String>(
if (result != null) { context: context,
bloc.createFolder(result); builder: (final context) => const NewsCreateFolderDialog(),
} );
}, if (result != null) {
tooltip: AppLocalizations.of(context).folderCreate, bloc.createFolder(result);
child: const Icon(Icons.add), }
); },
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 { class NewsFolderView extends StatefulWidget {
const NewsFolderView({ const NewsFolderView({
required this.bloc,
required this.folder, required this.folder,
super.key, super.key,
}); });
final NewsBloc bloc;
final NextcloudNewsFolder folder; final NextcloudNewsFolder folder;
@override @override
@ -15,9 +13,17 @@ class NewsFolderView extends StatefulWidget {
} }
class _NewsFolderViewState extends State<NewsFolderView> { 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; late DefaultFolderViewType _viewType = option.value;
@override
void initState() {
bloc = Provider.of<NewsBloc>(context, listen: false);
super.initState();
}
@override @override
Widget build(final BuildContext context) => Column( Widget build(final BuildContext context) => Column(
children: [ children: [
@ -45,17 +51,15 @@ class _NewsFolderViewState extends State<NewsFolderView> {
child: _viewType == DefaultFolderViewType.articles child: _viewType == DefaultFolderViewType.articles
? NewsArticlesView( ? NewsArticlesView(
bloc: NewsArticlesBloc( bloc: NewsArticlesBloc(
widget.bloc, bloc,
widget.bloc.options, bloc.options,
widget.bloc.requestManager, bloc.requestManager,
widget.bloc.client, bloc.client,
id: widget.folder.id, id: widget.folder.id,
listType: ListType.folder, listType: ListType.folder,
), ),
newsBloc: widget.bloc,
) )
: NewsFeedsView( : NewsFeedsView(
bloc: widget.bloc,
folderID: widget.folder.id, 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 { class NewsFoldersView extends StatelessWidget {
const NewsFoldersView({ const NewsFoldersView({
required this.bloc,
super.key, super.key,
}); });
final NewsBloc bloc;
@override @override
Widget build(final BuildContext context) => ResultBuilder<List<NextcloudNewsFolder>>( Widget build(final BuildContext context) {
stream: bloc.folders, final bloc = Provider.of<NewsBloc>(context, listen: false);
builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
stream: bloc.feeds, return ResultBuilder<List<NextcloudNewsFolder>>(
builder: (final context, final feeds) => SortBoxBuilder<FoldersSortProperty, FolderFeedsWrapper>( stream: bloc.folders,
sortBox: foldersSortBox, builder: (final context, final folders) => ResultBuilder<List<NextcloudNewsFeed>>(
sortPropertyOption: bloc.options.foldersSortPropertyOption, stream: bloc.feeds,
sortBoxOrderOption: bloc.options.foldersSortBoxOrderOption, builder: (final context, final feeds) => SortBoxBuilder<FoldersSortProperty, FolderFeedsWrapper>(
input: feeds.data == null sortBox: foldersSortBox,
? null sortPropertyOption: bloc.options.foldersSortPropertyOption,
: folders.data sortBoxOrderOption: bloc.options.foldersSortBoxOrderOption,
?.map( input: feeds.data == null
(final folder) => FolderFeedsWrapper( ? null
folder, : folders.data
feeds.data!.where((final feed) => feed.folderId == folder.id).toList(), ?.map(
), (final folder) => FolderFeedsWrapper(
) folder,
.toList(), feeds.data!.where((final feed) => feed.folderId == folder.id).toList(),
builder: (final context, final sorted) => NeonListView<FolderFeedsWrapper>( ),
scrollKey: 'news-folders', )
withFloatingActionButton: true, .toList(),
items: sorted, builder: (final context, final sorted) => NeonListView<FolderFeedsWrapper>(
isLoading: feeds.loading || folders.loading, scrollKey: 'news-folders',
error: feeds.error ?? folders.error, withFloatingActionButton: true,
onRefresh: bloc.refresh, items: sorted,
builder: _buildFolder, isLoading: feeds.loading || folders.loading,
), error: feeds.error ?? folders.error,
onRefresh: bloc.refresh,
builder: _buildFolder,
), ),
), ),
); ),
);
}
Widget _buildFolder( Widget _buildFolder(
final BuildContext context, final BuildContext context,
final FolderFeedsWrapper folderFeedsWrapper, final FolderFeedsWrapper folderFeedsWrapper,
) { ) {
final bloc = Provider.of<NewsBloc>(context, listen: false);
final unreadCount = feedsUnreadCountSum(folderFeedsWrapper.feeds); final unreadCount = feedsUnreadCountSum(folderFeedsWrapper.feeds);
return ListTile( return ListTile(
title: Text( title: Text(
folderFeedsWrapper.folder.name, folderFeedsWrapper.folder.name,
@ -122,7 +125,6 @@ class NewsFoldersView extends StatelessWidget {
await Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (final context) => NewsFolderPage( builder: (final context) => NewsFolderPage(
bloc: bloc,
folder: folderFeedsWrapper.folder, folder: folderFeedsWrapper.folder,
), ),
), ),

Loading…
Cancel
Save