mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
home page
This commit is contained in:
@@ -144,7 +144,7 @@ class RandomCategoryPartWithRuntimeData extends BaseCategoryPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CategoryData getCategoryDataWithKey(String key) {
|
CategoryData getCategoryDataWithKey(String key) {
|
||||||
for (var source in ComicSource.sources) {
|
for (var source in ComicSource._sources) {
|
||||||
if (source.categoryData?.key == key) {
|
if (source.categoryData?.key == key) {
|
||||||
return source.categoryData!;
|
return source.categoryData!;
|
||||||
}
|
}
|
||||||
|
@@ -43,13 +43,31 @@ typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
|
|||||||
String imageKey)?;
|
String imageKey)?;
|
||||||
|
|
||||||
class ComicSource {
|
class ComicSource {
|
||||||
static List<ComicSource> sources = [];
|
static final List<ComicSource> _sources = [];
|
||||||
|
|
||||||
|
static final List<Function> _listeners = [];
|
||||||
|
|
||||||
|
static void addListener(Function listener) {
|
||||||
|
_listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeListener(Function listener) {
|
||||||
|
_listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void notifyListeners() {
|
||||||
|
for (var listener in _listeners) {
|
||||||
|
listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<ComicSource> all() => List.from(_sources);
|
||||||
|
|
||||||
static ComicSource? find(String key) =>
|
static ComicSource? find(String key) =>
|
||||||
sources.firstWhereOrNull((element) => element.key == key);
|
_sources.firstWhereOrNull((element) => element.key == key);
|
||||||
|
|
||||||
static ComicSource? fromIntKey(int key) =>
|
static ComicSource? fromIntKey(int key) =>
|
||||||
sources.firstWhereOrNull((element) => element.key.hashCode == key);
|
_sources.firstWhereOrNull((element) => element.key.hashCode == key);
|
||||||
|
|
||||||
static Future<void> init() async {
|
static Future<void> init() async {
|
||||||
final path = "${App.dataPath}/comic_source";
|
final path = "${App.dataPath}/comic_source";
|
||||||
@@ -62,7 +80,7 @@ class ComicSource {
|
|||||||
try {
|
try {
|
||||||
var source = await ComicSourceParser()
|
var source = await ComicSourceParser()
|
||||||
.parse(await entity.readAsString(), entity.absolute.path);
|
.parse(await entity.readAsString(), entity.absolute.path);
|
||||||
sources.add(source);
|
_sources.add(source);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Log.error("ComicSource", "$e\n$s");
|
Log.error("ComicSource", "$e\n$s");
|
||||||
}
|
}
|
||||||
@@ -71,9 +89,20 @@ class ComicSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future reload() async {
|
static Future reload() async {
|
||||||
sources.clear();
|
_sources.clear();
|
||||||
JsEngine().runCode("ComicSource.sources = {};");
|
JsEngine().runCode("ComicSource.sources = {};");
|
||||||
await init();
|
await init();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add(ComicSource source) {
|
||||||
|
_sources.add(source);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove(String key) {
|
||||||
|
_sources.removeWhere((element) => element.key == key);
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Name of this source.
|
/// Name of this source.
|
||||||
@@ -123,7 +152,7 @@ class ComicSource {
|
|||||||
|
|
||||||
var data = <String, dynamic>{};
|
var data = <String, dynamic>{};
|
||||||
|
|
||||||
bool get isLogin => data["account"] != null;
|
bool get isLogged => data["account"] != null;
|
||||||
|
|
||||||
final String filePath;
|
final String filePath;
|
||||||
|
|
||||||
|
@@ -105,7 +105,7 @@ class ComicSourceParser {
|
|||||||
throw ComicSourceParseException("minAppVersion $minAppVersion is required");
|
throw ComicSourceParseException("minAppVersion $minAppVersion is required");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(var source in ComicSource.sources){
|
for(var source in ComicSource.all()){
|
||||||
if(source.key == key){
|
if(source.key == key){
|
||||||
throw ComicSourceParseException("key($key) already exists");
|
throw ComicSourceParseException("key($key) already exists");
|
||||||
}
|
}
|
||||||
@@ -188,8 +188,7 @@ class ComicSourceParser {
|
|||||||
ComicSource.sources.$_key.account.login(${jsonEncode(account)},
|
ComicSource.sources.$_key.account.login(${jsonEncode(account)},
|
||||||
${jsonEncode(pwd)})
|
${jsonEncode(pwd)})
|
||||||
""");
|
""");
|
||||||
var source = ComicSource.sources
|
var source = ComicSource.find(_key!)!;
|
||||||
.firstWhere((element) => element.key == _key);
|
|
||||||
source.data["account"] = <String>[account, pwd];
|
source.data["account"] = <String>[account, pwd];
|
||||||
source.saveData();
|
source.saveData();
|
||||||
return const Res(true);
|
return const Res(true);
|
||||||
@@ -461,7 +460,7 @@ class ComicSourceParser {
|
|||||||
final bool multiFolder = _getValue("favorites.multiFolder");
|
final bool multiFolder = _getValue("favorites.multiFolder");
|
||||||
|
|
||||||
Future<Res<T>> retryZone<T>(Future<Res<T>> Function() func) async{
|
Future<Res<T>> retryZone<T>(Future<Res<T>> Function() func) async{
|
||||||
if(!ComicSource.find(_key!)!.isLogin){
|
if(!ComicSource.find(_key!)!.isLogged){
|
||||||
return const Res.error("Not login");
|
return const Res.error("Not login");
|
||||||
}
|
}
|
||||||
var res = await func();
|
var res = await func();
|
||||||
|
@@ -15,7 +15,7 @@ class ComicType {
|
|||||||
if(this == local) {
|
if(this == local) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return ComicSource.sources.firstWhere((element) => element.intKey == value);
|
return ComicSource.fromIntKey(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,7 +22,6 @@ import 'package:pointycastle/block/modes/ecb.dart';
|
|||||||
import 'package:pointycastle/block/modes/ofb.dart';
|
import 'package:pointycastle/block/modes/ofb.dart';
|
||||||
import 'package:venera/network/app_dio.dart';
|
import 'package:venera/network/app_dio.dart';
|
||||||
import 'package:venera/network/cookie_jar.dart';
|
import 'package:venera/network/cookie_jar.dart';
|
||||||
import 'package:venera/utils/ext.dart';
|
|
||||||
|
|
||||||
import 'comic_source/comic_source.dart';
|
import 'comic_source/comic_source.dart';
|
||||||
import 'consts.dart';
|
import 'consts.dart';
|
||||||
@@ -107,8 +106,7 @@ class JsEngine with _JSEngineApi{
|
|||||||
{
|
{
|
||||||
String key = message["key"];
|
String key = message["key"];
|
||||||
String dataKey = message["data_key"];
|
String dataKey = message["data_key"];
|
||||||
return ComicSource.sources
|
return ComicSource.find(key)
|
||||||
.firstWhereOrNull((element) => element.key == key)
|
|
||||||
?.data[dataKey];
|
?.data[dataKey];
|
||||||
}
|
}
|
||||||
case 'save_data':
|
case 'save_data':
|
||||||
@@ -116,8 +114,7 @@ class JsEngine with _JSEngineApi{
|
|||||||
String key = message["key"];
|
String key = message["key"];
|
||||||
String dataKey = message["data_key"];
|
String dataKey = message["data_key"];
|
||||||
var data = message["data"];
|
var data = message["data"];
|
||||||
var source = ComicSource.sources
|
var source = ComicSource.find(key)!;
|
||||||
.firstWhere((element) => element.key == key);
|
|
||||||
source.data[dataKey] = data;
|
source.data[dataKey] = data;
|
||||||
source.saveData();
|
source.saveData();
|
||||||
}
|
}
|
||||||
@@ -125,8 +122,7 @@ class JsEngine with _JSEngineApi{
|
|||||||
{
|
{
|
||||||
String key = message["key"];
|
String key = message["key"];
|
||||||
String dataKey = message["data_key"];
|
String dataKey = message["data_key"];
|
||||||
var source = ComicSource.sources
|
var source = ComicSource.find(key);
|
||||||
.firstWhereOrNull((element) => element.key == key);
|
|
||||||
source?.data.remove(dataKey);
|
source?.data.remove(dataKey);
|
||||||
source?.saveData();
|
source?.saveData();
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:venera/components/components.dart';
|
import 'package:venera/components/components.dart';
|
||||||
import 'package:venera/foundation/app.dart';
|
import 'package:venera/foundation/app.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/image_provider/cached_image.dart';
|
import 'package:venera/foundation/image_provider/cached_image.dart';
|
||||||
@@ -21,19 +22,47 @@ class HomePage extends StatelessWidget {
|
|||||||
slivers: [
|
slivers: [
|
||||||
_History(),
|
_History(),
|
||||||
_Local(),
|
_Local(),
|
||||||
|
_ComicSourceWidget(),
|
||||||
|
_AccountsWidget(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _History extends StatelessWidget {
|
class _History extends StatefulWidget {
|
||||||
const _History();
|
const _History();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<_History> createState() => _HistoryState();
|
||||||
final history = HistoryManager().getRecent();
|
}
|
||||||
final count = HistoryManager().count();
|
|
||||||
|
|
||||||
|
class _HistoryState extends State<_History> {
|
||||||
|
late List<History> history;
|
||||||
|
late int count;
|
||||||
|
|
||||||
|
void onHistoryChange() {
|
||||||
|
setState(() {
|
||||||
|
history = HistoryManager().getRecent();
|
||||||
|
count = HistoryManager().count();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
history = HistoryManager().getRecent();
|
||||||
|
count = HistoryManager().count();
|
||||||
|
HistoryManager().addListener(onHistoryChange);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
HistoryManager().removeListener(onHistoryChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
@@ -117,14 +146,40 @@ class _History extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Local extends StatelessWidget {
|
class _Local extends StatefulWidget {
|
||||||
const _Local();
|
const _Local();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<_Local> createState() => _LocalState();
|
||||||
final local = LocalManager().getRecent();
|
}
|
||||||
final count = LocalManager().count;
|
|
||||||
|
|
||||||
|
class _LocalState extends State<_Local> {
|
||||||
|
late List<LocalComic> local;
|
||||||
|
late int count;
|
||||||
|
|
||||||
|
void onLocalComicsChange() {
|
||||||
|
setState(() {
|
||||||
|
local = LocalManager().getRecent();
|
||||||
|
count = LocalManager().count;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
local = LocalManager().getRecent();
|
||||||
|
count = LocalManager().count;
|
||||||
|
LocalManager().addListener(onLocalComicsChange);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
LocalManager().removeListener(onLocalComicsChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
@@ -495,3 +550,198 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ComicSourceWidget extends StatefulWidget {
|
||||||
|
const _ComicSourceWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ComicSourceWidget> createState() => _ComicSourceWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ComicSourceWidgetState extends State<_ComicSourceWidget> {
|
||||||
|
late List<String> comicSources;
|
||||||
|
|
||||||
|
void onComicSourceChange() {
|
||||||
|
setState(() {
|
||||||
|
comicSources = ComicSource.all().map((e) => e.name).toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
comicSources = ComicSource.all().map((e) => e.name).toList();
|
||||||
|
ComicSource.addListener(onComicSourceChange);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
ComicSource.removeListener(onComicSourceChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.outlineVariant,
|
||||||
|
width: 0.6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 56,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Text('Comic Source'.tl, style: ts.s18),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(comicSources.length.toString(), style: ts.s12),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(Icons.arrow_right),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).paddingHorizontal(16),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Wrap(
|
||||||
|
children: comicSources.map((e) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(e),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountsWidget extends StatefulWidget {
|
||||||
|
const _AccountsWidget();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_AccountsWidget> createState() => _AccountsWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountsWidgetState extends State<_AccountsWidget> {
|
||||||
|
late List<String> accounts;
|
||||||
|
|
||||||
|
void onComicSourceChange() {
|
||||||
|
setState(() {
|
||||||
|
for(var c in ComicSource.all()) {
|
||||||
|
if(c.isLogged) {
|
||||||
|
accounts.add(c.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
accounts = [];
|
||||||
|
for(var c in ComicSource.all()) {
|
||||||
|
if(c.isLogged) {
|
||||||
|
accounts.add(c.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ComicSource.addListener(onComicSourceChange);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
ComicSource.removeListener(onComicSourceChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.outlineVariant,
|
||||||
|
width: 0.6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 56,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Text('Accounts'.tl, style: ts.s18),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(accounts.length.toString(), style: ts.s12),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Icon(Icons.arrow_right),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).paddingHorizontal(16),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Wrap(
|
||||||
|
children: accounts.map((e) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(e),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user