A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
4.4 KiB

part of '../neon_news.dart';
3 years ago
class NewsFoldersView extends StatelessWidget {
const NewsFoldersView({
required this.bloc,
super.key,
});
final NewsBloc bloc;
@override
Widget build(final BuildContext context) => ResultBuilder<List<news.Folder>>.behaviorSubject(
subject: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<news.Feed>>.behaviorSubject(
subject: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FoldersSortProperty, FolderFeedsWrapper>(
sortBox: foldersSortBox,
sortProperty: bloc.options.foldersSortPropertyOption,
sortBoxOrder: bloc.options.foldersSortBoxOrderOption,
input: feeds.hasData
? folders.data?.map((final folder) {
final feedsInFolder = feeds.requireData.where((final feed) => feed.folderId == folder.id);
final feedCount = feedsInFolder.length;
final unreadCount = feedsInFolder.fold(0, (final a, final b) => a + b.unreadCount!);
return (folder: folder, feedCount: feedCount, unreadCount: unreadCount);
}).toList()
: null,
builder: (final context, final sorted) => NeonListView(
scrollKey: 'news-folders',
isLoading: feeds.isLoading || folders.isLoading,
error: feeds.error ?? folders.error,
onRefresh: bloc.refresh,
itemCount: sorted.length,
itemBuilder: (final context, final index) => _buildFolder(
context,
sorted[index],
),
3 years ago
),
),
),
);
Widget _buildFolder(
final BuildContext context,
final FolderFeedsWrapper folderFeedsWrapper,
) {
final (folder: folder, feedCount: feedCount, unreadCount: unreadCount) = folderFeedsWrapper;
3 years ago
return ListTile(
title: Text(
folder.name,
3 years ago
style: unreadCount == 0
? Theme.of(context).textTheme.titleMedium!.copyWith(color: Theme.of(context).disabledColor)
3 years ago
: null,
),
subtitle: unreadCount > 0 ? Text(NewsLocalizations.of(context).articlesUnread(unreadCount)) : const SizedBox(),
leading: SizedBox.square(
dimension: largeIconSize,
3 years ago
child: Stack(
children: [
Icon(
Icons.folder,
size: largeIconSize,
3 years ago
color: Theme.of(context).colorScheme.primary,
),
Center(
child: Text(
feedCount.toString(),
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
),
),
3 years ago
),
],
),
),
trailing: PopupMenuButton<NewsFolderAction>(
3 years ago
itemBuilder: (final context) => [
PopupMenuItem(
value: NewsFolderAction.delete,
child: Text(NewsLocalizations.of(context).actionDelete),
3 years ago
),
PopupMenuItem(
value: NewsFolderAction.rename,
child: Text(NewsLocalizations.of(context).actionRename),
3 years ago
),
],
onSelected: (final action) async {
switch (action) {
case NewsFolderAction.delete:
3 years ago
if (await showConfirmationDialog(
context,
NewsLocalizations.of(context).folderDeleteConfirm(folder.name),
3 years ago
)) {
bloc.deleteFolder(folder.id);
3 years ago
}
case NewsFolderAction.rename:
if (!context.mounted) {
return;
}
3 years ago
final result = await showRenameDialog(
context: context,
title: NewsLocalizations.of(context).folderRename,
value: folder.name,
3 years ago
);
if (result != null) {
bloc.renameFolder(folder.id, result);
3 years ago
}
}
},
),
onLongPress: () {
if (unreadCount > 0) {
bloc.markFolderAsRead(folder.id);
3 years ago
}
},
onTap: () async {
await Navigator.of(context).push(
MaterialPageRoute<void>(
3 years ago
builder: (final context) => NewsFolderPage(
bloc: bloc,
folder: folder,
3 years ago
),
),
);
},
);
}
}
enum NewsFolderAction {
3 years ago
delete,
rename,
}