mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
Add HistoryImageProvider
This commit is contained in:
@@ -164,6 +164,8 @@ class ComicTile extends StatelessWidget {
|
||||
ImageProvider image;
|
||||
if (comic is LocalComic) {
|
||||
image = LocalComicImageProvider(comic as LocalComic);
|
||||
} else if (comic is History) {
|
||||
image = HistoryImageProvider(comic as History);
|
||||
} else if (comic.sourceKey == 'local') {
|
||||
var localComic = LocalManager().find(comic.id, ComicType.local);
|
||||
if (localComic == null) {
|
||||
|
@@ -19,6 +19,7 @@ import 'package:venera/foundation/consts.dart';
|
||||
import 'package:venera/foundation/favorites.dart';
|
||||
import 'package:venera/foundation/history.dart';
|
||||
import 'package:venera/foundation/image_provider/cached_image.dart';
|
||||
import 'package:venera/foundation/image_provider/history_image_provider.dart';
|
||||
import 'package:venera/foundation/image_provider/local_comic_image.dart';
|
||||
import 'package:venera/foundation/local.dart';
|
||||
import 'package:venera/foundation/res.dart';
|
||||
|
@@ -2,7 +2,9 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart' show ChangeNotifier;
|
||||
import 'package:sqlite3/sqlite3.dart';
|
||||
import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||
import 'package:venera/foundation/comic_type.dart';
|
||||
import 'package:venera/utils/translations.dart';
|
||||
|
||||
import 'app.dart';
|
||||
|
||||
@@ -22,15 +24,18 @@ abstract mixin class HistoryMixin {
|
||||
HistoryType get historyType;
|
||||
}
|
||||
|
||||
class History {
|
||||
class History implements Comic {
|
||||
HistoryType type;
|
||||
|
||||
DateTime time;
|
||||
|
||||
@override
|
||||
String title;
|
||||
|
||||
@override
|
||||
String subtitle;
|
||||
|
||||
@override
|
||||
String cover;
|
||||
|
||||
int ep;
|
||||
@@ -44,6 +49,7 @@ class History {
|
||||
/// The number of episodes is 1-based.
|
||||
Set<int> readEpisode;
|
||||
|
||||
@override
|
||||
int? maxPage;
|
||||
|
||||
History.fromModel(
|
||||
@@ -137,6 +143,47 @@ class History {
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, type);
|
||||
|
||||
@override
|
||||
String get description {
|
||||
var res = "";
|
||||
if (ep >= 1) {
|
||||
res += "Chapter @ep".tlParams({
|
||||
"ep": ep,
|
||||
});
|
||||
}
|
||||
if (page >= 1) {
|
||||
if (ep >= 1) {
|
||||
res += " - ";
|
||||
}
|
||||
res += "Page @page".tlParams({
|
||||
"page": page,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
String? get favoriteId => null;
|
||||
|
||||
@override
|
||||
String? get language => null;
|
||||
|
||||
@override
|
||||
String get sourceKey => type == ComicType.local
|
||||
? 'local'
|
||||
: type.comicSource?.key ?? "Unknown:${type.value}";
|
||||
|
||||
@override
|
||||
double? get stars => null;
|
||||
|
||||
@override
|
||||
List<String>? get tags => null;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryManager with ChangeNotifier {
|
||||
|
57
lib/foundation/image_provider/history_image_provider.dart
Normal file
57
lib/foundation/image_provider/history_image_provider.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'dart:async' show Future, StreamController;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:venera/foundation/local.dart';
|
||||
import 'package:venera/network/images.dart';
|
||||
import '../history.dart';
|
||||
import 'base_image_provider.dart';
|
||||
import 'history_image_provider.dart' as image_provider;
|
||||
|
||||
class HistoryImageProvider
|
||||
extends BaseImageProvider<image_provider.HistoryImageProvider> {
|
||||
/// Image provider for normal image.
|
||||
///
|
||||
/// [url] is the url of the image. Local file path is also supported.
|
||||
const HistoryImageProvider(this.history);
|
||||
|
||||
final History history;
|
||||
|
||||
@override
|
||||
Future<Uint8List> load(StreamController<ImageChunkEvent> chunkEvents) async {
|
||||
var url = history.cover;
|
||||
if (!url.contains('/')) {
|
||||
var localComic = LocalManager().find(history.id, history.type);
|
||||
if (localComic != null) {
|
||||
return localComic.coverFile.readAsBytes();
|
||||
}
|
||||
var comicSource =
|
||||
history.type.comicSource ?? (throw "Comic source not found.");
|
||||
var comic = await comicSource.loadComicInfo!(history.id);
|
||||
url = comic.data.cover;
|
||||
history.cover = url;
|
||||
HistoryManager().addHistory(history);
|
||||
}
|
||||
await for (var progress in ImageDownloader.loadThumbnail(
|
||||
url,
|
||||
history.type.sourceKey,
|
||||
history.id,
|
||||
)) {
|
||||
chunkEvents.add(ImageChunkEvent(
|
||||
cumulativeBytesLoaded: progress.currentBytes,
|
||||
expectedTotalBytes: progress.totalBytes,
|
||||
));
|
||||
if (progress.imageBytes != null) {
|
||||
return progress.imageBytes!;
|
||||
}
|
||||
}
|
||||
throw "Error: Empty response body.";
|
||||
}
|
||||
|
||||
@override
|
||||
Future<HistoryImageProvider> obtainKey(ImageConfiguration configuration) {
|
||||
return SynchronousFuture(this);
|
||||
}
|
||||
|
||||
@override
|
||||
String get key => "history${history.id}${history.type.value}";
|
||||
}
|
@@ -78,33 +78,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
||||
],
|
||||
),
|
||||
SliverGridComics(
|
||||
comics: comics.map(
|
||||
(e) {
|
||||
var cover = e.cover;
|
||||
if (!cover.isURL) {
|
||||
var localComic = LocalManager().find(
|
||||
e.id,
|
||||
e.type,
|
||||
);
|
||||
if(localComic != null) {
|
||||
cover = "file://${localComic.coverFile.path}";
|
||||
}
|
||||
}
|
||||
return Comic(
|
||||
e.title,
|
||||
cover,
|
||||
e.id,
|
||||
e.subtitle,
|
||||
null,
|
||||
getDescription(e),
|
||||
e.type == ComicType.local
|
||||
? 'local'
|
||||
: e.type.comicSource?.key ?? "Unknown:${e.type.value}",
|
||||
null,
|
||||
null,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
comics: comics,
|
||||
badgeBuilder: (c) {
|
||||
return ComicSource.find(c.sourceKey)?.name;
|
||||
},
|
||||
|
@@ -7,6 +7,7 @@ import 'package:venera/foundation/consts.dart';
|
||||
import 'package:venera/foundation/favorites.dart';
|
||||
import 'package:venera/foundation/history.dart';
|
||||
import 'package:venera/foundation/image_provider/cached_image.dart';
|
||||
import 'package:venera/foundation/image_provider/history_image_provider.dart';
|
||||
import 'package:venera/foundation/image_provider/local_comic_image.dart';
|
||||
import 'package:venera/foundation/local.dart';
|
||||
import 'package:venera/pages/accounts_page.dart';
|
||||
@@ -265,21 +266,6 @@ class _HistoryState extends State<_History> {
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: history.length,
|
||||
itemBuilder: (context, index) {
|
||||
var cover = history[index].cover;
|
||||
ImageProvider imageProvider = CachedImageProvider(
|
||||
cover,
|
||||
sourceKey: history[index].type.comicSource?.key,
|
||||
cid: history[index].id,
|
||||
);
|
||||
if (!cover.isURL) {
|
||||
var localComic = LocalManager().find(
|
||||
history[index].id,
|
||||
history[index].type,
|
||||
);
|
||||
if (localComic != null) {
|
||||
imageProvider = FileImage(localComic.coverFile);
|
||||
}
|
||||
}
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
context.to(
|
||||
@@ -302,7 +288,7 @@ class _HistoryState extends State<_History> {
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: AnimatedImage(
|
||||
image: imageProvider,
|
||||
image: HistoryImageProvider(history[index]),
|
||||
width: 96,
|
||||
height: 128,
|
||||
fit: BoxFit.cover,
|
||||
|
Reference in New Issue
Block a user