diff --git a/lib/screens/downloads_screen.dart b/lib/screens/downloads_screen.dart index 2646e71a..a2a9d8fa 100644 --- a/lib/screens/downloads_screen.dart +++ b/lib/screens/downloads_screen.dart @@ -22,6 +22,7 @@ class _DownloadsScreenState extends State { // Variables related to the filter bar in the appbar final TextEditingController _filterController = TextEditingController(); List _filteredDownloads = []; + bool _useAndFilter = true; @override void initState() { @@ -48,12 +49,21 @@ class _DownloadsScreenState extends State { if (query.isEmpty) { _filteredDownloads = List.from(_downloadedArticles); } else { + List keywords = query.toLowerCase().split(' '); + _filteredDownloads = _downloadedArticles.where((article) { - final title = article.publicationCard.title.toLowerCase(); - final journalTitle = - article.publicationCard.journalTitle.toLowerCase(); - return title.contains(query.toLowerCase()) || - journalTitle.contains(query.toLowerCase()); + bool matchesAnyField(String word) { + return article.publicationCard.title.toLowerCase().contains(word) || + article.publicationCard.journalTitle + .toLowerCase() + .contains(word); + } + + if (_useAndFilter) { + return keywords.every(matchesAnyField); // AND logic + } else { + return keywords.any(matchesAnyField); // OR logic + } }).toList(); } }); @@ -82,23 +92,40 @@ class _DownloadsScreenState extends State { prefixIcon: Icon(Icons.search), filled: true, fillColor: Color.fromARGB(31, 148, 147, 147), - suffixIcon: PopupMenuButton( - icon: Icon(Icons.more_vert), - onSelected: (item) => handleMenuButton(item), - itemBuilder: (context) => [ - PopupMenuItem( - value: 0, - child: ListTile( - leading: Icon(Icons.sort), - title: Text(AppLocalizations.of(context)!.sortby), + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () { + setState(() { + _useAndFilter = !_useAndFilter; + _filterDownloads(_filterController.text); + }); + }, + child: Text( + _useAndFilter ? 'AND' : 'OR', ), ), - PopupMenuItem( - value: 1, - child: ListTile( - leading: Icon(Icons.sort_by_alpha), - title: Text(AppLocalizations.of(context)!.sortorder), - ), + PopupMenuButton( + icon: Icon(Icons.more_vert), + onSelected: (item) => handleMenuButton(item), + itemBuilder: (context) => [ + PopupMenuItem( + value: 0, + child: ListTile( + leading: Icon(Icons.sort), + title: Text(AppLocalizations.of(context)!.sortby), + ), + ), + PopupMenuItem( + value: 1, + child: ListTile( + leading: Icon(Icons.sort_by_alpha), + title: + Text(AppLocalizations.of(context)!.sortorder), + ), + ), + ], ), ], )), diff --git a/lib/screens/favorites_screen.dart b/lib/screens/favorites_screen.dart index 611ef874..1cc1e281 100644 --- a/lib/screens/favorites_screen.dart +++ b/lib/screens/favorites_screen.dart @@ -25,6 +25,8 @@ class _FavoritesScreenState extends State { List _allFavorites = []; List _filteredFavorites = []; + bool _useAndFilter = true; + @override void initState() { super.initState(); @@ -64,24 +66,28 @@ class _FavoritesScreenState extends State { if (query.isEmpty) { _filteredFavorites = List.from(_allFavorites); } else { - _filteredFavorites = _allFavorites - .where((publication) => - publication.title.toLowerCase().contains(query.toLowerCase()) || - publication.journalTitle - .toLowerCase() - .contains(query.toLowerCase()) || - publication.abstract - .toLowerCase() - .contains(query.toLowerCase()) || - publication.licenseName - .toLowerCase() - .contains(query.toLowerCase()) || - publication.authors.any((author) => author.family - .toLowerCase() - .contains(query.toLowerCase())) || - publication.authors.any((author) => - author.given.toLowerCase().contains(query.toLowerCase()))) - .toList(); + List keywords = query.toLowerCase().split(' '); + + _filteredFavorites = _allFavorites.where((publication) { + bool matchesAnyField(String word, PublicationCard pub) { + return pub.title.toLowerCase().contains(word) || + pub.journalTitle.toLowerCase().contains(word) || + pub.abstract.toLowerCase().contains(word) || + pub.licenseName.toLowerCase().contains(word) || + pub.authors.any( + (author) => author.family.toLowerCase().contains(word)) || + pub.authors + .any((author) => author.given.toLowerCase().contains(word)); + } + + if (_useAndFilter) { + return keywords.every( + (word) => matchesAnyField(word, publication)); // AND logic + } else { + return keywords + .any((word) => matchesAnyField(word, publication)); // OR logic + } + }).toList(); } _filteredFavorites = _sortFavorites(_filteredFavorites); }); @@ -110,23 +116,39 @@ class _FavoritesScreenState extends State { prefixIcon: Icon(Icons.search), filled: true, fillColor: Color.fromARGB(31, 148, 147, 147), - suffixIcon: PopupMenuButton( - icon: Icon(Icons.more_vert), - onSelected: (item) => handleMenuButton(item), - itemBuilder: (context) => [ - PopupMenuItem( - value: 0, - child: ListTile( - leading: Icon(Icons.sort), - title: Text(AppLocalizations.of(context)!.sortby), + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () { + setState(() { + _useAndFilter = !_useAndFilter; + _filterFeed(_filterController.text); + }); + }, + child: Text( + _useAndFilter ? 'AND' : 'OR', ), ), - PopupMenuItem( - value: 1, - child: ListTile( - leading: Icon(Icons.sort_by_alpha), - title: Text(AppLocalizations.of(context)!.sortorder), - ), + PopupMenuButton( + icon: Icon(Icons.more_vert), + onSelected: (item) => handleMenuButton(item), + itemBuilder: (context) => [ + PopupMenuItem( + value: 0, + child: ListTile( + leading: Icon(Icons.sort), + title: Text(AppLocalizations.of(context)!.sortby), + ), + ), + PopupMenuItem( + value: 1, + child: ListTile( + leading: Icon(Icons.sort_by_alpha), + title: Text(AppLocalizations.of(context)!.sortorder), + ), + ), + ], ), ], ), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index af645926..6306b396 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -35,6 +35,7 @@ class _HomeScreenState extends State { List> savedQueries = []; bool _feedLoaded = false; // Needed to avoid conflicts wih onAbstractChanged + bool _useAndFilter = true; @override void initState() { @@ -165,24 +166,28 @@ class _HomeScreenState extends State { if (query.isEmpty) { _filteredFeed = List.from(_allFeed); } else { - _filteredFeed = _allFeed - .where((publication) => - publication.title.toLowerCase().contains(query.toLowerCase()) || - publication.journalTitle - .toLowerCase() - .contains(query.toLowerCase()) || - publication.abstract - .toLowerCase() - .contains(query.toLowerCase()) || - publication.licenseName - .toLowerCase() - .contains(query.toLowerCase()) || - publication.authors.any((author) => author.family - .toLowerCase() - .contains(query.toLowerCase())) || - publication.authors.any((author) => - author.given.toLowerCase().contains(query.toLowerCase()))) - .toList(); + List keywords = query.toLowerCase().split(' '); + + _filteredFeed = _allFeed.where((publication) { + bool matchesAnyField(String word, PublicationCard pub) { + return pub.title.toLowerCase().contains(word) || + pub.journalTitle.toLowerCase().contains(word) || + pub.abstract.toLowerCase().contains(word) || + pub.licenseName.toLowerCase().contains(word) || + pub.authors.any( + (author) => author.family.toLowerCase().contains(word)) || + pub.authors + .any((author) => author.given.toLowerCase().contains(word)); + } + + if (_useAndFilter) { + return keywords.every( + (word) => matchesAnyField(word, publication)); // AND logic + } else { + return keywords + .any((word) => matchesAnyField(word, publication)); // OR logic + } + }).toList(); } _sortFeed(); }); @@ -269,30 +274,46 @@ class _HomeScreenState extends State { prefixIcon: Icon(Icons.search), filled: true, fillColor: Color.fromARGB(31, 148, 147, 147), - suffixIcon: PopupMenuButton( - icon: Icon(Icons.more_vert), - onSelected: (item) => handleMenuButton(item), - itemBuilder: (context) => [ - PopupMenuItem( - value: 0, - child: ListTile( - leading: Icon(Icons.settings_outlined), - title: Text(AppLocalizations.of(context)!.settings), - ), - ), - PopupMenuItem( - value: 1, - child: ListTile( - leading: Icon(Icons.sort), - title: Text(AppLocalizations.of(context)!.sortby), + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () { + setState(() { + _useAndFilter = !_useAndFilter; + _filterFeed(_filterController.text); + }); + }, + child: Text( + _useAndFilter ? 'AND' : 'OR', ), ), - PopupMenuItem( - value: 2, - child: ListTile( - leading: Icon(Icons.sort_by_alpha), - title: Text(AppLocalizations.of(context)!.sortorder), - ), + PopupMenuButton( + icon: Icon(Icons.more_vert), + onSelected: (item) => handleMenuButton(item), + itemBuilder: (context) => [ + PopupMenuItem( + value: 0, + child: ListTile( + leading: Icon(Icons.settings_outlined), + title: Text(AppLocalizations.of(context)!.settings), + ), + ), + PopupMenuItem( + value: 1, + child: ListTile( + leading: Icon(Icons.sort), + title: Text(AppLocalizations.of(context)!.sortby), + ), + ), + PopupMenuItem( + value: 2, + child: ListTile( + leading: Icon(Icons.sort_by_alpha), + title: Text(AppLocalizations.of(context)!.sortorder), + ), + ), + ], ), ], ),