mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
Add HistoryImageProvider
This commit is contained in:
@@ -164,6 +164,8 @@ class ComicTile extends StatelessWidget {
|
|||||||
ImageProvider image;
|
ImageProvider image;
|
||||||
if (comic is LocalComic) {
|
if (comic is LocalComic) {
|
||||||
image = LocalComicImageProvider(comic as LocalComic);
|
image = LocalComicImageProvider(comic as LocalComic);
|
||||||
|
} else if (comic is History) {
|
||||||
|
image = HistoryImageProvider(comic as History);
|
||||||
} else if (comic.sourceKey == 'local') {
|
} else if (comic.sourceKey == 'local') {
|
||||||
var localComic = LocalManager().find(comic.id, ComicType.local);
|
var localComic = LocalManager().find(comic.id, ComicType.local);
|
||||||
if (localComic == null) {
|
if (localComic == null) {
|
||||||
|
@@ -19,6 +19,7 @@ import 'package:venera/foundation/consts.dart';
|
|||||||
import 'package:venera/foundation/favorites.dart';
|
import 'package:venera/foundation/favorites.dart';
|
||||||
import 'package:venera/foundation/history.dart';
|
import 'package:venera/foundation/history.dart';
|
||||||
import 'package:venera/foundation/image_provider/cached_image.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/image_provider/local_comic_image.dart';
|
||||||
import 'package:venera/foundation/local.dart';
|
import 'package:venera/foundation/local.dart';
|
||||||
import 'package:venera/foundation/res.dart';
|
import 'package:venera/foundation/res.dart';
|
||||||
|
@@ -2,7 +2,9 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/widgets.dart' show ChangeNotifier;
|
import 'package:flutter/widgets.dart' show ChangeNotifier;
|
||||||
import 'package:sqlite3/sqlite3.dart';
|
import 'package:sqlite3/sqlite3.dart';
|
||||||
|
import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||||
import 'package:venera/foundation/comic_type.dart';
|
import 'package:venera/foundation/comic_type.dart';
|
||||||
|
import 'package:venera/utils/translations.dart';
|
||||||
|
|
||||||
import 'app.dart';
|
import 'app.dart';
|
||||||
|
|
||||||
@@ -22,15 +24,18 @@ abstract mixin class HistoryMixin {
|
|||||||
HistoryType get historyType;
|
HistoryType get historyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class History {
|
class History implements Comic {
|
||||||
HistoryType type;
|
HistoryType type;
|
||||||
|
|
||||||
DateTime time;
|
DateTime time;
|
||||||
|
|
||||||
|
@override
|
||||||
String title;
|
String title;
|
||||||
|
|
||||||
|
@override
|
||||||
String subtitle;
|
String subtitle;
|
||||||
|
|
||||||
|
@override
|
||||||
String cover;
|
String cover;
|
||||||
|
|
||||||
int ep;
|
int ep;
|
||||||
@@ -44,6 +49,7 @@ class History {
|
|||||||
/// The number of episodes is 1-based.
|
/// The number of episodes is 1-based.
|
||||||
Set<int> readEpisode;
|
Set<int> readEpisode;
|
||||||
|
|
||||||
|
@override
|
||||||
int? maxPage;
|
int? maxPage;
|
||||||
|
|
||||||
History.fromModel(
|
History.fromModel(
|
||||||
@@ -137,6 +143,47 @@ class History {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(id, type);
|
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 {
|
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(
|
SliverGridComics(
|
||||||
comics: comics.map(
|
comics: comics,
|
||||||
(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(),
|
|
||||||
badgeBuilder: (c) {
|
badgeBuilder: (c) {
|
||||||
return ComicSource.find(c.sourceKey)?.name;
|
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/favorites.dart';
|
||||||
import 'package:venera/foundation/history.dart';
|
import 'package:venera/foundation/history.dart';
|
||||||
import 'package:venera/foundation/image_provider/cached_image.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/image_provider/local_comic_image.dart';
|
||||||
import 'package:venera/foundation/local.dart';
|
import 'package:venera/foundation/local.dart';
|
||||||
import 'package:venera/pages/accounts_page.dart';
|
import 'package:venera/pages/accounts_page.dart';
|
||||||
@@ -265,21 +266,6 @@ class _HistoryState extends State<_History> {
|
|||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: history.length,
|
itemCount: history.length,
|
||||||
itemBuilder: (context, index) {
|
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(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.to(
|
context.to(
|
||||||
@@ -302,7 +288,7 @@ class _HistoryState extends State<_History> {
|
|||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: AnimatedImage(
|
child: AnimatedImage(
|
||||||
image: imageProvider,
|
image: HistoryImageProvider(history[index]),
|
||||||
width: 96,
|
width: 96,
|
||||||
height: 128,
|
height: 128,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
Reference in New Issue
Block a user