Improve local comics page

This commit is contained in:
2024-12-18 20:04:45 +08:00
parent ea973a2787
commit 24c5a1bb01
5 changed files with 141 additions and 144 deletions

View File

@@ -77,7 +77,7 @@ class ComicTile extends StatelessWidget {
icon: Icons.stars_outlined, icon: Icons.stars_outlined,
text: 'Add to favorites'.tl, text: 'Add to favorites'.tl,
onClick: () { onClick: () {
addFavorite(comic); addFavorite([comic]);
}, },
), ),
MenuEntry( MenuEntry(

View File

@@ -77,7 +77,7 @@ String? validateFolderName(String newFolderName) {
return null; return null;
} }
void addFavorite(Comic comic) { void addFavorite(List<Comic> comics) {
var folders = LocalFavoritesManager().folderNames; var folders = LocalFavoritesManager().folderNames;
showDialog( showDialog(
@@ -105,6 +105,7 @@ void addFavorite(Comic comic) {
FilledButton( FilledButton(
onPressed: () { onPressed: () {
if (selectedFolder != null) { if (selectedFolder != null) {
for (var comic in comics) {
LocalFavoritesManager().addComic( LocalFavoritesManager().addComic(
selectedFolder!, selectedFolder!,
FavoriteItem( FavoriteItem(
@@ -118,6 +119,7 @@ void addFavorite(Comic comic) {
tags: comic.tags ?? [], tags: comic.tags ?? [],
), ),
); );
}
context.pop(); context.pop();
} }
}, },

View File

@@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
import 'package:venera/components/components.dart'; import 'package:venera/components/components.dart';
import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/appdata.dart'; import 'package:venera/foundation/appdata.dart';
import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/local.dart'; import 'package:venera/foundation/local.dart';
import 'package:venera/foundation/log.dart'; import 'package:venera/foundation/log.dart';
import 'package:venera/pages/downloading_page.dart'; import 'package:venera/pages/downloading_page.dart';
import 'package:venera/pages/favorites/favorites_page.dart';
import 'package:venera/utils/cbz.dart'; import 'package:venera/utils/cbz.dart';
import 'package:venera/utils/epub.dart'; import 'package:venera/utils/epub.dart';
import 'package:venera/utils/io.dart'; import 'package:venera/utils/io.dart';
@@ -30,7 +30,7 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
bool multiSelectMode = false; bool multiSelectMode = false;
Map<Comic, bool> selectedComics = {}; Map<LocalComic, bool> selectedComics = {};
void update() { void update() {
if (keyword.isEmpty) { if (keyword.isEmpty) {
@@ -117,8 +117,32 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
); );
} }
@override Widget buildMultiSelectMenu() {
Widget build(BuildContext context) { return MenuButton(entries: [
MenuEntry(
icon: Icons.delete_outline,
text: "Delete".tl,
onClick: () {
deleteComics(selectedComics.keys.toList()).then((value) {
if (value) {
setState(() {
multiSelectMode = false;
selectedComics.clear();
});
}
});
},
),
MenuEntry(
icon: Icons.favorite_border,
text: "Add to favorites".tl,
onClick: () {
addFavorite(selectedComics.keys.toList());
},
),
]);
}
void selectAll() { void selectAll() {
setState(() { setState(() {
selectedComics = comics.asMap().map((k, v) => MapEntry(v, true)); selectedComics = comics.asMap().map((k, v) => MapEntry(v, true));
@@ -140,25 +164,8 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
}); });
} }
void selectRange() { @override
setState(() { Widget build(BuildContext context) {
List<int> l = [];
selectedComics.forEach((k, v) {
l.add(comics.indexOf(k as LocalComic));
});
if (l.isEmpty) {
return;
}
l.sort();
int start = l.first;
int end = l.last;
selectedComics.clear();
selectedComics.addEntries(List.generate(end - start + 1, (i) {
return MapEntry(comics[start + i], true);
}));
});
}
List<Widget> selectActions = [ List<Widget> selectActions = [
IconButton( IconButton(
icon: const Icon(Icons.select_all), icon: const Icon(Icons.select_all),
@@ -172,10 +179,7 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
icon: const Icon(Icons.flip), icon: const Icon(Icons.flip),
tooltip: "Invert Selection".tl, tooltip: "Invert Selection".tl,
onPressed: invertSelection), onPressed: invertSelection),
IconButton( buildMultiSelectMenu(),
icon: const Icon(Icons.border_horizontal_outlined),
tooltip: "Select in range".tl,
onPressed: selectRange),
]; ];
var body = Scaffold( var body = Scaffold(
@@ -212,19 +216,6 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
}, },
), ),
), ),
Tooltip(
message: multiSelectMode
? "Exit Multi-Select".tl
: "Multi-Select".tl,
child: IconButton(
icon: const Icon(Icons.checklist),
onPressed: () {
setState(() {
multiSelectMode = !multiSelectMode;
});
},
),
),
], ],
) )
else if (multiSelectMode) else if (multiSelectMode)
@@ -275,18 +266,27 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
SliverGridComics( SliverGridComics(
comics: comics, comics: comics,
selections: selectedComics, selections: selectedComics,
onTap: multiSelectMode onLongPressed: (c) {
? (c) { setState(() {
multiSelectMode = true;
selectedComics[c as LocalComic] = true;
});
},
onTap: (c) {
if(multiSelectMode) {
setState(() { setState(() {
if (selectedComics.containsKey(c as LocalComic)) { if (selectedComics.containsKey(c as LocalComic)) {
selectedComics.remove(c); selectedComics.remove(c);
} else { } else {
selectedComics[c] = true; selectedComics[c] = true;
} }
}); if(selectedComics.isEmpty) {
multiSelectMode = false;
} }
: (c) { });
} else {
(c as LocalComic).read(); (c as LocalComic).read();
}
}, },
menuBuilder: (c) { menuBuilder: (c) {
return [ return [
@@ -294,45 +294,13 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
icon: Icons.delete, icon: Icons.delete,
text: "Delete".tl, text: "Delete".tl,
onClick: () { onClick: () {
showDialog( deleteComics([c as LocalComic]).then((value) {
context: context, if (value && multiSelectMode) {
builder: (context) {
bool removeComicFile = true;
return StatefulBuilder(builder: (context, state) {
return ContentDialog(
title: "Delete".tl,
content: CheckboxListTile(
title: Text("Also remove files on disk".tl),
value: removeComicFile,
onChanged: (v) {
state(() {
removeComicFile = !removeComicFile;
});
},
),
actions: [
FilledButton(
onPressed: () {
context.pop();
if (multiSelectMode) {
for (var comic in selectedComics.keys) {
LocalManager().deleteComic(
comic as LocalComic,
removeComicFile);
}
setState(() { setState(() {
multiSelectMode = false;
selectedComics.clear(); selectedComics.clear();
}); });
} else {
LocalManager().deleteComic(
c as LocalComic, removeComicFile);
} }
},
child: Text("Confirm".tl),
),
],
);
});
}); });
}), }),
MenuEntry( MenuEntry(
@@ -344,26 +312,14 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
allowCancel: false, allowCancel: false,
); );
try { try {
if (multiSelectMode) {
for (var comic in selectedComics.keys) {
var file = await CBZ.export(comic as LocalComic);
await saveFile(filename: file.name, file: file);
await file.delete();
}
setState(() {
selectedComics.clear();
});
} else {
var file = await CBZ.export(c as LocalComic); var file = await CBZ.export(c as LocalComic);
await saveFile(filename: file.name, file: file); await saveFile(filename: file.name, file: file);
await file.delete(); await file.delete();
}
} catch (e) { } catch (e) {
context.showMessage(message: e.toString()); context.showMessage(message: e.toString());
} }
controller.close(); controller.close();
}), }),
if (!multiSelectMode)
MenuEntry( MenuEntry(
icon: Icons.picture_as_pdf_outlined, icon: Icons.picture_as_pdf_outlined,
text: "Export as pdf".tl, text: "Export as pdf".tl,
@@ -391,7 +347,6 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
} }
}, },
), ),
if (!multiSelectMode)
MenuEntry( MenuEntry(
icon: Icons.import_contacts_outlined, icon: Icons.import_contacts_outlined,
text: "Export as epub".tl, text: "Export as epub".tl,
@@ -444,4 +399,44 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
child: body, child: body,
); );
} }
Future<bool> deleteComics(List<LocalComic> comics) async {
bool isDeleted = false;
await showDialog(
context: App.rootContext,
builder: (context) {
bool removeComicFile = true;
return StatefulBuilder(builder: (context, state) {
return ContentDialog(
title: "Delete".tl,
content: CheckboxListTile(
title: Text("Also remove files on disk".tl),
value: removeComicFile,
onChanged: (v) {
state(() {
removeComicFile = !removeComicFile;
});
},
),
actions: [
FilledButton(
onPressed: () {
context.pop();
for (var comic in comics) {
LocalManager().deleteComic(
comic,
removeComicFile,
);
}
isDeleted = true;
},
child: Text("Confirm".tl),
),
],
);
});
},
);
return isDeleted;
}
} }

View File

@@ -1125,10 +1125,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: zip_flutter name: zip_flutter
sha256: "78c9b6053117828581346d24b07943ba6d3032a13f68d15c133bc21df2cf178a" sha256: "955b53d58709fcd9feefbed3d41b5522bc5273e677603e9fc67017a81e568d24"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.4" version: "0.0.5"
sdks: sdks:
dart: ">=3.6.0 <4.0.0" dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.1" flutter: ">=3.27.1"

View File

@@ -51,7 +51,7 @@ dependencies:
sliver_tools: ^0.2.12 sliver_tools: ^0.2.12
flutter_file_dialog: ^3.0.2 flutter_file_dialog: ^3.0.2
file_selector: ^1.0.3 file_selector: ^1.0.3
zip_flutter: ^0.0.4 zip_flutter: ^0.0.5
lodepng_flutter: lodepng_flutter:
git: git:
url: https://github.com/venera-app/lodepng_flutter url: https://github.com/venera-app/lodepng_flutter