mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
fix local comic display
This commit is contained in:
@@ -149,6 +149,11 @@ class ComicTile extends StatelessWidget {
|
|||||||
ImageProvider image;
|
ImageProvider image;
|
||||||
if (comic is LocalComic) {
|
if (comic is LocalComic) {
|
||||||
image = FileImage((comic as LocalComic).coverFile);
|
image = FileImage((comic as LocalComic).coverFile);
|
||||||
|
} else if (comic.cover.startsWith('file://')) {
|
||||||
|
image = FileImage(File(comic.cover.substring(7)));
|
||||||
|
} else if (comic.sourceKey == 'local') {
|
||||||
|
var localComic = LocalManager().find(comic.id, ComicType.local);
|
||||||
|
image = FileImage(localComic!.coverFile);
|
||||||
} else {
|
} else {
|
||||||
image = CachedImageProvider(comic.cover, sourceKey: comic.sourceKey);
|
image = CachedImageProvider(comic.cover, sourceKey: comic.sourceKey);
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ import 'package:venera/network/cloudflare.dart';
|
|||||||
import 'package:venera/pages/comic_page.dart';
|
import 'package:venera/pages/comic_page.dart';
|
||||||
import 'package:venera/pages/favorites/favorites_page.dart';
|
import 'package:venera/pages/favorites/favorites_page.dart';
|
||||||
import 'package:venera/utils/ext.dart';
|
import 'package:venera/utils/ext.dart';
|
||||||
|
import 'package:venera/utils/io.dart';
|
||||||
import 'package:venera/utils/tags_translation.dart';
|
import 'package:venera/utils/tags_translation.dart';
|
||||||
import 'package:venera/utils/translations.dart';
|
import 'package:venera/utils/translations.dart';
|
||||||
|
|
||||||
|
@@ -113,6 +113,7 @@ abstract class LoadingState<T extends StatefulWidget, S extends Object>
|
|||||||
if (res.success) {
|
if (res.success) {
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
|
if(!mounted) return res;
|
||||||
if (retry >= 3) {
|
if (retry >= 3) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -170,6 +171,7 @@ abstract class LoadingState<T extends StatefulWidget, S extends Object>
|
|||||||
isLoading = true;
|
isLoading = true;
|
||||||
Future.microtask(() {
|
Future.microtask(() {
|
||||||
loadDataWithRetry().then((value) async {
|
loadDataWithRetry().then((value) async {
|
||||||
|
if(!mounted) return;
|
||||||
if (value.success) {
|
if (value.success) {
|
||||||
data = value.data;
|
data = value.data;
|
||||||
await onDataLoaded();
|
await onDataLoaded();
|
||||||
|
@@ -11,6 +11,14 @@ class ComicType {
|
|||||||
@override
|
@override
|
||||||
int get hashCode => value.hashCode;
|
int get hashCode => value.hashCode;
|
||||||
|
|
||||||
|
String get sourceKey {
|
||||||
|
if(this == local) {
|
||||||
|
return "local";
|
||||||
|
} else {
|
||||||
|
return comicSource!.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ComicSource? get comicSource {
|
ComicSource? get comicSource {
|
||||||
if(this == local) {
|
if(this == local) {
|
||||||
return null;
|
return null;
|
||||||
|
@@ -19,6 +19,11 @@ extension Navigation on BuildContext {
|
|||||||
.push<T>(AppPageRoute(builder: (context) => builder()));
|
.push<T>(AppPageRoute(builder: (context) => builder()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> toReplacement<T>(Widget Function() builder) {
|
||||||
|
return Navigator.of(this)
|
||||||
|
.pushReplacement(AppPageRoute(builder: (context) => builder()));
|
||||||
|
}
|
||||||
|
|
||||||
double get width => MediaQuery.of(this).size.width;
|
double get width => MediaQuery.of(this).size.width;
|
||||||
|
|
||||||
double get height => MediaQuery.of(this).size.height;
|
double get height => MediaQuery.of(this).size.height;
|
||||||
|
@@ -55,7 +55,7 @@ class FavoriteItem implements Comic {
|
|||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var s = "FavoriteItem: $name $author $coverPath $hashCode $tags";
|
var s = "FavoriteItem: $name $author $coverPath $hashCode $tags";
|
||||||
if(s.length > 100) {
|
if (s.length > 100) {
|
||||||
return s.substring(0, 100);
|
return s.substring(0, 100);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
@@ -65,7 +65,9 @@ class FavoriteItem implements Comic {
|
|||||||
String get cover => coverPath;
|
String get cover => coverPath;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => "$time | ${type.comicSource?.name ?? "Unknown"}";
|
String get description {
|
||||||
|
return "$time | ${type == ComicType.local ? 'local' : type.comicSource?.name ?? "Unknown"}";
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? get favoriteId => null;
|
String? get favoriteId => null;
|
||||||
@@ -77,7 +79,7 @@ class FavoriteItem implements Comic {
|
|||||||
int? get maxPage => null;
|
int? get maxPage => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sourceKey => type.comicSource?.key ?? "Unknown:${type.value}";
|
String get sourceKey => type == ComicType.local ? 'local' : type.comicSource?.key ?? "Unknown:${type.value}";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double? get stars => null;
|
double? get stars => null;
|
||||||
@@ -514,6 +516,13 @@ class LocalFavoritesManager {
|
|||||||
update "$folder"
|
update "$folder"
|
||||||
set name = ?, author = ?, cover_path = ?, tags = ?
|
set name = ?, author = ?, cover_path = ?, tags = ?
|
||||||
where id == ? and type == ?;
|
where id == ? and type == ?;
|
||||||
""", [comic.name, comic.author, comic.coverPath, comic.tags.join(","), comic.id, comic.type.value]);
|
""", [
|
||||||
|
comic.name,
|
||||||
|
comic.author,
|
||||||
|
comic.coverPath,
|
||||||
|
comic.tags.join(","),
|
||||||
|
comic.id,
|
||||||
|
comic.type.value
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,7 +79,7 @@ class LocalComic with HistoryMixin implements Comic {
|
|||||||
String get description => "";
|
String get description => "";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get sourceKey => comicType.comicSource?.key ?? '_local_';
|
String get sourceKey => comicType == ComicType.local ? "local" : "";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@@ -214,7 +214,7 @@ class LocalManager with ChangeNotifier {
|
|||||||
SELECT id FROM comics WHERE comic_type = ?
|
SELECT id FROM comics WHERE comic_type = ?
|
||||||
ORDER BY CAST(id AS INTEGER) DESC
|
ORDER BY CAST(id AS INTEGER) DESC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
'''[type.value],
|
''', [type.value],
|
||||||
);
|
);
|
||||||
if (res.isEmpty) {
|
if (res.isEmpty) {
|
||||||
return '1';
|
return '1';
|
||||||
@@ -341,7 +341,7 @@ class LocalManager with ChangeNotifier {
|
|||||||
if (comic == null) return false;
|
if (comic == null) return false;
|
||||||
if (comic.chapters == null) return true;
|
if (comic.chapters == null) return true;
|
||||||
return comic.downloadedChapters
|
return comic.downloadedChapters
|
||||||
.contains(comic.chapters!.keys.elementAt(ep));
|
.contains(comic.chapters!.keys.elementAt(ep-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DownloadTask> downloadingTasks = [];
|
List<DownloadTask> downloadingTasks = [];
|
||||||
|
@@ -161,7 +161,7 @@ class NetworkCacheManager implements Interceptor {
|
|||||||
return handler.next(response);
|
return handler.next(response);
|
||||||
}
|
}
|
||||||
var size = _calculateSize(response.data);
|
var size = _calculateSize(response.data);
|
||||||
if(size != null && size < 1024 * 1024) {
|
if(size != null && size < 1024 * 1024 && size > 1024) {
|
||||||
var cache = NetworkCache(
|
var cache = NetworkCache(
|
||||||
uri: response.requestOptions.uri,
|
uri: response.requestOptions.uri,
|
||||||
requestHeaders: response.requestOptions.headers,
|
requestHeaders: response.requestOptions.headers,
|
||||||
|
@@ -203,6 +203,8 @@ class ImagesDownloadTask extends DownloadTask with _TransferSpeedMixin {
|
|||||||
@override
|
@override
|
||||||
void resume() async {
|
void resume() async {
|
||||||
if (_isRunning) return;
|
if (_isRunning) return;
|
||||||
|
_isError = false;
|
||||||
|
_message = "Resuming...";
|
||||||
_isRunning = true;
|
_isRunning = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
runRecorder();
|
runRecorder();
|
||||||
|
@@ -72,6 +72,8 @@ class _ComicPageState extends LoadingState<ComicPage, ComicDetails>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isFirst = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildContent(BuildContext context, ComicDetails data) {
|
Widget buildContent(BuildContext context, ComicDetails data) {
|
||||||
return SmoothCustomScrollView(
|
return SmoothCustomScrollView(
|
||||||
@@ -91,8 +93,37 @@ class _ComicPageState extends LoadingState<ComicPage, ComicDetails>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Res<ComicDetails>> loadData() async {
|
Future<Res<ComicDetails>> loadData() async {
|
||||||
|
if (widget.sourceKey == 'local') {
|
||||||
|
var localComic = LocalManager().find(widget.id, ComicType.local);
|
||||||
|
if (localComic == null) {
|
||||||
|
return const Res.error('Local comic not found');
|
||||||
|
}
|
||||||
|
var history = await HistoryManager().find(widget.id, ComicType.local);
|
||||||
|
if(isFirst) {
|
||||||
|
Future.microtask(() {
|
||||||
|
App.rootContext.to(() {
|
||||||
|
return Reader(
|
||||||
|
type: ComicType.local,
|
||||||
|
cid: widget.id,
|
||||||
|
name: localComic.title,
|
||||||
|
chapters: localComic.chapters,
|
||||||
|
history: history ??
|
||||||
|
History.fromModel(
|
||||||
|
model: localComic,
|
||||||
|
ep: 0,
|
||||||
|
page: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
App.mainNavigatorKey!.currentContext!.pop();
|
||||||
|
});
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
|
return const Res.error('Local comic');
|
||||||
|
}
|
||||||
var comicSource = ComicSource.find(widget.sourceKey);
|
var comicSource = ComicSource.find(widget.sourceKey);
|
||||||
if(comicSource == null) {
|
if (comicSource == null) {
|
||||||
return const Res.error('Comic source not found');
|
return const Res.error('Comic source not found');
|
||||||
}
|
}
|
||||||
isAddToLocalFav = LocalFavoritesManager().isExist(
|
isAddToLocalFav = LocalFavoritesManager().isExist(
|
||||||
@@ -101,7 +132,7 @@ class _ComicPageState extends LoadingState<ComicPage, ComicDetails>
|
|||||||
);
|
);
|
||||||
history = await HistoryManager()
|
history = await HistoryManager()
|
||||||
.find(widget.id, ComicType(widget.sourceKey.hashCode));
|
.find(widget.id, ComicType(widget.sourceKey.hashCode));
|
||||||
return comicSource!.loadComicInfo!(widget.id);
|
return comicSource.loadComicInfo!(widget.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -500,7 +531,11 @@ abstract mixin class _ComicPageActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void share() {
|
void share() {
|
||||||
Share.shareText(comic.title);
|
var text = comic.title;
|
||||||
|
if (comic.url != null) {
|
||||||
|
text += '\n${comic.url}';
|
||||||
|
}
|
||||||
|
Share.shareText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read the comic
|
/// read the comic
|
||||||
@@ -694,8 +729,7 @@ abstract mixin class _ComicPageActions {
|
|||||||
setState(() {
|
setState(() {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
});
|
});
|
||||||
comicSource.starRatingFunc!
|
comicSource.starRatingFunc!(comic.id, rating.round())
|
||||||
(comic.id, rating.round())
|
|
||||||
.then((value) {
|
.then((value) {
|
||||||
if (value.success) {
|
if (value.success) {
|
||||||
App.rootContext
|
App.rootContext
|
||||||
|
@@ -66,7 +66,16 @@ class _ExplorePageState extends State<ExplorePage>
|
|||||||
return NetworkError(
|
return NetworkError(
|
||||||
message: "No Explore Pages".tl,
|
message: "No Explore Pages".tl,
|
||||||
retry: () {
|
retry: () {
|
||||||
setState(() {});
|
setState(() {
|
||||||
|
pages = ComicSource.all()
|
||||||
|
.map((e) => e.explorePages)
|
||||||
|
.expand((e) => e.map((e) => e.title))
|
||||||
|
.toList();
|
||||||
|
controller = TabController(
|
||||||
|
length: pages.length,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
withAppbar: false,
|
withAppbar: false,
|
||||||
);
|
);
|
||||||
|
@@ -98,7 +98,7 @@ void addFavorite(Comic comic) {
|
|||||||
name: comic.title,
|
name: comic.title,
|
||||||
coverPath: comic.cover,
|
coverPath: comic.cover,
|
||||||
author: comic.subtitle ?? '',
|
author: comic.subtitle ?? '',
|
||||||
type: ComicType(comic.sourceKey.hashCode),
|
type: ComicType((comic.sourceKey == 'local' ? 0 : comic.sourceKey.hashCode)),
|
||||||
tags: comic.tags ?? [],
|
tags: comic.tags ?? [],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@@ -4,6 +4,8 @@ import 'package:venera/foundation/app.dart';
|
|||||||
import 'package:venera/foundation/comic_source/comic_source.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/foundation/history.dart';
|
import 'package:venera/foundation/history.dart';
|
||||||
|
import 'package:venera/foundation/local.dart';
|
||||||
|
import 'package:venera/utils/ext.dart';
|
||||||
import 'package:venera/utils/translations.dart';
|
import 'package:venera/utils/translations.dart';
|
||||||
|
|
||||||
class HistoryPage extends StatefulWidget {
|
class HistoryPage extends StatefulWidget {
|
||||||
@@ -78,9 +80,19 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
SliverGridComics(
|
SliverGridComics(
|
||||||
comics: comics.map(
|
comics: comics.map(
|
||||||
(e) {
|
(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(
|
return Comic(
|
||||||
e.title,
|
e.title,
|
||||||
e.cover,
|
cover,
|
||||||
e.id,
|
e.id,
|
||||||
e.subtitle,
|
e.subtitle,
|
||||||
null,
|
null,
|
||||||
@@ -100,7 +112,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
icon: Icons.remove,
|
icon: Icons.remove,
|
||||||
text: 'Remove'.tl,
|
text: 'Remove'.tl,
|
||||||
onClick: () {
|
onClick: () {
|
||||||
if(c.sourceKey.startsWith("Invalid")) {
|
if (c.sourceKey.startsWith("Invalid")) {
|
||||||
HistoryManager().remove(
|
HistoryManager().remove(
|
||||||
c.id,
|
c.id,
|
||||||
ComicType(int.parse(c.sourceKey.split(':')[1])),
|
ComicType(int.parse(c.sourceKey.split(':')[1])),
|
||||||
|
@@ -15,6 +15,7 @@ import 'package:venera/pages/comic_source_page.dart';
|
|||||||
import 'package:venera/pages/downloading_page.dart';
|
import 'package:venera/pages/downloading_page.dart';
|
||||||
import 'package:venera/pages/history_page.dart';
|
import 'package:venera/pages/history_page.dart';
|
||||||
import 'package:venera/pages/search_page.dart';
|
import 'package:venera/pages/search_page.dart';
|
||||||
|
import 'package:venera/utils/ext.dart';
|
||||||
import 'package:venera/utils/io.dart';
|
import 'package:venera/utils/io.dart';
|
||||||
import 'package:venera/utils/translations.dart';
|
import 'package:venera/utils/translations.dart';
|
||||||
|
|
||||||
@@ -155,12 +156,26 @@ 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,
|
||||||
|
);
|
||||||
|
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(
|
||||||
() => ComicPage(
|
() => ComicPage(
|
||||||
id: history[index].id,
|
id: history[index].id,
|
||||||
sourceKey: history[index].type.comicSource!.key,
|
sourceKey: history[index].type.sourceKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -177,10 +192,7 @@ class _HistoryState extends State<_History> {
|
|||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: AnimatedImage(
|
child: AnimatedImage(
|
||||||
image: CachedImageProvider(
|
image: imageProvider,
|
||||||
history[index].cover,
|
|
||||||
sourceKey: history[index].type.comicSource?.key,
|
|
||||||
),
|
|
||||||
width: 96,
|
width: 96,
|
||||||
height: 128,
|
height: 128,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
@@ -196,7 +196,7 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: sources.map((e) {
|
children: sources.map((e) {
|
||||||
return OptionChip(
|
return OptionChip(
|
||||||
text: e.name.tl,
|
text: e.name,
|
||||||
isSelected: searchTarget == e.key,
|
isSelected: searchTarget == e.key,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@@ -75,7 +75,7 @@ extension StringExt on String{
|
|||||||
|
|
||||||
bool _isURL(){
|
bool _isURL(){
|
||||||
final regex = RegExp(
|
final regex = RegExp(
|
||||||
r'^((http|https|ftp)://)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-|]*[\w@?^=%&/~+#-])?$',
|
r'^((http|https|ftp)://)[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-|]*[\w@?^=%&/~+#-])?$',
|
||||||
caseSensitive: false);
|
caseSensitive: false);
|
||||||
return regex.hasMatch(this);
|
return regex.hasMatch(this);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user