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:
@@ -342,21 +342,39 @@ class ComicTile extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<String> _splitText(String text) {
|
||||
// split text by space, comma. text in brackets will be kept together.
|
||||
// split text by comma, brackets
|
||||
var words = <String>[];
|
||||
var buffer = StringBuffer();
|
||||
var inBracket = false;
|
||||
String? prevBracket;
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
var c = text[i];
|
||||
if (c == '[' || c == '(') {
|
||||
inBracket = true;
|
||||
} else if (c == ']' || c == ')') {
|
||||
inBracket = false;
|
||||
} else if (c == ' ' || c == ',') {
|
||||
if (inBracket) {
|
||||
buffer.write(c);
|
||||
} else {
|
||||
words.add(buffer.toString());
|
||||
if (buffer.isNotEmpty) {
|
||||
words.add(buffer.toString().trim());
|
||||
buffer.clear();
|
||||
}
|
||||
inBracket = true;
|
||||
prevBracket = c;
|
||||
}
|
||||
} else if (c == ']' || c == ')') {
|
||||
if (prevBracket == '[' && c == ']' || prevBracket == '(' && c == ')') {
|
||||
if (buffer.isNotEmpty) {
|
||||
words.add(buffer.toString().trim());
|
||||
buffer.clear();
|
||||
}
|
||||
inBracket = false;
|
||||
} else {
|
||||
buffer.write(c);
|
||||
}
|
||||
} else if (c == ',') {
|
||||
if (inBracket) {
|
||||
buffer.write(c);
|
||||
} else {
|
||||
words.add(buffer.toString().trim());
|
||||
buffer.clear();
|
||||
}
|
||||
} else {
|
||||
@@ -364,8 +382,10 @@ class ComicTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
if (buffer.isNotEmpty) {
|
||||
words.add(buffer.toString());
|
||||
words.add(buffer.toString().trim());
|
||||
}
|
||||
words.removeWhere((element) => element == "");
|
||||
words = words.toSet().toList();
|
||||
return words;
|
||||
}
|
||||
|
||||
@@ -383,26 +403,33 @@ class ComicTile extends StatelessWidget {
|
||||
return StatefulBuilder(builder: (context, setState) {
|
||||
return ContentDialog(
|
||||
title: 'Block'.tl,
|
||||
content: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (var word in all)
|
||||
OptionChip(
|
||||
text: word,
|
||||
isSelected: words.contains(word),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (!words.contains(word)) {
|
||||
words.add(word);
|
||||
} else {
|
||||
words.remove(word);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
).paddingHorizontal(16),
|
||||
content: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: math.min(400, context.height - 136),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (var word in all)
|
||||
OptionChip(
|
||||
text: word,
|
||||
isSelected: words.contains(word),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (!words.contains(word)) {
|
||||
words.add(word);
|
||||
} else {
|
||||
words.remove(word);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
).paddingHorizontal(16),
|
||||
),
|
||||
actions: [
|
||||
Button.filled(
|
||||
onPressed: () {
|
||||
|
@@ -140,6 +140,8 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
||||
addFavorite(selectedComics.keys.toList());
|
||||
},
|
||||
),
|
||||
if (selectedComics.length == 1)
|
||||
...exportActions(selectedComics.keys.first),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -182,59 +184,63 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
||||
buildMultiSelectMenu(),
|
||||
];
|
||||
|
||||
List<Widget> normalActions = [
|
||||
Tooltip(
|
||||
message: "Search".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.search),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
searchMode = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Sort".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.sort),
|
||||
onPressed: sort,
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Downloading".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.download),
|
||||
onPressed: () {
|
||||
showPopUpWidget(context, const DownloadingPage());
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
var body = Scaffold(
|
||||
body: SmoothCustomScrollView(
|
||||
slivers: [
|
||||
if (!searchMode && !multiSelectMode)
|
||||
SliverAppbar(
|
||||
title: Text("Local".tl),
|
||||
actions: [
|
||||
Tooltip(
|
||||
message: "Search".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.search),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
searchMode = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Sort".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.sort),
|
||||
onPressed: sort,
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Downloading".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.download),
|
||||
onPressed: () {
|
||||
showPopUpWidget(context, const DownloadingPage());
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
else if (multiSelectMode)
|
||||
if (!searchMode)
|
||||
SliverAppbar(
|
||||
leading: Tooltip(
|
||||
message: "Cancel".tl,
|
||||
message: multiSelectMode ? "Cancel".tl : "Back".tl,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
multiSelectMode = false;
|
||||
selectedComics.clear();
|
||||
});
|
||||
if (multiSelectMode) {
|
||||
setState(() {
|
||||
multiSelectMode = false;
|
||||
selectedComics.clear();
|
||||
});
|
||||
} else {
|
||||
context.pop();
|
||||
}
|
||||
},
|
||||
icon: multiSelectMode
|
||||
? const Icon(Icons.close)
|
||||
: const Icon(Icons.arrow_back),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
"Selected @c comics".tlParams({"c": selectedComics.length})),
|
||||
actions: selectActions,
|
||||
title: multiSelectMode
|
||||
? Text(selectedComics.length.toString())
|
||||
: Text("Local".tl),
|
||||
actions: multiSelectMode ? selectActions : normalActions,
|
||||
)
|
||||
else if (searchMode)
|
||||
SliverAppbar(
|
||||
@@ -273,14 +279,14 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
||||
});
|
||||
},
|
||||
onTap: (c) {
|
||||
if(multiSelectMode) {
|
||||
if (multiSelectMode) {
|
||||
setState(() {
|
||||
if (selectedComics.containsKey(c as LocalComic)) {
|
||||
selectedComics.remove(c);
|
||||
} else {
|
||||
selectedComics[c] = true;
|
||||
}
|
||||
if(selectedComics.isEmpty) {
|
||||
if (selectedComics.isEmpty) {
|
||||
multiSelectMode = false;
|
||||
}
|
||||
});
|
||||
@@ -291,88 +297,20 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
||||
menuBuilder: (c) {
|
||||
return [
|
||||
MenuEntry(
|
||||
icon: Icons.delete,
|
||||
text: "Delete".tl,
|
||||
onClick: () {
|
||||
deleteComics([c as LocalComic]).then((value) {
|
||||
if (value && multiSelectMode) {
|
||||
setState(() {
|
||||
multiSelectMode = false;
|
||||
selectedComics.clear();
|
||||
});
|
||||
}
|
||||
});
|
||||
}),
|
||||
MenuEntry(
|
||||
icon: Icons.outbox_outlined,
|
||||
text: "Export as cbz".tl,
|
||||
onClick: () async {
|
||||
var controller = showLoadingDialog(
|
||||
context,
|
||||
allowCancel: false,
|
||||
);
|
||||
try {
|
||||
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());
|
||||
icon: Icons.delete,
|
||||
text: "Delete".tl,
|
||||
onClick: () {
|
||||
deleteComics([c as LocalComic]).then((value) {
|
||||
if (value && multiSelectMode) {
|
||||
setState(() {
|
||||
multiSelectMode = false;
|
||||
selectedComics.clear();
|
||||
});
|
||||
}
|
||||
controller.close();
|
||||
}),
|
||||
MenuEntry(
|
||||
icon: Icons.picture_as_pdf_outlined,
|
||||
text: "Export as pdf".tl,
|
||||
onClick: () async {
|
||||
var cache = FilePath.join(App.cachePath, 'temp.pdf');
|
||||
var controller = showLoadingDialog(
|
||||
context,
|
||||
allowCancel: false,
|
||||
);
|
||||
try {
|
||||
await createPdfFromComicIsolate(
|
||||
comic: c as LocalComic,
|
||||
savePath: cache,
|
||||
);
|
||||
await saveFile(
|
||||
file: File(cache),
|
||||
filename: "${c.title}.pdf",
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("PDF Export", e, s);
|
||||
context.showMessage(message: e.toString());
|
||||
} finally {
|
||||
controller.close();
|
||||
File(cache).deleteIgnoreError();
|
||||
}
|
||||
},
|
||||
),
|
||||
MenuEntry(
|
||||
icon: Icons.import_contacts_outlined,
|
||||
text: "Export as epub".tl,
|
||||
onClick: () async {
|
||||
var controller = showLoadingDialog(
|
||||
context,
|
||||
allowCancel: false,
|
||||
);
|
||||
File? file;
|
||||
try {
|
||||
file = await createEpubWithLocalComic(
|
||||
c as LocalComic,
|
||||
);
|
||||
await saveFile(
|
||||
file: file,
|
||||
filename: "${c.title}.epub",
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("EPUB Export", e, s);
|
||||
context.showMessage(message: e.toString());
|
||||
} finally {
|
||||
controller.close();
|
||||
file?.deleteIgnoreError();
|
||||
}
|
||||
},
|
||||
)
|
||||
});
|
||||
},
|
||||
),
|
||||
...exportActions(c as LocalComic),
|
||||
];
|
||||
},
|
||||
),
|
||||
@@ -439,4 +377,79 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
||||
);
|
||||
return isDeleted;
|
||||
}
|
||||
|
||||
List<MenuEntry> exportActions(LocalComic c) {
|
||||
return [
|
||||
MenuEntry(
|
||||
icon: Icons.outbox_outlined,
|
||||
text: "Export as cbz".tl,
|
||||
onClick: () async {
|
||||
var controller = showLoadingDialog(
|
||||
context,
|
||||
allowCancel: false,
|
||||
);
|
||||
try {
|
||||
var file = await CBZ.export(c);
|
||||
await saveFile(filename: file.name, file: file);
|
||||
await file.delete();
|
||||
} catch (e) {
|
||||
context.showMessage(message: e.toString());
|
||||
}
|
||||
controller.close();
|
||||
}),
|
||||
MenuEntry(
|
||||
icon: Icons.picture_as_pdf_outlined,
|
||||
text: "Export as pdf".tl,
|
||||
onClick: () async {
|
||||
var cache = FilePath.join(App.cachePath, 'temp.pdf');
|
||||
var controller = showLoadingDialog(
|
||||
context,
|
||||
allowCancel: false,
|
||||
);
|
||||
try {
|
||||
await createPdfFromComicIsolate(
|
||||
comic: c,
|
||||
savePath: cache,
|
||||
);
|
||||
await saveFile(
|
||||
file: File(cache),
|
||||
filename: "${c.title}.pdf",
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("PDF Export", e, s);
|
||||
context.showMessage(message: e.toString());
|
||||
} finally {
|
||||
controller.close();
|
||||
File(cache).deleteIgnoreError();
|
||||
}
|
||||
},
|
||||
),
|
||||
MenuEntry(
|
||||
icon: Icons.import_contacts_outlined,
|
||||
text: "Export as epub".tl,
|
||||
onClick: () async {
|
||||
var controller = showLoadingDialog(
|
||||
context,
|
||||
allowCancel: false,
|
||||
);
|
||||
File? file;
|
||||
try {
|
||||
file = await createEpubWithLocalComic(
|
||||
c,
|
||||
);
|
||||
await saveFile(
|
||||
file: file,
|
||||
filename: "${c.title}.epub",
|
||||
);
|
||||
} catch (e, s) {
|
||||
Log.error("EPUB Export", e, s);
|
||||
context.showMessage(message: e.toString());
|
||||
} finally {
|
||||
controller.close();
|
||||
file?.deleteIgnoreError();
|
||||
}
|
||||
},
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user