comic list & explore page

This commit is contained in:
nyne
2024-10-01 16:37:49 +08:00
parent fdb3901fd1
commit 16857185fc
15 changed files with 1232 additions and 102 deletions

View File

@@ -11,6 +11,13 @@ class _Appdata {
var file = File(FilePath.join(App.dataPath, 'settings.json'));
await file.writeAsString(data);
}
Future<void> init() async {
var json = jsonDecode(await File(FilePath.join(App.dataPath, 'settings.json')).readAsString()) as Map<String, dynamic>;
for(var key in json.keys) {
settings[key] = json[key];
}
}
}
final appdata = _Appdata();
@@ -29,6 +36,9 @@ class _Settings {
'explore_pages': [],
'categories': [],
'favorites': [],
'showFavoriteStatusOnTile': true,
'showHistoryStatusOnTile': false,
'blockedWords': [],
};
operator[](String key) {

View File

@@ -389,35 +389,39 @@ class Comic {
final String id;
final String? subTitle;
final String? subtitle;
final List<String>? tags;
final String description;
final String sourceKey;
final int? maxPage;
const Comic(this.title, this.cover, this.id, this.subTitle, this.tags, this.description, this.sourceKey);
const Comic(this.title, this.cover, this.id, this.subtitle, this.tags, this.description, this.sourceKey, this.maxPage);
Map<String, dynamic> toJson() {
return {
"title": title,
"cover": cover,
"id": id,
"subTitle": subTitle,
"subTitle": subtitle,
"tags": tags,
"description": description,
"sourceKey": sourceKey,
"maxPage": maxPage,
};
}
Comic.fromJson(Map<String, dynamic> json, this.sourceKey)
: title = json["title"],
subTitle = json["subTitle"] ?? "",
subtitle = json["subTitle"] ?? "",
cover = json["cover"],
id = json["id"],
tags = List<String>.from(json["tags"] ?? []),
description = json["description"] ?? "";
description = json["description"] ?? "",
maxPage = json["maxPage"];
}
class ComicDetails with HistoryMixin {

View File

@@ -455,11 +455,11 @@ class LocalFavoritesManager {
final _cachedFavoritedIds = <String, bool>{};
bool isExist(String id) {
bool isExist(String id, ComicType type) {
if (_modifiedAfterLastCache) {
_cacheFavoritedIds();
}
return _cachedFavoritedIds.containsKey(id);
return _cachedFavoritedIds.containsKey("$id@${type.value}");
}
bool _modifiedAfterLastCache = true;
@@ -468,11 +468,11 @@ class LocalFavoritesManager {
_modifiedAfterLastCache = false;
_cachedFavoritedIds.clear();
for (var folder in folderNames) {
var res = _db.select("""
select id from "$folder";
var rows = _db.select("""
select id, type from "$folder";
""");
for (var row in res) {
_cachedFavoritedIds[row["id"]] = true;
for (var row in rows) {
_cachedFavoritedIds["${row["id"]}@${row["type"]}"] = true;
}
}
}

View File

@@ -1,12 +1,10 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/widgets.dart' show ChangeNotifier;
import 'package:sqlite3/sqlite3.dart';
import 'package:venera/foundation/comic_type.dart';
import 'app.dart';
import 'log.dart';
typedef HistoryType = ComicType;
@@ -113,7 +111,7 @@ class History {
int ep = 0,
int page = 0,
}) async {
var history = await HistoryManager().find(model.id);
var history = await HistoryManager().find(model.id, model.historyType);
if (history != null) {
return history;
}
@@ -147,36 +145,6 @@ class HistoryManager with ChangeNotifier {
Map<String, bool>? _cachedHistory;
Future<void> tryUpdateDb() async {
var file = File("${App.dataPath}/history_temp.db");
if (!file.existsSync()) {
Log.info("HistoryManager.tryUpdateDb", "db file not exist");
return;
}
var db = sqlite3.open(file.path);
var newHistory0 = db.select("""
select * from history
order by time DESC;
""");
var newHistory =
newHistory0.map((element) => History.fromRow(element)).toList();
if (file.existsSync()) {
var skips = 0;
for (var history in newHistory) {
if (findSync(history.id) == null) {
addHistory(history);
Log.info("HistoryManager", "merge history ${history.id}");
} else {
skips++;
}
}
Log.info("HistoryManager",
"merge history, skipped $skips, added ${newHistory.length - skips}");
}
db.dispose();
file.deleteSync();
}
Future<void> init() async {
_db = sqlite3.open("${App.dataPath}/history.db");
@@ -202,8 +170,8 @@ class HistoryManager with ChangeNotifier {
Future<void> addHistory(History newItem) async {
var res = _db.select("""
select * from history
where id == ?;
""", [newItem.id]);
where id == ? and type == ?;
""", [newItem.id, newItem.type.value]);
if (res.isEmpty) {
_db.execute("""
insert into history (id, title, subtitle, cover, time, type, ep, page, readEpisode, max_page)
@@ -224,8 +192,8 @@ class HistoryManager with ChangeNotifier {
_db.execute("""
update history
set time = ${DateTime.now().millisecondsSinceEpoch}
where id == ?;
""", [newItem.id]);
where id == ? and type == ?;
""", [newItem.id, newItem.type.value]);
}
updateCache();
notifyListeners();
@@ -235,13 +203,14 @@ class HistoryManager with ChangeNotifier {
_db.execute("""
update history
set time = ${DateTime.now().millisecondsSinceEpoch}, ep = ?, page = ?, readEpisode = ?, max_page = ?
where id == ?;
where id == ? and type == ?;
""", [
history.ep,
history.page,
history.readEpisode.join(','),
history.maxPage,
history.id
history.id,
history.type.value
]);
notifyListeners();
}
@@ -251,16 +220,16 @@ class HistoryManager with ChangeNotifier {
updateCache();
}
void remove(String id) async {
void remove(String id, ComicType type) async {
_db.execute("""
delete from history
where id == '$id';
""");
where id == ? and type == ?;
""", [id, type.value]);
updateCache();
}
Future<History?> find(String id) async {
return findSync(id);
Future<History?> find(String id, ComicType type) async {
return findSync(id, type);
}
void updateCache() {
@@ -273,7 +242,7 @@ class HistoryManager with ChangeNotifier {
}
}
History? findSync(String id) {
History? findSync(String id, ComicType type) {
if(_cachedHistory == null) {
updateCache();
}
@@ -283,8 +252,8 @@ class HistoryManager with ChangeNotifier {
var res = _db.select("""
select * from history
where id == ?;
""", [id]);
where id == ? and type == ?;
""", [id, type.value]);
if (res.isEmpty) {
return null;
}

View File

@@ -4,17 +4,22 @@ import 'dart:io';
import 'package:flutter/widgets.dart' show ChangeNotifier;
import 'package:path_provider/path_provider.dart';
import 'package:sqlite3/sqlite3.dart';
import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/comic_type.dart';
import 'app.dart';
class LocalComic {
final int id;
class LocalComic implements Comic{
@override
final String id;
@override
final String title;
@override
final String subtitle;
@override
final List<String> tags;
/// name of the directory, which is in `LocalManager.path`
@@ -26,6 +31,7 @@ class LocalComic {
final Map<String, String>? chapters;
/// relative path to the cover image
@override
final String cover;
final ComicType comicType;
@@ -45,7 +51,7 @@ class LocalComic {
});
LocalComic.fromRow(Row row)
: id = row[0] as int,
: id = row[0] as String,
title = row[1] as String,
subtitle = row[2] as String,
tags = List.from(jsonDecode(row[3] as String)),
@@ -56,6 +62,28 @@ class LocalComic {
createdAt = DateTime.fromMillisecondsSinceEpoch(row[8] as int);
File get coverFile => File('${LocalManager().path}/$directory/$cover');
@override
String get description => "";
@override
String get sourceKey => comicType.comicSource?.key ?? '_local_';
@override
Map<String, dynamic> toJson() {
return {
"title": title,
"cover": cover,
"id": id,
"subTitle": subtitle,
"tags": tags,
"description": description,
"sourceKey": sourceKey,
};
}
@override
int? get maxPage => null;
}
class LocalManager with ChangeNotifier {
@@ -77,7 +105,7 @@ class LocalManager with ChangeNotifier {
);
_db.execute('''
CREATE TABLE IF NOT EXISTS comics (
id INTEGER,
id TEXT NOT NULL,
title TEXT NOT NULL,
subtitle TEXT NOT NULL,
tags TEXT NOT NULL,
@@ -108,18 +136,21 @@ class LocalManager with ChangeNotifier {
}
}
int findValidId(ComicType type) {
final res = _db.select(
'SELECT id FROM comics WHERE comic_type = ? ORDER BY id DESC LIMIT 1;',
String findValidId(ComicType type) {
final res = _db.select('''
SELECT id FROM comics WHERE comic_type = ?
ORDER BY CAST(id AS INTEGER) DESC
LIMIT 1;
'''
[type.value],
);
if (res.isEmpty) {
return 1;
return '1';
}
return (res.first[0] as int) + 1;
return ((res.first[0] as int) + 1).toString();
}
Future<void> add(LocalComic comic, [int? id]) async {
Future<void> add(LocalComic comic, [String? id]) async {
_db.execute(
'INSERT INTO comics VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);',
[
@@ -137,7 +168,7 @@ class LocalManager with ChangeNotifier {
notifyListeners();
}
void remove(int id, ComicType comicType) async {
void remove(String id, ComicType comicType) async {
_db.execute(
'DELETE FROM comics WHERE id = ? AND comic_type = ?;',
[id, comicType.value],
@@ -155,7 +186,7 @@ class LocalManager with ChangeNotifier {
return res.map((row) => LocalComic.fromRow(row)).toList();
}
LocalComic? find(int id, ComicType comicType) {
LocalComic? find(String id, ComicType comicType) {
final res = _db.select(
'SELECT * FROM comics WHERE id = ? AND comic_type = ?;',
[id, comicType.value],

View File

@@ -64,6 +64,10 @@ extension WidgetExtension on Widget{
Widget fixHeight(double height){
return SizedBox(height: height, child: this);
}
Widget toSliver(){
return SliverToBoxAdapter(child: this);
}
}
/// create default text style