mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
comic list & explore page
This commit is contained in:
@@ -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) {
|
||||
|
@@ -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 {
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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],
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user