Add an 'All' folder to the local favorites page. Close #335

This commit is contained in:
2025-04-22 20:19:22 +08:00
parent a29a7cbaf3
commit 62e4056f4a
6 changed files with 119 additions and 114 deletions

View File

@@ -234,7 +234,7 @@ class LocalFavoritesManager with ChangeNotifier {
alter table "$folder" alter table "$folder"
add column translated_tags TEXT; add column translated_tags TEXT;
"""); """);
var comics = getAllComics(folder); var comics = getFolderComics(folder);
for (var comic in comics) { for (var comic in comics) {
var translatedTags = _translateTags(comic.tags); var translatedTags = _translateTags(comic.tags);
_db.execute(""" _db.execute("""
@@ -349,7 +349,7 @@ class LocalFavoritesManager with ChangeNotifier {
""").firstOrNull?["min_value"] ?? 0; """).firstOrNull?["min_value"] ?? 0;
} }
List<FavoriteItem> getAllComics(String folder) { List<FavoriteItem> getFolderComics(String folder) {
var rows = _db.select(""" var rows = _db.select("""
select * from "$folder" select * from "$folder"
ORDER BY display_order; ORDER BY display_order;
@@ -357,6 +357,17 @@ class LocalFavoritesManager with ChangeNotifier {
return rows.map((element) => FavoriteItem.fromRow(element)).toList(); return rows.map((element) => FavoriteItem.fromRow(element)).toList();
} }
List<FavoriteItem> getAllComics() {
var res = <FavoriteItem>{};
for (final folder in folderNames) {
var comics = _db.select("""
select * from "$folder";
""");
res.addAll(comics.map((element) => FavoriteItem.fromRow(element)));
}
return res.toList();
}
void addTagTo(String folder, String id, String tag) { void addTagTo(String folder, String id, String tag) {
_db.execute(""" _db.execute("""
update "$folder" update "$folder"
@@ -736,10 +747,10 @@ class LocalFavoritesManager with ChangeNotifier {
return comics; return comics;
} }
List<FavoriteItemWithFolderInfo> search(String keyword) { List<FavoriteItem> search(String keyword) {
var keywordList = keyword.split(" "); var keywordList = keyword.split(" ");
keyword = keywordList.first; keyword = keywordList.first;
var comics = <FavoriteItemWithFolderInfo>[]; var comics = <FavoriteItem>{};
for (var table in folderNames) { for (var table in folderNames) {
keyword = "%$keyword%"; keyword = "%$keyword%";
var res = _db.select(""" var res = _db.select("""
@@ -747,15 +758,18 @@ class LocalFavoritesManager with ChangeNotifier {
WHERE name LIKE ? OR author LIKE ? OR tags LIKE ? OR translated_tags LIKE ?; WHERE name LIKE ? OR author LIKE ? OR tags LIKE ? OR translated_tags LIKE ?;
""", [keyword, keyword, keyword, keyword]); """, [keyword, keyword, keyword, keyword]);
for (var comic in res) { for (var comic in res) {
comics.add( comics.add(FavoriteItem.fromRow(comic));
FavoriteItemWithFolderInfo(FavoriteItem.fromRow(comic), table));
} }
if (comics.length > 200) { if (comics.length > 200) {
break; break;
} }
} }
bool test(FavoriteItemWithFolderInfo comic, String keyword) { bool test(FavoriteItem comic, String keyword) {
keyword = keyword.trim();
if (keyword.isEmpty) {
return true;
}
if (comic.name.contains(keyword)) { if (comic.name.contains(keyword)) {
return true; return true;
} else if (comic.author.contains(keyword)) { } else if (comic.author.contains(keyword)) {
@@ -766,12 +780,14 @@ class LocalFavoritesManager with ChangeNotifier {
return false; return false;
} }
for (var i = 1; i < keywordList.length; i++) { return comics.where((element) {
comics = for (var i = 1; i < keywordList.length; i++) {
comics.where((element) => test(element, keywordList[i])).toList(); if (!test(element, keywordList[i])) {
} return false;
}
return comics; }
return true;
}).toList();
} }
void editTags(String id, String folder, List<String> tags) { void editTags(String id, String folder, List<String> tags) {

View File

@@ -133,7 +133,7 @@ void addFavorite(List<Comic> comics) {
} }
Future<List<FavoriteItem>> updateComicsInfo(String folder) async { Future<List<FavoriteItem>> updateComicsInfo(String folder) async {
var comics = LocalFavoritesManager().getAllComics(folder); var comics = LocalFavoritesManager().getFolderComics(folder);
Future<void> updateSingleComic(int index) async { Future<void> updateSingleComic(int index) async {
int retry = 3; int retry = 3;

View File

@@ -25,7 +25,6 @@ part 'favorite_actions.dart';
part 'side_bar.dart'; part 'side_bar.dart';
part 'local_favorites_page.dart'; part 'local_favorites_page.dart';
part 'network_favorites_page.dart'; part 'network_favorites_page.dart';
part 'local_search_page.dart';
const _kLeftBarWidth = 256.0; const _kLeftBarWidth = 256.0;

View File

@@ -1,5 +1,7 @@
part of 'favorites_page.dart'; part of 'favorites_page.dart';
const _localAllFolderLabel = '^_^[%local_all%]^_^';
class _LocalFavoritesPage extends StatefulWidget { class _LocalFavoritesPage extends StatefulWidget {
const _LocalFavoritesPage({required this.folder, super.key}); const _LocalFavoritesPage({required this.folder, super.key});
@@ -31,14 +33,25 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
int? lastSelectedIndex; int? lastSelectedIndex;
bool get isAllFolder => widget.folder == _localAllFolderLabel;
void updateComics() { void updateComics() {
if (keyword.isEmpty) { if (keyword.isEmpty) {
setState(() { setState(() {
comics = LocalFavoritesManager().getAllComics(widget.folder); if (isAllFolder) {
comics = LocalFavoritesManager().getAllComics();
} else {
comics = LocalFavoritesManager().getFolderComics(widget.folder);
}
}); });
} else { } else {
setState(() { setState(() {
comics = LocalFavoritesManager().searchInFolder(widget.folder, keyword); if (isAllFolder) {
comics = LocalFavoritesManager().search(keyword);
} else {
comics =
LocalFavoritesManager().searchInFolder(widget.folder, keyword);
}
}); });
} }
} }
@@ -46,10 +59,16 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
@override @override
void initState() { void initState() {
favPage = context.findAncestorStateOfType<_FavoritesPageState>()!; favPage = context.findAncestorStateOfType<_FavoritesPageState>()!;
comics = LocalFavoritesManager().getAllComics(widget.folder); if (!isAllFolder) {
var (a, b) = LocalFavoritesManager().findLinked(widget.folder); comics = LocalFavoritesManager().getFolderComics(widget.folder);
networkSource = a; var (a, b) = LocalFavoritesManager().findLinked(widget.folder);
networkFolder = b; networkSource = a;
networkFolder = b;
} else {
comics = LocalFavoritesManager().getAllComics();
networkSource = null;
networkFolder = null;
}
LocalFavoritesManager().addListener(updateComics); LocalFavoritesManager().addListener(updateComics);
super.initState(); super.initState();
} }
@@ -113,6 +132,11 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var title = favPage.folder ?? "Unselected".tl;
if (title == _localAllFolderLabel) {
title = "All".tl;
}
Widget body = SmoothCustomScrollView( Widget body = SmoothCustomScrollView(
controller: scrollController, controller: scrollController,
slivers: [ slivers: [
@@ -135,10 +159,10 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
onTap: context.width < _kTwoPanelChangeWidth onTap: context.width < _kTwoPanelChangeWidth
? favPage.showFolderSelector ? favPage.showFolderSelector
: null, : null,
child: Text(favPage.folder ?? "Unselected".tl), child: Text(title),
), ),
actions: [ actions: [
if (networkSource != null) if (networkSource != null && !isAllFolder)
Tooltip( Tooltip(
message: "Sync".tl, message: "Sync".tl,
child: Flyout( child: Flyout(
@@ -196,9 +220,10 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
}, },
), ),
), ),
MenuButton( if (!isAllFolder)
entries: [ MenuButton(
MenuEntry( entries: [
MenuEntry(
icon: Icons.edit_outlined, icon: Icons.edit_outlined,
text: "Rename".tl, text: "Rename".tl,
onClick: () { onClick: () {
@@ -220,8 +245,9 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
return null; return null;
}, },
); );
}), },
MenuEntry( ),
MenuEntry(
icon: Icons.reorder, icon: Icons.reorder,
text: "Reorder".tl, text: "Reorder".tl,
onClick: () { onClick: () {
@@ -241,8 +267,9 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
} }
}, },
); );
}), },
MenuEntry( ),
MenuEntry(
icon: Icons.upload_file, icon: Icons.upload_file,
text: "Export".tl, text: "Export".tl,
onClick: () { onClick: () {
@@ -253,8 +280,9 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
data: utf8.encode(json), data: utf8.encode(json),
filename: "${widget.folder}.json", filename: "${widget.folder}.json",
); );
}), },
MenuEntry( ),
MenuEntry(
icon: Icons.update, icon: Icons.update,
text: "Update Comics Info".tl, text: "Update Comics Info".tl,
onClick: () { onClick: () {
@@ -265,8 +293,9 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
}); });
} }
}); });
}), },
MenuEntry( ),
MenuEntry(
icon: Icons.delete_outline, icon: Icons.delete_outline,
text: "Delete Folder".tl, text: "Delete Folder".tl,
color: context.colorScheme.error, color: context.colorScheme.error,
@@ -284,9 +313,10 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
favPage.folderList?.updateFolders(); favPage.folderList?.updateFolders();
}, },
); );
}), },
], ),
), ],
),
], ],
) )
else if (multiSelectMode) else if (multiSelectMode)
@@ -330,22 +360,23 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
icon: Icons.flip, icon: Icons.flip,
text: "Invert Selection".tl, text: "Invert Selection".tl,
onClick: invertSelection), onClick: invertSelection),
MenuEntry( if (!isAllFolder)
icon: Icons.delete_outline, MenuEntry(
text: "Delete Comic".tl, icon: Icons.delete_outline,
color: context.colorScheme.error, text: "Delete Comic".tl,
onClick: () { color: context.colorScheme.error,
showConfirmDialog( onClick: () {
context: context, showConfirmDialog(
title: "Delete".tl, context: context,
content: "Delete @c comics?" title: "Delete".tl,
.tlParams({"c": selectedComics.length}), content: "Delete @c comics?"
btnColor: context.colorScheme.error, .tlParams({"c": selectedComics.length}),
onConfirm: () { btnColor: context.colorScheme.error,
_deleteComicWithId(); onConfirm: () {
}, _deleteComicWithId();
); },
}), );
}),
MenuEntry( MenuEntry(
icon: Icons.download, icon: Icons.download,
text: "Download".tl, text: "Download".tl,
@@ -404,17 +435,18 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
selections: selectedComics, selections: selectedComics,
menuBuilder: (c) { menuBuilder: (c) {
return [ return [
MenuEntry( if (!isAllFolder)
icon: Icons.delete, MenuEntry(
text: "Delete".tl, icon: Icons.delete,
onClick: () { text: "Delete".tl,
LocalFavoritesManager().deleteComicWithId( onClick: () {
widget.folder, LocalFavoritesManager().deleteComicWithId(
c.id, widget.folder,
(c as FavoriteItem).type, c.id,
); (c as FavoriteItem).type,
}, );
), },
),
MenuEntry( MenuEntry(
icon: Icons.check, icon: Icons.check,
text: "Select".tl, text: "Select".tl,
@@ -725,7 +757,7 @@ class _ReorderComicsPageState extends State<_ReorderComicsPage> {
final _key = GlobalKey(); final _key = GlobalKey();
var reorderWidgetKey = UniqueKey(); var reorderWidgetKey = UniqueKey();
final _scrollController = ScrollController(); final _scrollController = ScrollController();
late var comics = LocalFavoritesManager().getAllComics(widget.name); late var comics = LocalFavoritesManager().getFolderComics(widget.name);
bool changed = false; bool changed = false;
static int _floatToInt8(double x) { static int _floatToInt8(double x) {

View File

@@ -1,41 +0,0 @@
part of 'favorites_page.dart';
class LocalSearchPage extends StatefulWidget {
const LocalSearchPage({super.key});
@override
State<LocalSearchPage> createState() => _LocalSearchPageState();
}
class _LocalSearchPageState extends State<LocalSearchPage> {
String keyword = '';
var comics = <FavoriteItemWithFolderInfo>[];
late final SearchBarController controller;
@override
void initState() {
super.initState();
controller = SearchBarController(onSearch: (text) {
keyword = text;
comics = LocalFavoritesManager().search(keyword);
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SmoothCustomScrollView(slivers: [
SliverSearchBar(controller: controller),
SliverGridComics(
comics: comics,
badgeBuilder: (c) {
return (c as FavoriteItemWithFolderInfo).folder;
},
),
]),
);
}
}

View File

@@ -102,13 +102,6 @@ class _LeftBarState extends State<_LeftBar> implements FolderList {
const Spacer(), const Spacer(),
MenuButton( MenuButton(
entries: [ entries: [
MenuEntry(
icon: Icons.search,
text: 'Search'.tl,
onClick: () {
context.to(() => const LocalSearchPage());
},
),
MenuEntry( MenuEntry(
icon: Icons.add, icon: Icons.add,
text: 'Create Folder'.tl, text: 'Create Folder'.tl,
@@ -140,6 +133,10 @@ class _LeftBarState extends State<_LeftBar> implements FolderList {
); );
} }
index--; index--;
if (index == 0) {
return buildLocalFolder(_localAllFolderLabel);
}
index--;
if (index < folders.length) { if (index < folders.length) {
return buildLocalFolder(folders[index]); return buildLocalFolder(folders[index]);
} }
@@ -214,7 +211,9 @@ class _LeftBarState extends State<_LeftBar> implements FolderList {
), ),
), ),
padding: const EdgeInsets.only(left: 16), padding: const EdgeInsets.only(left: 16),
child: Text(name), child: Text(name == _localAllFolderLabel
? "All".tl
: getFavoriteDataOrNull(name)?.title ?? name),
), ),
); );
} }