mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
Improve local comics page
This commit is contained in:
@@ -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(
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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"
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user