implement saving image, sharing image, reading settings and chapters view

This commit is contained in:
nyne
2024-10-08 16:52:20 +08:00
parent b44998663a
commit 5deb71e10a
15 changed files with 723 additions and 213 deletions

View File

@@ -12,49 +12,51 @@ class _ReaderImagesState extends State<_ReaderImages> {
bool inProgress = false;
late _ReaderState reader;
@override
void initState() {
context.reader.isLoading = true;
reader = context.reader;
reader.isLoading = true;
super.initState();
}
void load() async {
if (inProgress) return;
inProgress = true;
if (context.reader.type == ComicType.local ||
(await LocalManager().isDownloaded(
context.reader.cid, context.reader.type, context.reader.chapter))) {
if (reader.type == ComicType.local ||
(await LocalManager()
.isDownloaded(reader.cid, reader.type, reader.chapter))) {
try {
var images = await LocalManager().getImages(
context.reader.cid, context.reader.type, context.reader.chapter);
var images = await LocalManager()
.getImages(reader.cid, reader.type, reader.chapter);
setState(() {
context.reader.images = images;
context.reader.isLoading = false;
reader.images = images;
reader.isLoading = false;
inProgress = false;
});
} catch (e) {
setState(() {
error = e.toString();
context.reader.isLoading = false;
reader.isLoading = false;
inProgress = false;
});
}
} else {
var res = await context.reader.type.comicSource!.loadComicPages!(
context.reader.widget.cid,
context.reader.widget.chapters?.keys
.elementAt(context.reader.chapter - 1),
var res = await reader.type.comicSource!.loadComicPages!(
reader.widget.cid,
reader.widget.chapters?.keys.elementAt(reader.chapter - 1),
);
if (res.error) {
setState(() {
error = res.errorMessage;
context.reader.isLoading = false;
reader.isLoading = false;
inProgress = false;
});
} else {
setState(() {
context.reader.images = res.data;
context.reader.isLoading = false;
reader.images = res.data;
reader.isLoading = false;
inProgress = false;
});
}
@@ -64,7 +66,7 @@ class _ReaderImagesState extends State<_ReaderImages> {
@override
Widget build(BuildContext context) {
if (context.reader.isLoading) {
if (reader.isLoading) {
load();
return const Center(
child: CircularProgressIndicator(),
@@ -74,14 +76,14 @@ class _ReaderImagesState extends State<_ReaderImages> {
message: error!,
retry: () {
setState(() {
context.reader.isLoading = true;
reader.isLoading = true;
error = null;
});
},
);
} else {
if (context.reader.mode.isGallery) {
return _GalleryMode(key: Key(context.reader.mode.key));
if (reader.mode.isGallery) {
return _GalleryMode(key: Key(reader.mode.key));
} else {
// TODO: Implement other modes
throw UnimplementedError();
@@ -107,17 +109,20 @@ class _GalleryModeState extends State<_GalleryMode>
var photoViewControllers = <int, PhotoViewController>{};
late _ReaderState reader;
@override
void initState() {
controller = PageController(initialPage: context.reader.page);
context.reader._imageViewController = this;
cached = List.filled(context.reader.maxPage + 2, false);
reader = context.reader;
controller = PageController(initialPage: reader.page);
reader._imageViewController = this;
cached = List.filled(reader.maxPage + 2, false);
super.initState();
}
void cache(int current) {
for (int i = current + 1; i <= current + preCacheCount; i++) {
if (i <= context.reader.maxPage && !cached[i]) {
if (i <= reader.maxPage && !cached[i]) {
_precacheImage(i, context);
cached[i] = true;
}
@@ -130,14 +135,14 @@ class _GalleryModeState extends State<_GalleryMode>
backgroundDecoration: BoxDecoration(
color: context.colorScheme.surface,
),
reverse: context.reader.mode == ReaderMode.galleryRightToLeft,
scrollDirection: context.reader.mode == ReaderMode.galleryTopToBottom
reverse: reader.mode == ReaderMode.galleryRightToLeft,
scrollDirection: reader.mode == ReaderMode.galleryTopToBottom
? Axis.vertical
: Axis.horizontal,
itemCount: context.reader.images!.length + 2,
itemCount: reader.images!.length + 2,
builder: (BuildContext context, int index) {
ImageProvider? imageProvider;
if (index != 0 && index != context.reader.images!.length + 1) {
if (index != 0 && index != reader.images!.length + 1) {
imageProvider = _createImageProvider(index, context);
} else {
return PhotoViewGalleryPageOptions.customChild(
@@ -176,15 +181,15 @@ class _GalleryModeState extends State<_GalleryMode>
),
onPageChanged: (i) {
if (i == 0) {
if (!context.reader.toNextChapter()) {
context.reader.toPage(1);
if (!reader.toNextChapter()) {
reader.toPage(1);
}
} else if (i == context.reader.maxPage + 1) {
if (!context.reader.toPrevChapter()) {
context.reader.toPage(context.reader.maxPage);
} else if (i == reader.maxPage + 1) {
if (!reader.toPrevChapter()) {
reader.toPage(reader.maxPage);
}
} else {
context.reader.setPage(i);
reader.setPage(i);
context.readerScaffold.update();
}
},
@@ -210,21 +215,22 @@ class _GalleryModeState extends State<_GalleryMode>
@override
void handleDoubleTap(Offset location) {
var controller = photoViewControllers[context.reader.page]!;
var controller = photoViewControllers[reader.page]!;
controller.onDoubleClick?.call();
}
}
ImageProvider _createImageProvider(int page, BuildContext context) {
var imageKey = context.reader.images![page-1];
if(imageKey.startsWith('file://')) {
var reader = context.reader;
var imageKey = reader.images![page - 1];
if (imageKey.startsWith('file://')) {
return FileImage(File(imageKey.replaceFirst("file://", '')));
} else {
return ReaderImageProvider(
imageKey,
context.reader.type.comicSource!.key,
context.reader.cid,
context.reader.eid,
reader.type.comicSource!.key,
reader.cid,
reader.eid,
);
}
}

View File

@@ -10,10 +10,13 @@ import 'package:photo_view/photo_view_gallery.dart';
import 'package:venera/components/components.dart';
import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/appdata.dart';
import 'package:venera/foundation/cache_manager.dart';
import 'package:venera/foundation/comic_type.dart';
import 'package:venera/foundation/history.dart';
import 'package:venera/foundation/image_provider/reader_image.dart';
import 'package:venera/foundation/local.dart';
import 'package:venera/pages/settings/settings_page.dart';
import 'package:venera/utils/file_type.dart';
import 'package:venera/utils/io.dart';
import 'package:venera/utils/translations.dart';
import 'package:window_manager/window_manager.dart';

View File

@@ -63,6 +63,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
child: Container(
padding: EdgeInsets.only(top: context.padding.top),
decoration: BoxDecoration(
color: context.colorScheme.surface.withOpacity(0.82),
border: Border(
bottom: BorderSide(
color: Colors.grey.withOpacity(0.5),
@@ -73,16 +74,20 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
child: Row(
children: [
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
const BackButton(),
const SizedBox(width: 8),
Expanded(
child: Text(context.reader.widget.name, style: ts.s18),
),
const SizedBox(width: 8),
Tooltip(
message: "Settings".tl,
child: IconButton(
icon: const Icon(Icons.settings),
onPressed: openSetting,
),
),
const SizedBox(width: 8),
],
),
),
@@ -191,7 +196,10 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
icon: context.reader.autoPageTurningTimer != null
? const Icon(Icons.timer)
: const Icon(Icons.timer_sharp),
onPressed: context.reader.autoPageTurning,
onPressed: () {
context.reader.autoPageTurning();
update();
},
),
),
if (context.reader.widget.chapters != null)
@@ -226,6 +234,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
return BlurEffect(
child: Container(
decoration: BoxDecoration(
color: context.colorScheme.surface.withOpacity(0.82),
border: Border(
top: BorderSide(
color: Colors.grey.withOpacity(0.5),
@@ -243,7 +252,8 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
return Slider(
value: context.reader.page.toDouble(),
min: 1,
max: context.reader.maxPage.clamp(context.reader.page, 1 << 16).toDouble(),
max:
context.reader.maxPage.clamp(context.reader.page, 1 << 16).toDouble(),
divisions: (context.reader.maxPage - 1).clamp(2, 1 << 16),
onChanged: (i) {
context.reader.toPage(i.toInt());
@@ -285,18 +295,131 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
}
void openChapterDrawer() {
// TODO
showSideBar(
context,
_ChaptersView(context.reader),
width: 400,
);
}
void saveCurrentImage() {
// TODO
Future<Uint8List> _getCurrentImageData() async {
var imageKey = context.reader.images![context.reader.page - 1];
if (imageKey.startsWith("file://")) {
return await File(imageKey.substring(7)).readAsBytes();
} else {
return (await CacheManager()
.findCache("$imageKey@${context.reader.type.comicSource!.key}"))!
.readAsBytes();
}
}
void share() {
// TODO
void saveCurrentImage() async {
var data = await _getCurrentImageData();
var fileType = detectFileType(data);
var filename = "${context.reader.page}${fileType.ext}";
saveFile(data: data, filename: filename);
}
void share() async {
var data = await _getCurrentImageData();
var fileType = detectFileType(data);
var filename = "${context.reader.page}${fileType.ext}";
Share.shareFile(
data: data,
filename: filename,
mime: fileType.mime,
);
}
void openSetting() {
// TODO
showSideBar(
context,
ReaderSettings(
onChanged: (key) {
if(key == "readerMode") {
context.reader.mode = ReaderMode.fromKey(appdata.settings[key]);
App.rootContext.pop();
}
context.reader.update();
},
),
width: 400,
);
}
}
class _ChaptersView extends StatefulWidget {
const _ChaptersView(this.reader);
final _ReaderState reader;
@override
State<_ChaptersView> createState() => _ChaptersViewState();
}
class _ChaptersViewState extends State<_ChaptersView> {
bool desc = false;
@override
Widget build(BuildContext context) {
var chapters = widget.reader.widget.chapters!;
var current = widget.reader.chapter - 1;
return Scaffold(
body: SmoothCustomScrollView(
slivers: [
SliverAppbar(
title: Text("Chapters".tl),
actions: [
Tooltip(
message: "Click to change the order".tl,
child: TextButton.icon(
icon: Icon(
!desc ? Icons.arrow_upward : Icons.arrow_downward,
size: 18,
),
label: Text(!desc ? "Ascending".tl : "Descending".tl),
onPressed: () {
setState(() {
desc = !desc;
});
},
),
),
],
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (desc) {
index = chapters.length - 1 - index;
}
var chapter = chapters.values.elementAt(index);
return ListTile(
shape: Border(
left: BorderSide(
color: current == index
? context.colorScheme.primary
: Colors.transparent,
width: 4,
),
),
title: Text(
chapter,
style: current == index
? ts.withColor(context.colorScheme.primary).bold
: null,
),
onTap: () {
widget.reader.toChapter(index + 1);
Navigator.of(context).pop();
},
);
},
childCount: chapters.length,
),
),
],
),
);
}
}