|  |  |  | @ -42,128 +42,132 @@ class NewsFeedsView extends StatelessWidget { | 
			
		
	
		
			
				
					|  |  |  |  |     final BuildContext context, | 
			
		
	
		
			
				
					|  |  |  |  |     final news.Feed feed, | 
			
		
	
		
			
				
					|  |  |  |  |     final List<news.Folder> folders, | 
			
		
	
		
			
				
					|  |  |  |  |   ) => | 
			
		
	
		
			
				
					|  |  |  |  |       ListTile( | 
			
		
	
		
			
				
					|  |  |  |  |         title: Text( | 
			
		
	
		
			
				
					|  |  |  |  |           feed.title, | 
			
		
	
		
			
				
					|  |  |  |  |           style: feed.unreadCount! == 0 | 
			
		
	
		
			
				
					|  |  |  |  |               ? Theme.of(context).textTheme.titleMedium!.copyWith(color: Theme.of(context).disabledColor) | 
			
		
	
		
			
				
					|  |  |  |  |               : null, | 
			
		
	
		
			
				
					|  |  |  |  |   ) { | 
			
		
	
		
			
				
					|  |  |  |  |     Widget trailing = PopupMenuButton<NewsFeedAction>( | 
			
		
	
		
			
				
					|  |  |  |  |       itemBuilder: (final context) => [ | 
			
		
	
		
			
				
					|  |  |  |  |         PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |           value: NewsFeedAction.showURL, | 
			
		
	
		
			
				
					|  |  |  |  |           child: Text(NewsLocalizations.of(context).feedShowURL), | 
			
		
	
		
			
				
					|  |  |  |  |         ), | 
			
		
	
		
			
				
					|  |  |  |  |         subtitle: feed.unreadCount! > 0 | 
			
		
	
		
			
				
					|  |  |  |  |             ? Text(NewsLocalizations.of(context).articlesUnread(feed.unreadCount!)) | 
			
		
	
		
			
				
					|  |  |  |  |             : const SizedBox(), | 
			
		
	
		
			
				
					|  |  |  |  |         leading: NewsFeedIcon(feed: feed), | 
			
		
	
		
			
				
					|  |  |  |  |         trailing: Row( | 
			
		
	
		
			
				
					|  |  |  |  |           mainAxisSize: MainAxisSize.min, | 
			
		
	
		
			
				
					|  |  |  |  |           children: [ | 
			
		
	
		
			
				
					|  |  |  |  |             if (feed.updateErrorCount > 0) ...[ | 
			
		
	
		
			
				
					|  |  |  |  |               IconButton( | 
			
		
	
		
			
				
					|  |  |  |  |                 onPressed: () async { | 
			
		
	
		
			
				
					|  |  |  |  |                   await showDialog<void>( | 
			
		
	
		
			
				
					|  |  |  |  |                     context: context, | 
			
		
	
		
			
				
					|  |  |  |  |                     builder: (final context) => NewsFeedUpdateErrorDialog( | 
			
		
	
		
			
				
					|  |  |  |  |                       feed: feed, | 
			
		
	
		
			
				
					|  |  |  |  |                     ), | 
			
		
	
		
			
				
					|  |  |  |  |                   ); | 
			
		
	
		
			
				
					|  |  |  |  |                 }, | 
			
		
	
		
			
				
					|  |  |  |  |                 tooltip: NewsLocalizations.of(context).feedShowErrorMessage, | 
			
		
	
		
			
				
					|  |  |  |  |                 iconSize: 30, | 
			
		
	
		
			
				
					|  |  |  |  |                 icon: Text( | 
			
		
	
		
			
				
					|  |  |  |  |                   feed.updateErrorCount.toString(), | 
			
		
	
		
			
				
					|  |  |  |  |                   style: TextStyle( | 
			
		
	
		
			
				
					|  |  |  |  |                     color: Theme.of(context).colorScheme.error, | 
			
		
	
		
			
				
					|  |  |  |  |                   ), | 
			
		
	
		
			
				
					|  |  |  |  |                 ), | 
			
		
	
		
			
				
					|  |  |  |  |               ), | 
			
		
	
		
			
				
					|  |  |  |  |             ], | 
			
		
	
		
			
				
					|  |  |  |  |             PopupMenuButton<NewsFeedAction>( | 
			
		
	
		
			
				
					|  |  |  |  |               itemBuilder: (final context) => [ | 
			
		
	
		
			
				
					|  |  |  |  |                 PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |                   value: NewsFeedAction.showURL, | 
			
		
	
		
			
				
					|  |  |  |  |                   child: Text(NewsLocalizations.of(context).feedShowURL), | 
			
		
	
		
			
				
					|  |  |  |  |                 ), | 
			
		
	
		
			
				
					|  |  |  |  |                 PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |                   value: NewsFeedAction.delete, | 
			
		
	
		
			
				
					|  |  |  |  |                   child: Text(NewsLocalizations.of(context).actionDelete), | 
			
		
	
		
			
				
					|  |  |  |  |                 ), | 
			
		
	
		
			
				
					|  |  |  |  |                 PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |                   value: NewsFeedAction.rename, | 
			
		
	
		
			
				
					|  |  |  |  |                   child: Text(NewsLocalizations.of(context).actionRename), | 
			
		
	
		
			
				
					|  |  |  |  |                 ), | 
			
		
	
		
			
				
					|  |  |  |  |                 if (folders.isNotEmpty) ...[ | 
			
		
	
		
			
				
					|  |  |  |  |                   PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |                     value: NewsFeedAction.move, | 
			
		
	
		
			
				
					|  |  |  |  |                     child: Text(NewsLocalizations.of(context).actionMove), | 
			
		
	
		
			
				
					|  |  |  |  |                   ), | 
			
		
	
		
			
				
					|  |  |  |  |                 ], | 
			
		
	
		
			
				
					|  |  |  |  |               ], | 
			
		
	
		
			
				
					|  |  |  |  |               onSelected: (final action) async { | 
			
		
	
		
			
				
					|  |  |  |  |                 switch (action) { | 
			
		
	
		
			
				
					|  |  |  |  |                   case NewsFeedAction.showURL: | 
			
		
	
		
			
				
					|  |  |  |  |                     await showDialog<void>( | 
			
		
	
		
			
				
					|  |  |  |  |                       context: context, | 
			
		
	
		
			
				
					|  |  |  |  |                       builder: (final context) => NewsFeedShowURLDialog( | 
			
		
	
		
			
				
					|  |  |  |  |                         feed: feed, | 
			
		
	
		
			
				
					|  |  |  |  |                       ), | 
			
		
	
		
			
				
					|  |  |  |  |                     ); | 
			
		
	
		
			
				
					|  |  |  |  |                   case NewsFeedAction.delete: | 
			
		
	
		
			
				
					|  |  |  |  |                     if (!context.mounted) { | 
			
		
	
		
			
				
					|  |  |  |  |                       return; | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                     if (await showConfirmationDialog( | 
			
		
	
		
			
				
					|  |  |  |  |                       context, | 
			
		
	
		
			
				
					|  |  |  |  |                       NewsLocalizations.of(context).feedRemoveConfirm(feed.title), | 
			
		
	
		
			
				
					|  |  |  |  |                     )) { | 
			
		
	
		
			
				
					|  |  |  |  |                       bloc.removeFeed(feed.id); | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                   case NewsFeedAction.rename: | 
			
		
	
		
			
				
					|  |  |  |  |                     if (!context.mounted) { | 
			
		
	
		
			
				
					|  |  |  |  |                       return; | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                     final result = await showRenameDialog( | 
			
		
	
		
			
				
					|  |  |  |  |                       context: context, | 
			
		
	
		
			
				
					|  |  |  |  |                       title: NewsLocalizations.of(context).feedRename, | 
			
		
	
		
			
				
					|  |  |  |  |                       value: feed.title, | 
			
		
	
		
			
				
					|  |  |  |  |                     ); | 
			
		
	
		
			
				
					|  |  |  |  |                     if (result != null) { | 
			
		
	
		
			
				
					|  |  |  |  |                       bloc.renameFeed(feed.id, result); | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                   case NewsFeedAction.move: | 
			
		
	
		
			
				
					|  |  |  |  |                     if (!context.mounted) { | 
			
		
	
		
			
				
					|  |  |  |  |                       return; | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                     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]); | 
			
		
	
		
			
				
					|  |  |  |  |                     } | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |               }, | 
			
		
	
		
			
				
					|  |  |  |  |             ), | 
			
		
	
		
			
				
					|  |  |  |  |           ], | 
			
		
	
		
			
				
					|  |  |  |  |         PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |           value: NewsFeedAction.delete, | 
			
		
	
		
			
				
					|  |  |  |  |           child: Text(NewsLocalizations.of(context).actionDelete), | 
			
		
	
		
			
				
					|  |  |  |  |         ), | 
			
		
	
		
			
				
					|  |  |  |  |         onLongPress: () { | 
			
		
	
		
			
				
					|  |  |  |  |           if (feed.unreadCount! > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |             bloc.markFeedAsRead(feed.id); | 
			
		
	
		
			
				
					|  |  |  |  |           } | 
			
		
	
		
			
				
					|  |  |  |  |         }, | 
			
		
	
		
			
				
					|  |  |  |  |         onTap: () async { | 
			
		
	
		
			
				
					|  |  |  |  |           await Navigator.of(context).push( | 
			
		
	
		
			
				
					|  |  |  |  |             MaterialPageRoute<void>( | 
			
		
	
		
			
				
					|  |  |  |  |               builder: (final context) => NewsFeedPage( | 
			
		
	
		
			
				
					|  |  |  |  |                 bloc: bloc, | 
			
		
	
		
			
				
					|  |  |  |  |         PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |           value: NewsFeedAction.rename, | 
			
		
	
		
			
				
					|  |  |  |  |           child: Text(NewsLocalizations.of(context).actionRename), | 
			
		
	
		
			
				
					|  |  |  |  |         ), | 
			
		
	
		
			
				
					|  |  |  |  |         if (folders.isNotEmpty) ...[ | 
			
		
	
		
			
				
					|  |  |  |  |           PopupMenuItem( | 
			
		
	
		
			
				
					|  |  |  |  |             value: NewsFeedAction.move, | 
			
		
	
		
			
				
					|  |  |  |  |             child: Text(NewsLocalizations.of(context).actionMove), | 
			
		
	
		
			
				
					|  |  |  |  |           ), | 
			
		
	
		
			
				
					|  |  |  |  |         ], | 
			
		
	
		
			
				
					|  |  |  |  |       ], | 
			
		
	
		
			
				
					|  |  |  |  |       onSelected: (final action) async { | 
			
		
	
		
			
				
					|  |  |  |  |         switch (action) { | 
			
		
	
		
			
				
					|  |  |  |  |           case NewsFeedAction.showURL: | 
			
		
	
		
			
				
					|  |  |  |  |             await showDialog<void>( | 
			
		
	
		
			
				
					|  |  |  |  |               context: context, | 
			
		
	
		
			
				
					|  |  |  |  |               builder: (final context) => NewsFeedShowURLDialog( | 
			
		
	
		
			
				
					|  |  |  |  |                 feed: feed, | 
			
		
	
		
			
				
					|  |  |  |  |               ), | 
			
		
	
		
			
				
					|  |  |  |  |             ); | 
			
		
	
		
			
				
					|  |  |  |  |           case NewsFeedAction.delete: | 
			
		
	
		
			
				
					|  |  |  |  |             if (!context.mounted) { | 
			
		
	
		
			
				
					|  |  |  |  |               return; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             if (await showConfirmationDialog( | 
			
		
	
		
			
				
					|  |  |  |  |               context, | 
			
		
	
		
			
				
					|  |  |  |  |               NewsLocalizations.of(context).feedRemoveConfirm(feed.title), | 
			
		
	
		
			
				
					|  |  |  |  |             )) { | 
			
		
	
		
			
				
					|  |  |  |  |               bloc.removeFeed(feed.id); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |           case NewsFeedAction.rename: | 
			
		
	
		
			
				
					|  |  |  |  |             if (!context.mounted) { | 
			
		
	
		
			
				
					|  |  |  |  |               return; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             final result = await showRenameDialog( | 
			
		
	
		
			
				
					|  |  |  |  |               context: context, | 
			
		
	
		
			
				
					|  |  |  |  |               title: NewsLocalizations.of(context).feedRename, | 
			
		
	
		
			
				
					|  |  |  |  |               value: feed.title, | 
			
		
	
		
			
				
					|  |  |  |  |             ); | 
			
		
	
		
			
				
					|  |  |  |  |             if (result != null) { | 
			
		
	
		
			
				
					|  |  |  |  |               bloc.renameFeed(feed.id, result); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |           case NewsFeedAction.move: | 
			
		
	
		
			
				
					|  |  |  |  |             if (!context.mounted) { | 
			
		
	
		
			
				
					|  |  |  |  |               return; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             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]); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |  |     if (feed.updateErrorCount > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       trailing = Row( | 
			
		
	
		
			
				
					|  |  |  |  |         mainAxisSize: MainAxisSize.min, | 
			
		
	
		
			
				
					|  |  |  |  |         children: [ | 
			
		
	
		
			
				
					|  |  |  |  |           IconButton( | 
			
		
	
		
			
				
					|  |  |  |  |             onPressed: () async { | 
			
		
	
		
			
				
					|  |  |  |  |               await showDialog<void>( | 
			
		
	
		
			
				
					|  |  |  |  |                 context: context, | 
			
		
	
		
			
				
					|  |  |  |  |                 builder: (final context) => NewsFeedUpdateErrorDialog( | 
			
		
	
		
			
				
					|  |  |  |  |                   feed: feed, | 
			
		
	
		
			
				
					|  |  |  |  |                 ), | 
			
		
	
		
			
				
					|  |  |  |  |               ); | 
			
		
	
		
			
				
					|  |  |  |  |             }, | 
			
		
	
		
			
				
					|  |  |  |  |             tooltip: NewsLocalizations.of(context).feedShowErrorMessage, | 
			
		
	
		
			
				
					|  |  |  |  |             iconSize: 30, | 
			
		
	
		
			
				
					|  |  |  |  |             icon: Text( | 
			
		
	
		
			
				
					|  |  |  |  |               feed.updateErrorCount.toString(), | 
			
		
	
		
			
				
					|  |  |  |  |               style: TextStyle( | 
			
		
	
		
			
				
					|  |  |  |  |                 color: Theme.of(context).colorScheme.error, | 
			
		
	
		
			
				
					|  |  |  |  |               ), | 
			
		
	
		
			
				
					|  |  |  |  |             ), | 
			
		
	
		
			
				
					|  |  |  |  |           ); | 
			
		
	
		
			
				
					|  |  |  |  |         }, | 
			
		
	
		
			
				
					|  |  |  |  |           ), | 
			
		
	
		
			
				
					|  |  |  |  |           trailing, | 
			
		
	
		
			
				
					|  |  |  |  |         ], | 
			
		
	
		
			
				
					|  |  |  |  |       ); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     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(NewsLocalizations.of(context).articlesUnread(feed.unreadCount!)) | 
			
		
	
		
			
				
					|  |  |  |  |           : const SizedBox(), | 
			
		
	
		
			
				
					|  |  |  |  |       leading: NewsFeedIcon(feed: feed), | 
			
		
	
		
			
				
					|  |  |  |  |       trailing: trailing, | 
			
		
	
		
			
				
					|  |  |  |  |       onLongPress: () { | 
			
		
	
		
			
				
					|  |  |  |  |         if (feed.unreadCount! > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |           bloc.markFeedAsRead(feed.id); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |  |       onTap: () async { | 
			
		
	
		
			
				
					|  |  |  |  |         await Navigator.of(context).push( | 
			
		
	
		
			
				
					|  |  |  |  |           MaterialPageRoute<void>( | 
			
		
	
		
			
				
					|  |  |  |  |             builder: (final context) => NewsFeedPage( | 
			
		
	
		
			
				
					|  |  |  |  |               bloc: bloc, | 
			
		
	
		
			
				
					|  |  |  |  |               feed: feed, | 
			
		
	
		
			
				
					|  |  |  |  |             ), | 
			
		
	
		
			
				
					|  |  |  |  |           ), | 
			
		
	
		
			
				
					|  |  |  |  |         ); | 
			
		
	
		
			
				
					|  |  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | enum NewsFeedAction { | 
			
		
	
	
		
			
				
					|  |  |  | 
 |