mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
Improve image loading
This commit is contained in:
@@ -310,7 +310,11 @@
|
|||||||
"Check for updates on startup": "启动时检查更新",
|
"Check for updates on startup": "启动时检查更新",
|
||||||
"Start Time": "开始时间",
|
"Start Time": "开始时间",
|
||||||
"End Time": "结束时间",
|
"End Time": "结束时间",
|
||||||
"Custom": "自定义"
|
"Custom": "自定义",
|
||||||
|
"Reset": "重置",
|
||||||
|
"Tags": "标签",
|
||||||
|
"Authors": "作者",
|
||||||
|
"Comics": "漫画"
|
||||||
},
|
},
|
||||||
"zh_TW": {
|
"zh_TW": {
|
||||||
"Home": "首頁",
|
"Home": "首頁",
|
||||||
@@ -623,6 +627,10 @@
|
|||||||
"Check for updates on startup": "啟動時檢查更新",
|
"Check for updates on startup": "啟動時檢查更新",
|
||||||
"Start Time": "開始時間",
|
"Start Time": "開始時間",
|
||||||
"End Time": "結束時間",
|
"End Time": "結束時間",
|
||||||
"Custom": "自定義"
|
"Custom": "自定義",
|
||||||
|
"Reset": "重置",
|
||||||
|
"Tags": "標籤",
|
||||||
|
"Authors": "作者",
|
||||||
|
"Comics": "漫畫"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -149,7 +149,7 @@ class _Settings with ChangeNotifier {
|
|||||||
'enableDnsOverrides': false,
|
'enableDnsOverrides': false,
|
||||||
'dnsOverrides': {},
|
'dnsOverrides': {},
|
||||||
'enableCustomImageProcessing': false,
|
'enableCustomImageProcessing': false,
|
||||||
'customImageProcessing': _defaultCustomImageProcessing,
|
'customImageProcessing': defaultCustomImageProcessing,
|
||||||
'sni': true,
|
'sni': true,
|
||||||
'autoAddLanguageFilter': 'none', // none, chinese, english, japanese
|
'autoAddLanguageFilter': 'none', // none, chinese, english, japanese
|
||||||
};
|
};
|
||||||
@@ -169,15 +169,20 @@ class _Settings with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _defaultCustomImageProcessing = '''
|
const defaultCustomImageProcessing = '''
|
||||||
/**
|
/**
|
||||||
* Process an image
|
* Process an image
|
||||||
* @param image {ArayBuffer} - The image to process
|
* @param image {ArrayBuffer} - The image to process
|
||||||
* @param cid {string} - The comic ID
|
* @param cid {string} - The comic ID
|
||||||
* @param eid {string} - The episode ID
|
* @param eid {string} - The episode ID
|
||||||
* @returns {Promise<ArrayBuffer>} - The processed image
|
* @param page {number} - The page number
|
||||||
|
* @param sourceKey {string} - The source key
|
||||||
|
* @returns {Promise<ArrayBuffer> | {image: Promise<ArrayBuffer>, onCancel: () => void}} - The processed image
|
||||||
*/
|
*/
|
||||||
async function processImage(image, cid, eid) {
|
function processImage(image, cid, eid, page, sourceKey) {
|
||||||
|
let image = new Promise((resolve, reject) => {
|
||||||
|
resolve(image);
|
||||||
|
});
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
|
@@ -78,7 +78,13 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
|||||||
|
|
||||||
while (data == null && !stop) {
|
while (data == null && !stop) {
|
||||||
try {
|
try {
|
||||||
data = await load(chunkEvents);
|
data = await load(chunkEvents, () {
|
||||||
|
if (stop) {
|
||||||
|
throw const _ImageLoadingStopException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} on _ImageLoadingStopException {
|
||||||
|
rethrow;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.toString().contains("Invalid Status Code: 404")) {
|
if (e.toString().contains("Invalid Status Code: 404")) {
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -100,7 +106,7 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stop) {
|
if (stop) {
|
||||||
throw Exception("Image loading is stopped");
|
throw const _ImageLoadingStopException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data!.isEmpty) {
|
if (data!.isEmpty) {
|
||||||
@@ -127,6 +133,8 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
|||||||
}
|
}
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
} on _ImageLoadingStopException {
|
||||||
|
rethrow;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
scheduleMicrotask(() {
|
scheduleMicrotask(() {
|
||||||
PaintingBinding.instance.imageCache.evict(key);
|
PaintingBinding.instance.imageCache.evict(key);
|
||||||
@@ -138,7 +146,10 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents);
|
Future<Uint8List> load(
|
||||||
|
StreamController<ImageChunkEvent> chunkEvents,
|
||||||
|
void Function() checkStop,
|
||||||
|
);
|
||||||
|
|
||||||
String get key;
|
String get key;
|
||||||
|
|
||||||
@@ -159,3 +170,7 @@ abstract class BaseImageProvider<T extends BaseImageProvider<T>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef FileDecoderCallback = Future<ui.Codec> Function(Uint8List);
|
typedef FileDecoderCallback = Future<ui.Codec> Function(Uint8List);
|
||||||
|
|
||||||
|
class _ImageLoadingStopException implements Exception {
|
||||||
|
const _ImageLoadingStopException();
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import 'dart:async' show Future, StreamController;
|
import 'dart:async' show Future;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:venera/network/images.dart';
|
import 'package:venera/network/images.dart';
|
||||||
@@ -26,9 +26,10 @@ class CachedImageProvider
|
|||||||
static const _kMaxLoadingCount = 8;
|
static const _kMaxLoadingCount = 8;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
Future<Uint8List> load(chunkEvents, checkStop) async {
|
||||||
while(loadingCount > _kMaxLoadingCount) {
|
while(loadingCount > _kMaxLoadingCount) {
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
checkStop();
|
||||||
}
|
}
|
||||||
loadingCount++;
|
loadingCount++;
|
||||||
try {
|
try {
|
||||||
@@ -37,6 +38,7 @@ class CachedImageProvider
|
|||||||
return file.readAsBytes();
|
return file.readAsBytes();
|
||||||
}
|
}
|
||||||
await for (var progress in ImageDownloader.loadThumbnail(url, sourceKey, cid)) {
|
await for (var progress in ImageDownloader.loadThumbnail(url, sourceKey, cid)) {
|
||||||
|
checkStop();
|
||||||
chunkEvents.add(ImageChunkEvent(
|
chunkEvents.add(ImageChunkEvent(
|
||||||
cumulativeBytesLoaded: progress.currentBytes,
|
cumulativeBytesLoaded: progress.currentBytes,
|
||||||
expectedTotalBytes: progress.totalBytes,
|
expectedTotalBytes: progress.totalBytes,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import 'dart:async' show Future, StreamController;
|
import 'dart:async' show Future;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:venera/foundation/local.dart';
|
import 'package:venera/foundation/local.dart';
|
||||||
@@ -17,7 +17,7 @@ class HistoryImageProvider
|
|||||||
final History history;
|
final History history;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
Future<Uint8List> load(chunkEvents, checkStop) async {
|
||||||
var url = history.cover;
|
var url = history.cover;
|
||||||
if (!url.contains('/')) {
|
if (!url.contains('/')) {
|
||||||
var localComic = LocalManager().find(history.id, history.type);
|
var localComic = LocalManager().find(history.id, history.type);
|
||||||
@@ -27,6 +27,7 @@ class HistoryImageProvider
|
|||||||
var comicSource =
|
var comicSource =
|
||||||
history.type.comicSource ?? (throw "Comic source not found.");
|
history.type.comicSource ?? (throw "Comic source not found.");
|
||||||
var comic = await comicSource.loadComicInfo!(history.id);
|
var comic = await comicSource.loadComicInfo!(history.id);
|
||||||
|
checkStop();
|
||||||
url = comic.data.cover;
|
url = comic.data.cover;
|
||||||
history.cover = url;
|
history.cover = url;
|
||||||
HistoryManager().addHistory(history);
|
HistoryManager().addHistory(history);
|
||||||
@@ -36,6 +37,7 @@ class HistoryImageProvider
|
|||||||
history.type.sourceKey,
|
history.type.sourceKey,
|
||||||
history.id,
|
history.id,
|
||||||
)) {
|
)) {
|
||||||
|
checkStop();
|
||||||
chunkEvents.add(ImageChunkEvent(
|
chunkEvents.add(ImageChunkEvent(
|
||||||
cumulativeBytesLoaded: progress.currentBytes,
|
cumulativeBytesLoaded: progress.currentBytes,
|
||||||
expectedTotalBytes: progress.totalBytes,
|
expectedTotalBytes: progress.totalBytes,
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async' show Future, StreamController;
|
import 'dart:async' show Future, StreamController;
|
||||||
import 'dart:io';
|
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -29,30 +28,36 @@ class ImageFavoritesProvider
|
|||||||
String get eid => imageFavorite.eid;
|
String get eid => imageFavorite.eid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent>? chunkEvents) async {
|
Future<Uint8List> load(
|
||||||
|
StreamController<ImageChunkEvent>? chunkEvents,
|
||||||
|
void Function()? checkStop,
|
||||||
|
) async {
|
||||||
var imageKey = imageFavorite.imageKey;
|
var imageKey = imageFavorite.imageKey;
|
||||||
var localImage = await getImageFromLocal();
|
var localImage = await getImageFromLocal();
|
||||||
|
checkStop?.call();
|
||||||
if (localImage != null) {
|
if (localImage != null) {
|
||||||
return localImage;
|
return localImage;
|
||||||
}
|
}
|
||||||
var cacheImage = await readFromCache();
|
var cacheImage = await readFromCache();
|
||||||
|
checkStop?.call();
|
||||||
if (cacheImage != null) {
|
if (cacheImage != null) {
|
||||||
return cacheImage;
|
return cacheImage;
|
||||||
}
|
}
|
||||||
var gotImageKey = false;
|
var gotImageKey = false;
|
||||||
if (imageKey == "") {
|
if (imageKey == "") {
|
||||||
imageKey = await getImageKey();
|
imageKey = await getImageKey();
|
||||||
|
checkStop?.call();
|
||||||
gotImageKey = true;
|
gotImageKey = true;
|
||||||
}
|
}
|
||||||
Uint8List image;
|
Uint8List image;
|
||||||
try {
|
try {
|
||||||
image = await getImageFromNetwork(imageKey, chunkEvents);
|
image = await getImageFromNetwork(imageKey, chunkEvents, checkStop);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (gotImageKey) {
|
if (gotImageKey) {
|
||||||
rethrow;
|
rethrow;
|
||||||
} else {
|
} else {
|
||||||
imageKey = await getImageKey();
|
imageKey = await getImageKey();
|
||||||
image = await getImageFromNetwork(imageKey, chunkEvents);
|
image = await getImageFromNetwork(imageKey, chunkEvents, checkStop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await writeToCache(image);
|
await writeToCache(image);
|
||||||
@@ -106,9 +111,13 @@ class ImageFavoritesProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> getImageFromNetwork(
|
Future<Uint8List> getImageFromNetwork(
|
||||||
String imageKey, StreamController<ImageChunkEvent>? chunkEvents) async {
|
String imageKey,
|
||||||
|
StreamController<ImageChunkEvent>? chunkEvents,
|
||||||
|
void Function()? checkStop,
|
||||||
|
) async {
|
||||||
await for (var progress
|
await for (var progress
|
||||||
in ImageDownloader.loadComicImage(imageKey, sourceKey, cid, eid)) {
|
in ImageDownloader.loadComicImage(imageKey, sourceKey, cid, eid)) {
|
||||||
|
checkStop?.call();
|
||||||
if (chunkEvents != null) {
|
if (chunkEvents != null) {
|
||||||
chunkEvents.add(ImageChunkEvent(
|
chunkEvents.add(ImageChunkEvent(
|
||||||
cumulativeBytesLoaded: progress.currentBytes,
|
cumulativeBytesLoaded: progress.currentBytes,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import 'dart:async' show Future, StreamController;
|
import 'dart:async' show Future;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:venera/foundation/local.dart';
|
import 'package:venera/foundation/local.dart';
|
||||||
@@ -16,7 +16,7 @@ class LocalComicImageProvider
|
|||||||
final LocalComic comic;
|
final LocalComic comic;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
Future<Uint8List> load(chunkEvents, checkStop) async {
|
||||||
File? file = comic.coverFile;
|
File? file = comic.coverFile;
|
||||||
if(! await file.exists()) {
|
if(! await file.exists()) {
|
||||||
file = null;
|
file = null;
|
||||||
@@ -49,6 +49,7 @@ class LocalComicImageProvider
|
|||||||
if(file == null) {
|
if(file == null) {
|
||||||
throw "Error: Cover not found.";
|
throw "Error: Cover not found.";
|
||||||
}
|
}
|
||||||
|
checkStop();
|
||||||
var data = await file.readAsBytes();
|
var data = await file.readAsBytes();
|
||||||
if(data.isEmpty) {
|
if(data.isEmpty) {
|
||||||
throw "Exception: Empty file(${file.path}).";
|
throw "Exception: Empty file(${file.path}).";
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import 'dart:async' show Future, StreamController;
|
import 'dart:async' show Future;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:venera/foundation/app.dart';
|
import 'package:venera/foundation/app.dart';
|
||||||
@@ -28,7 +28,7 @@ class LocalFavoriteImageProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
Future<Uint8List> load(chunkEvents, checkStop) async {
|
||||||
var sourceKey = ComicSource.fromIntKey(intKey)?.key;
|
var sourceKey = ComicSource.fromIntKey(intKey)?.key;
|
||||||
var fileName = key.hashCode.toString();
|
var fileName = key.hashCode.toString();
|
||||||
var file = File(FilePath.join(App.dataPath, 'favorite_cover', fileName));
|
var file = File(FilePath.join(App.dataPath, 'favorite_cover', fileName));
|
||||||
@@ -37,7 +37,9 @@ class LocalFavoriteImageProvider
|
|||||||
} else {
|
} else {
|
||||||
await file.create(recursive: true);
|
await file.create(recursive: true);
|
||||||
}
|
}
|
||||||
|
checkStop();
|
||||||
await for (var progress in ImageDownloader.loadThumbnail(url, sourceKey)) {
|
await for (var progress in ImageDownloader.loadThumbnail(url, sourceKey)) {
|
||||||
|
checkStop();
|
||||||
chunkEvents.add(ImageChunkEvent(
|
chunkEvents.add(ImageChunkEvent(
|
||||||
cumulativeBytesLoaded: progress.currentBytes,
|
cumulativeBytesLoaded: progress.currentBytes,
|
||||||
expectedTotalBytes: progress.totalBytes,
|
expectedTotalBytes: progress.totalBytes,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import 'dart:async' show Future, StreamController;
|
import 'dart:async' show Future;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_qjs/flutter_qjs.dart';
|
import 'package:flutter_qjs/flutter_qjs.dart';
|
||||||
@@ -12,7 +12,7 @@ import 'package:venera/foundation/appdata.dart';
|
|||||||
class ReaderImageProvider
|
class ReaderImageProvider
|
||||||
extends BaseImageProvider<image_provider.ReaderImageProvider> {
|
extends BaseImageProvider<image_provider.ReaderImageProvider> {
|
||||||
/// Image provider for normal image.
|
/// Image provider for normal image.
|
||||||
const ReaderImageProvider(this.imageKey, this.sourceKey, this.cid, this.eid);
|
const ReaderImageProvider(this.imageKey, this.sourceKey, this.cid, this.eid, this.page);
|
||||||
|
|
||||||
final String imageKey;
|
final String imageKey;
|
||||||
|
|
||||||
@@ -22,8 +22,10 @@ class ReaderImageProvider
|
|||||||
|
|
||||||
final String eid;
|
final String eid;
|
||||||
|
|
||||||
|
final int page;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
Future<Uint8List> load(chunkEvents, checkStop) async {
|
||||||
Uint8List? imageBytes;
|
Uint8List? imageBytes;
|
||||||
if (imageKey.startsWith('file://')) {
|
if (imageKey.startsWith('file://')) {
|
||||||
var file = File(imageKey);
|
var file = File(imageKey);
|
||||||
@@ -35,6 +37,7 @@ class ReaderImageProvider
|
|||||||
} else {
|
} else {
|
||||||
await for (var event
|
await for (var event
|
||||||
in ImageDownloader.loadComicImage(imageKey, sourceKey, cid, eid)) {
|
in ImageDownloader.loadComicImage(imageKey, sourceKey, cid, eid)) {
|
||||||
|
checkStop();
|
||||||
chunkEvents.add(ImageChunkEvent(
|
chunkEvents.add(ImageChunkEvent(
|
||||||
cumulativeBytesLoaded: event.currentBytes,
|
cumulativeBytesLoaded: event.currentBytes,
|
||||||
expectedTotalBytes: event.totalBytes,
|
expectedTotalBytes: event.totalBytes,
|
||||||
@@ -60,14 +63,57 @@ class ReaderImageProvider
|
|||||||
})()
|
})()
|
||||||
''');
|
''');
|
||||||
if (func is JSInvokable) {
|
if (func is JSInvokable) {
|
||||||
var result = await func.invoke([imageBytes, cid, eid]);
|
var result = func.invoke([imageBytes, cid, eid, page, sourceKey]);
|
||||||
func.free();
|
|
||||||
if (result is Uint8List) {
|
if (result is Uint8List) {
|
||||||
return result;
|
imageBytes = result;
|
||||||
|
} else if (result is Future) {
|
||||||
|
var futureResult = await result;
|
||||||
|
if (futureResult is Uint8List) {
|
||||||
|
imageBytes = futureResult;
|
||||||
|
}
|
||||||
|
} else if (result is Map) {
|
||||||
|
var image = result['image'];
|
||||||
|
if (image is Uint8List) {
|
||||||
|
imageBytes = image;
|
||||||
|
} else if (image is Future) {
|
||||||
|
JSInvokable? onCancel;
|
||||||
|
if (result['onCancel'] is JSInvokable) {
|
||||||
|
onCancel = result['onCancel'];
|
||||||
|
}
|
||||||
|
if (onCancel == null) {
|
||||||
|
var futureImage = await image;
|
||||||
|
if (futureImage is Uint8List) {
|
||||||
|
imageBytes = futureImage;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dynamic futureImage;
|
||||||
|
image.then((value) {
|
||||||
|
futureImage = value;
|
||||||
|
futureImage ??= Uint8List(0);
|
||||||
|
});
|
||||||
|
while (futureImage == null) {
|
||||||
|
try {
|
||||||
|
checkStop();
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
onCancel.invoke([]);
|
||||||
|
onCancel.free();
|
||||||
|
func.free();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
await Future.delayed(Duration(milliseconds: 50));
|
||||||
|
}
|
||||||
|
if (futureImage is Uint8List) {
|
||||||
|
imageBytes = futureImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCancel?.free();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
func.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return imageBytes;
|
return imageBytes!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@@ -224,7 +224,7 @@ class _ImageFavoritesPhotoViewState extends State<ImageFavoritesPhotoView> {
|
|||||||
onClick: () async {
|
onClick: () async {
|
||||||
var temp = images[currentPage];
|
var temp = images[currentPage];
|
||||||
var imageProvider = ImageFavoritesProvider(temp);
|
var imageProvider = ImageFavoritesProvider(temp);
|
||||||
var data = await imageProvider.load(null);
|
var data = await imageProvider.load(null, null);
|
||||||
var fileType = detectFileType(data);
|
var fileType = detectFileType(data);
|
||||||
var fileName = "${currentPage + 1}.${fileType.ext}";
|
var fileName = "${currentPage + 1}.${fileType.ext}";
|
||||||
await saveFile(filename: fileName, data: data);
|
await saveFile(filename: fileName, data: data);
|
||||||
|
@@ -673,6 +673,7 @@ ImageProvider _createImageProviderFromKey(
|
|||||||
reader.type.comicSource?.key,
|
reader.type.comicSource?.key,
|
||||||
reader.cid,
|
reader.cid,
|
||||||
reader.eid,
|
reader.eid,
|
||||||
|
reader.page,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -650,6 +650,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
reader.type.comicSource!.key,
|
reader.type.comicSource!.key,
|
||||||
reader.cid,
|
reader.cid,
|
||||||
reader.eid,
|
reader.eid,
|
||||||
|
reader.page,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return InkWell(
|
return InkWell(
|
||||||
|
@@ -131,9 +131,10 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
"On the image browsing page, you can quickly collect images by sliding horizontally or vertically according to your reading mode"
|
"On the image browsing page, you can quickly collect images by sliding horizontally or vertically according to your reading mode"
|
||||||
.tl,
|
.tl,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_PopupWindowSetting(
|
_CallbackSetting(
|
||||||
title: "Custom Image Processing".tl,
|
title: "Custom Image Processing".tl,
|
||||||
builder: () => _CustomImageProcessing(),
|
callback: () => context.to(() => _CustomImageProcessing()),
|
||||||
|
actionTitle: "Edit".tl,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -163,10 +164,25 @@ class __CustomImageProcessingState extends State<_CustomImageProcessing> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int resetKey = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopUpWidgetScaffold(
|
return Scaffold(
|
||||||
title: "Custom Image Processing".tl,
|
appBar: Appbar(
|
||||||
|
title: Text("Custom Image Processing".tl),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
current = defaultCustomImageProcessing;
|
||||||
|
appdata.settings['customImageProcessing'] = current;
|
||||||
|
resetKey++;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Text("Reset".tl),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
@@ -182,6 +198,7 @@ class __CustomImageProcessingState extends State<_CustomImageProcessing> {
|
|||||||
),
|
),
|
||||||
child: SizedBox.expand(
|
child: SizedBox.expand(
|
||||||
child: CodeEditor(
|
child: CodeEditor(
|
||||||
|
key: ValueKey(resetKey),
|
||||||
initialValue: appdata.settings['customImageProcessing'],
|
initialValue: appdata.settings['customImageProcessing'],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
current = value;
|
current = value;
|
||||||
|
Reference in New Issue
Block a user