From 24c5a1bb010f5972ef419c15ebfa8075d1ab5d57 Mon Sep 17 00:00:00 2001 From: nyne Date: Wed, 18 Dec 2024 20:04:45 +0800 Subject: [PATCH] Improve local comics page --- lib/components/comic.dart | 2 +- lib/pages/favorites/favorite_actions.dart | 30 +-- lib/pages/local_comics_page.dart | 247 +++++++++++----------- pubspec.lock | 4 +- pubspec.yaml | 2 +- 5 files changed, 141 insertions(+), 144 deletions(-) diff --git a/lib/components/comic.dart b/lib/components/comic.dart index 41cccab..b131e4d 100644 --- a/lib/components/comic.dart +++ b/lib/components/comic.dart @@ -77,7 +77,7 @@ class ComicTile extends StatelessWidget { icon: Icons.stars_outlined, text: 'Add to favorites'.tl, onClick: () { - addFavorite(comic); + addFavorite([comic]); }, ), MenuEntry( diff --git a/lib/pages/favorites/favorite_actions.dart b/lib/pages/favorites/favorite_actions.dart index 23f13f0..6c3a822 100644 --- a/lib/pages/favorites/favorite_actions.dart +++ b/lib/pages/favorites/favorite_actions.dart @@ -77,7 +77,7 @@ String? validateFolderName(String newFolderName) { return null; } -void addFavorite(Comic comic) { +void addFavorite(List comics) { var folders = LocalFavoritesManager().folderNames; showDialog( @@ -105,19 +105,21 @@ void addFavorite(Comic comic) { FilledButton( onPressed: () { if (selectedFolder != null) { - LocalFavoritesManager().addComic( - selectedFolder!, - FavoriteItem( - id: comic.id, - name: comic.title, - coverPath: comic.cover, - author: comic.subtitle ?? '', - type: ComicType((comic.sourceKey == 'local' - ? 0 - : comic.sourceKey.hashCode)), - tags: comic.tags ?? [], - ), - ); + for (var comic in comics) { + LocalFavoritesManager().addComic( + selectedFolder!, + FavoriteItem( + id: comic.id, + name: comic.title, + coverPath: comic.cover, + author: comic.subtitle ?? '', + type: ComicType((comic.sourceKey == 'local' + ? 0 + : comic.sourceKey.hashCode)), + tags: comic.tags ?? [], + ), + ); + } context.pop(); } }, diff --git a/lib/pages/local_comics_page.dart b/lib/pages/local_comics_page.dart index cbbc979..05c6dd8 100644 --- a/lib/pages/local_comics_page.dart +++ b/lib/pages/local_comics_page.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:venera/components/components.dart'; import 'package:venera/foundation/app.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/log.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/epub.dart'; import 'package:venera/utils/io.dart'; @@ -30,7 +30,7 @@ class _LocalComicsPageState extends State { bool multiSelectMode = false; - Map selectedComics = {}; + Map selectedComics = {}; void update() { if (keyword.isEmpty) { @@ -117,48 +117,55 @@ class _LocalComicsPageState extends State { ); } + Widget buildMultiSelectMenu() { + 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() { + setState(() { + selectedComics = comics.asMap().map((k, v) => MapEntry(v, true)); + }); + } + + void deSelect() { + setState(() { + selectedComics.clear(); + }); + } + + void invertSelection() { + setState(() { + comics.asMap().forEach((k, v) { + selectedComics[v] = !selectedComics.putIfAbsent(v, () => false); + }); + selectedComics.removeWhere((k, v) => !v); + }); + } + @override Widget build(BuildContext context) { - void selectAll() { - setState(() { - selectedComics = comics.asMap().map((k, v) => MapEntry(v, true)); - }); - } - - void deSelect() { - setState(() { - selectedComics.clear(); - }); - } - - void invertSelection() { - setState(() { - comics.asMap().forEach((k, v) { - selectedComics[v] = !selectedComics.putIfAbsent(v, () => false); - }); - selectedComics.removeWhere((k, v) => !v); - }); - } - - void selectRange() { - setState(() { - List 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 selectActions = [ IconButton( icon: const Icon(Icons.select_all), @@ -172,10 +179,7 @@ class _LocalComicsPageState extends State { icon: const Icon(Icons.flip), tooltip: "Invert Selection".tl, onPressed: invertSelection), - IconButton( - icon: const Icon(Icons.border_horizontal_outlined), - tooltip: "Select in range".tl, - onPressed: selectRange), + buildMultiSelectMenu(), ]; var body = Scaffold( @@ -212,19 +216,6 @@ class _LocalComicsPageState extends State { }, ), ), - Tooltip( - message: multiSelectMode - ? "Exit Multi-Select".tl - : "Multi-Select".tl, - child: IconButton( - icon: const Icon(Icons.checklist), - onPressed: () { - setState(() { - multiSelectMode = !multiSelectMode; - }); - }, - ), - ), ], ) else if (multiSelectMode) @@ -275,65 +266,42 @@ class _LocalComicsPageState extends State { SliverGridComics( comics: comics, selections: selectedComics, - onTap: multiSelectMode - ? (c) { - setState(() { - if (selectedComics.containsKey(c as LocalComic)) { - selectedComics.remove(c); - } else { - selectedComics[c] = true; - } - }); + onLongPressed: (c) { + setState(() { + multiSelectMode = true; + selectedComics[c as LocalComic] = true; + }); + }, + onTap: (c) { + if(multiSelectMode) { + setState(() { + if (selectedComics.containsKey(c as LocalComic)) { + selectedComics.remove(c); + } else { + selectedComics[c] = true; } - : (c) { - (c as LocalComic).read(); - }, + if(selectedComics.isEmpty) { + multiSelectMode = false; + } + }); + } else { + (c as LocalComic).read(); + } + }, menuBuilder: (c) { return [ MenuEntry( icon: Icons.delete, text: "Delete".tl, onClick: () { - showDialog( - context: context, - 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(() { - selectedComics.clear(); - }); - } else { - LocalManager().deleteComic( - c as LocalComic, removeComicFile); - } - }, - child: Text("Confirm".tl), - ), - ], - ); - }); + deleteComics([c as LocalComic]).then((value) { + if (value && multiSelectMode) { + setState(() { + multiSelectMode = false; + selectedComics.clear(); }); + } + }); }), MenuEntry( icon: Icons.outbox_outlined, @@ -344,26 +312,14 @@ class _LocalComicsPageState extends State { allowCancel: false, ); 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); - await saveFile(filename: file.name, file: file); - await file.delete(); - } + var file = await CBZ.export(c as LocalComic); + await saveFile(filename: file.name, file: file); + await file.delete(); } catch (e) { context.showMessage(message: e.toString()); } controller.close(); }), - if (!multiSelectMode) MenuEntry( icon: Icons.picture_as_pdf_outlined, text: "Export as pdf".tl, @@ -391,7 +347,6 @@ class _LocalComicsPageState extends State { } }, ), - if (!multiSelectMode) MenuEntry( icon: Icons.import_contacts_outlined, text: "Export as epub".tl, @@ -444,4 +399,44 @@ class _LocalComicsPageState extends State { child: body, ); } + + Future deleteComics(List 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; + } } diff --git a/pubspec.lock b/pubspec.lock index 916f35e..fb17d47 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1125,10 +1125,10 @@ packages: dependency: "direct main" description: name: zip_flutter - sha256: "78c9b6053117828581346d24b07943ba6d3032a13f68d15c133bc21df2cf178a" + sha256: "955b53d58709fcd9feefbed3d41b5522bc5273e677603e9fc67017a81e568d24" url: "https://pub.dev" source: hosted - version: "0.0.4" + version: "0.0.5" sdks: dart: ">=3.6.0 <4.0.0" flutter: ">=3.27.1" diff --git a/pubspec.yaml b/pubspec.yaml index 096574d..2d7ec68 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,7 +51,7 @@ dependencies: sliver_tools: ^0.2.12 flutter_file_dialog: ^3.0.2 file_selector: ^1.0.3 - zip_flutter: ^0.0.4 + zip_flutter: ^0.0.5 lodepng_flutter: git: url: https://github.com/venera-app/lodepng_flutter