mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
Improve init. Close #236
This commit is contained in:
@@ -640,5 +640,5 @@ TransitionBuilder VirtualWindowFrameInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void debug() {
|
void debug() {
|
||||||
ComicSource.reload();
|
ComicSourceManager().reload();
|
||||||
}
|
}
|
||||||
|
@@ -77,10 +77,15 @@ class _App {
|
|||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
cachePath = (await getApplicationCacheDirectory()).path;
|
cachePath = (await getApplicationCacheDirectory()).path;
|
||||||
dataPath = (await getApplicationSupportDirectory()).path;
|
dataPath = (await getApplicationSupportDirectory()).path;
|
||||||
await data.init();
|
}
|
||||||
await history.init();
|
|
||||||
await favorites.init();
|
Future<void> initComponents() async {
|
||||||
await local.init();
|
await Future.wait([
|
||||||
|
data.init(),
|
||||||
|
history.init(),
|
||||||
|
favorites.init(),
|
||||||
|
local.init(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Function? _forceRebuildHandler;
|
Function? _forceRebuildHandler;
|
||||||
|
@@ -145,7 +145,7 @@ class RandomCategoryPartWithRuntimeData extends BaseCategoryPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CategoryData getCategoryDataWithKey(String key) {
|
CategoryData getCategoryDataWithKey(String key) {
|
||||||
for (var source in ComicSource._sources) {
|
for (var source in ComicSource.all()) {
|
||||||
if (source.categoryData?.key == key) {
|
if (source.categoryData?.key == key) {
|
||||||
return source.categoryData!;
|
return source.categoryData!;
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ import 'package:venera/foundation/history.dart';
|
|||||||
import 'package:venera/foundation/res.dart';
|
import 'package:venera/foundation/res.dart';
|
||||||
import 'package:venera/utils/data_sync.dart';
|
import 'package:venera/utils/data_sync.dart';
|
||||||
import 'package:venera/utils/ext.dart';
|
import 'package:venera/utils/ext.dart';
|
||||||
|
import 'package:venera/utils/init.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';
|
||||||
|
|
||||||
@@ -27,81 +28,29 @@ part 'parser.dart';
|
|||||||
|
|
||||||
part 'models.dart';
|
part 'models.dart';
|
||||||
|
|
||||||
/// build comic list, [Res.subData] should be maxPage or null if there is no limit.
|
part 'types.dart';
|
||||||
typedef ComicListBuilder = Future<Res<List<Comic>>> Function(int page);
|
|
||||||
|
|
||||||
/// build comic list with next param, [Res.subData] should be next page param or null if there is no next page.
|
class ComicSourceManager with ChangeNotifier, Init {
|
||||||
typedef ComicListBuilderWithNext = Future<Res<List<Comic>>> Function(
|
final List<ComicSource> _sources = [];
|
||||||
String? next);
|
|
||||||
|
|
||||||
typedef LoginFunction = Future<Res<bool>> Function(String, String);
|
static ComicSourceManager? _instance;
|
||||||
|
|
||||||
typedef LoadComicFunc = Future<Res<ComicDetails>> Function(String id);
|
ComicSourceManager._create();
|
||||||
|
|
||||||
typedef LoadComicPagesFunc = Future<Res<List<String>>> Function(
|
factory ComicSourceManager() => _instance ??= ComicSourceManager._create();
|
||||||
String id, String? ep);
|
|
||||||
|
|
||||||
typedef CommentsLoader = Future<Res<List<Comment>>> Function(
|
List<ComicSource> all() => List.from(_sources);
|
||||||
String id, String? subId, int page, String? replyTo);
|
|
||||||
|
|
||||||
typedef SendCommentFunc = Future<Res<bool>> Function(
|
ComicSource? find(String key) =>
|
||||||
String id, String? subId, String content, String? replyTo);
|
|
||||||
|
|
||||||
typedef GetImageLoadingConfigFunc = Future<Map<String, dynamic>> Function(
|
|
||||||
String imageKey, String comicId, String epId)?;
|
|
||||||
typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
|
|
||||||
String imageKey)?;
|
|
||||||
|
|
||||||
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(
|
|
||||||
String comicId, String? next);
|
|
||||||
|
|
||||||
typedef LikeOrUnlikeComicFunc = Future<Res<bool>> Function(
|
|
||||||
String comicId, bool isLiking);
|
|
||||||
|
|
||||||
/// [isLiking] is true if the user is liking the comment, false if unliking.
|
|
||||||
/// return the new likes count or null.
|
|
||||||
typedef LikeCommentFunc = Future<Res<int?>> Function(
|
|
||||||
String comicId, String? subId, String commentId, bool isLiking);
|
|
||||||
|
|
||||||
/// [isUp] is true if the user is upvoting the comment, false if downvoting.
|
|
||||||
/// return the new vote count or null.
|
|
||||||
typedef VoteCommentFunc = Future<Res<int?>> Function(
|
|
||||||
String comicId, String? subId, String commentId, bool isUp, bool isCancel);
|
|
||||||
|
|
||||||
typedef HandleClickTagEvent = Map<String, String> Function(
|
|
||||||
String namespace, String tag);
|
|
||||||
|
|
||||||
/// [rating] is the rating value, 0-10. 1 represents 0.5 star.
|
|
||||||
typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating);
|
|
||||||
|
|
||||||
class ComicSource {
|
|
||||||
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) =>
|
|
||||||
_sources.firstWhereOrNull((element) => element.key == key);
|
_sources.firstWhereOrNull((element) => element.key == key);
|
||||||
|
|
||||||
static ComicSource? fromIntKey(int key) =>
|
ComicSource? fromIntKey(int key) =>
|
||||||
_sources.firstWhereOrNull((element) => element.key.hashCode == key);
|
_sources.firstWhereOrNull((element) => element.key.hashCode == key);
|
||||||
|
|
||||||
static Future<void> init() async {
|
@override
|
||||||
|
@protected
|
||||||
|
Future<void> doInit() async {
|
||||||
|
await JsEngine().ensureInit();
|
||||||
final path = "${App.dataPath}/comic_source";
|
final path = "${App.dataPath}/comic_source";
|
||||||
if (!(await Directory(path).exists())) {
|
if (!(await Directory(path).exists())) {
|
||||||
Directory(path).create();
|
Directory(path).create();
|
||||||
@@ -120,26 +69,49 @@ class ComicSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future reload() async {
|
Future reload() async {
|
||||||
_sources.clear();
|
_sources.clear();
|
||||||
JsEngine().runCode("ComicSource.sources = {};");
|
JsEngine().runCode("ComicSource.sources = {};");
|
||||||
await init();
|
await doInit();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add(ComicSource source) {
|
void add(ComicSource source) {
|
||||||
_sources.add(source);
|
_sources.add(source);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove(String key) {
|
void remove(String key) {
|
||||||
_sources.removeWhere((element) => element.key == key);
|
_sources.removeWhere((element) => element.key == key);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
static final availableUpdates = <String, String>{};
|
bool get isEmpty => _sources.isEmpty;
|
||||||
|
|
||||||
static bool get isEmpty => _sources.isEmpty;
|
/// Key is the source key, value is the version.
|
||||||
|
final _availableUpdates = <String, String>{};
|
||||||
|
|
||||||
|
void updateAvailableUpdates(Map<String, String> updates) {
|
||||||
|
_availableUpdates.addAll(updates);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> get availableUpdates => Map.from(_availableUpdates);
|
||||||
|
|
||||||
|
void notifyStateChange() {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ComicSource {
|
||||||
|
static List<ComicSource> all() => ComicSourceManager().all();
|
||||||
|
|
||||||
|
static ComicSource? find(String key) => ComicSourceManager().find(key);
|
||||||
|
|
||||||
|
static ComicSource? fromIntKey(int key) =>
|
||||||
|
ComicSourceManager().fromIntKey(key);
|
||||||
|
|
||||||
|
static bool get isEmpty => ComicSourceManager().isEmpty;
|
||||||
|
|
||||||
/// Name of this source.
|
/// Name of this source.
|
||||||
final String name;
|
final String name;
|
||||||
|
48
lib/foundation/comic_source/types.dart
Normal file
48
lib/foundation/comic_source/types.dart
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
part of 'comic_source.dart';
|
||||||
|
|
||||||
|
/// build comic list, [Res.subData] should be maxPage or null if there is no limit.
|
||||||
|
typedef ComicListBuilder = Future<Res<List<Comic>>> Function(int page);
|
||||||
|
|
||||||
|
/// build comic list with next param, [Res.subData] should be next page param or null if there is no next page.
|
||||||
|
typedef ComicListBuilderWithNext = Future<Res<List<Comic>>> Function(
|
||||||
|
String? next);
|
||||||
|
|
||||||
|
typedef LoginFunction = Future<Res<bool>> Function(String, String);
|
||||||
|
|
||||||
|
typedef LoadComicFunc = Future<Res<ComicDetails>> Function(String id);
|
||||||
|
|
||||||
|
typedef LoadComicPagesFunc = Future<Res<List<String>>> Function(
|
||||||
|
String id, String? ep);
|
||||||
|
|
||||||
|
typedef CommentsLoader = Future<Res<List<Comment>>> Function(
|
||||||
|
String id, String? subId, int page, String? replyTo);
|
||||||
|
|
||||||
|
typedef SendCommentFunc = Future<Res<bool>> Function(
|
||||||
|
String id, String? subId, String content, String? replyTo);
|
||||||
|
|
||||||
|
typedef GetImageLoadingConfigFunc = Future<Map<String, dynamic>> Function(
|
||||||
|
String imageKey, String comicId, String epId)?;
|
||||||
|
typedef GetThumbnailLoadingConfigFunc = Map<String, dynamic> Function(
|
||||||
|
String imageKey)?;
|
||||||
|
|
||||||
|
typedef ComicThumbnailLoader = Future<Res<List<String>>> Function(
|
||||||
|
String comicId, String? next);
|
||||||
|
|
||||||
|
typedef LikeOrUnlikeComicFunc = Future<Res<bool>> Function(
|
||||||
|
String comicId, bool isLiking);
|
||||||
|
|
||||||
|
/// [isLiking] is true if the user is liking the comment, false if unliking.
|
||||||
|
/// return the new likes count or null.
|
||||||
|
typedef LikeCommentFunc = Future<Res<int?>> Function(
|
||||||
|
String comicId, String? subId, String commentId, bool isLiking);
|
||||||
|
|
||||||
|
/// [isUp] is true if the user is upvoting the comment, false if downvoting.
|
||||||
|
/// return the new vote count or null.
|
||||||
|
typedef VoteCommentFunc = Future<Res<int?>> Function(
|
||||||
|
String comicId, String? subId, String commentId, bool isUp, bool isCancel);
|
||||||
|
|
||||||
|
typedef HandleClickTagEvent = Map<String, String> Function(
|
||||||
|
String namespace, String tag);
|
||||||
|
|
||||||
|
/// [rating] is the rating value, 0-10. 1 represents 0.5 star.
|
||||||
|
typedef StarRatingFunc = Future<Res<bool>> Function(String comicId, int rating);
|
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
|
import 'package:flutter/foundation.dart' show protected;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:html/parser.dart' as html;
|
import 'package:html/parser.dart' as html;
|
||||||
import 'package:html/dom.dart' as dom;
|
import 'package:html/dom.dart' as dom;
|
||||||
@@ -24,6 +25,7 @@ import 'package:venera/components/js_ui.dart';
|
|||||||
import 'package:venera/foundation/app.dart';
|
import 'package:venera/foundation/app.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/init.dart';
|
||||||
|
|
||||||
import 'comic_source/comic_source.dart';
|
import 'comic_source/comic_source.dart';
|
||||||
import 'consts.dart';
|
import 'consts.dart';
|
||||||
@@ -40,7 +42,7 @@ class JavaScriptRuntimeException implements Exception {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class JsEngine with _JSEngineApi, JsUiApi {
|
class JsEngine with _JSEngineApi, JsUiApi, Init {
|
||||||
factory JsEngine() => _cache ?? (_cache = JsEngine._create());
|
factory JsEngine() => _cache ?? (_cache = JsEngine._create());
|
||||||
|
|
||||||
static JsEngine? _cache;
|
static JsEngine? _cache;
|
||||||
@@ -64,7 +66,9 @@ class JsEngine with _JSEngineApi, JsUiApi {
|
|||||||
responseType: ResponseType.plain, validateStatus: (status) => true));
|
responseType: ResponseType.plain, validateStatus: (status) => true));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
@override
|
||||||
|
@protected
|
||||||
|
Future<void> doInit() async {
|
||||||
if (!_closed) {
|
if (!_closed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -265,6 +265,7 @@ class LocalManager with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
_checkPathValidation();
|
_checkPathValidation();
|
||||||
_checkNoMedia();
|
_checkNoMedia();
|
||||||
|
await ComicSourceManager().ensureInit();
|
||||||
restoreDownloadingTasks();
|
restoreDownloadingTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,13 +30,15 @@ extension _FutureInit<T> on Future<T> {
|
|||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
await App.init().wait();
|
await App.init().wait();
|
||||||
SingleInstanceCookieJar("${App.dataPath}/cookie.db");
|
await SingleInstanceCookieJar.createInstance();
|
||||||
var futures = [
|
var futures = [
|
||||||
Rhttp.init(),
|
Rhttp.init(),
|
||||||
|
App.initComponents(),
|
||||||
SAFTaskWorker().init().wait(),
|
SAFTaskWorker().init().wait(),
|
||||||
AppTranslation.init().wait(),
|
AppTranslation.init().wait(),
|
||||||
TagsTranslation.readData().wait(),
|
TagsTranslation.readData().wait(),
|
||||||
JsEngine().init().then((_) => ComicSource.init()).wait(),
|
JsEngine().init().wait(),
|
||||||
|
ComicSourceManager().init().wait(),
|
||||||
];
|
];
|
||||||
await Future.wait(futures);
|
await Future.wait(futures);
|
||||||
CacheManager().setLimitSize(appdata.settings['cacheSize']);
|
CacheManager().setLimitSize(appdata.settings['cacheSize']);
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sqlite3/sqlite3.dart';
|
import 'package:sqlite3/sqlite3.dart';
|
||||||
import 'package:venera/foundation/log.dart';
|
import 'package:venera/foundation/log.dart';
|
||||||
import 'package:venera/utils/ext.dart';
|
import 'package:venera/utils/ext.dart';
|
||||||
@@ -200,6 +201,11 @@ class SingleInstanceCookieJar extends CookieJarSql {
|
|||||||
SingleInstanceCookieJar._create(super.path);
|
SingleInstanceCookieJar._create(super.path);
|
||||||
|
|
||||||
static SingleInstanceCookieJar? instance;
|
static SingleInstanceCookieJar? instance;
|
||||||
|
|
||||||
|
static Future<void> createInstance() async {
|
||||||
|
var dataPath = (await getApplicationSupportDirectory()).path;
|
||||||
|
instance = SingleInstanceCookieJar("$dataPath/cookie.db");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CookieManagerSql extends Interceptor {
|
class CookieManagerSql extends Interceptor {
|
||||||
|
@@ -40,10 +40,11 @@ class ComicSourcePage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldUpdate.isNotEmpty) {
|
if (shouldUpdate.isNotEmpty) {
|
||||||
|
var updates = <String, String>{};
|
||||||
for (var key in shouldUpdate) {
|
for (var key in shouldUpdate) {
|
||||||
ComicSource.availableUpdates[key] = versions[key]!;
|
updates[key] = versions[key]!;
|
||||||
}
|
}
|
||||||
ComicSource.notifyListeners();
|
ComicSourceManager().updateAvailableUpdates(updates);
|
||||||
}
|
}
|
||||||
return shouldUpdate.length;
|
return shouldUpdate.length;
|
||||||
}
|
}
|
||||||
@@ -73,13 +74,13 @@ class _BodyState extends State<_Body> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
ComicSource.addListener(updateUI);
|
ComicSourceManager().addListener(updateUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
ComicSource.removeListener(updateUI);
|
ComicSourceManager().removeListener(updateUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -115,7 +116,7 @@ class _BodyState extends State<_Body> {
|
|||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
var file = File(source.filePath);
|
var file = File(source.filePath);
|
||||||
file.delete();
|
file.delete();
|
||||||
ComicSource.remove(source.key);
|
ComicSourceManager().remove(source.key);
|
||||||
_validatePages();
|
_validatePages();
|
||||||
App.forceRebuild();
|
App.forceRebuild();
|
||||||
},
|
},
|
||||||
@@ -136,7 +137,7 @@ class _BodyState extends State<_Body> {
|
|||||||
child: const Text("cancel")),
|
child: const Text("cancel")),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await ComicSource.reload();
|
await ComicSourceManager().reload();
|
||||||
App.forceRebuild();
|
App.forceRebuild();
|
||||||
},
|
},
|
||||||
child: const Text("continue")),
|
child: const Text("continue")),
|
||||||
@@ -150,7 +151,7 @@ class _BodyState extends State<_Body> {
|
|||||||
}
|
}
|
||||||
context.to(
|
context.to(
|
||||||
() => _EditFilePage(source.filePath, () async {
|
() => _EditFilePage(source.filePath, () async {
|
||||||
await ComicSource.reload();
|
await ComicSourceManager().reload();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -162,7 +163,7 @@ class _BodyState extends State<_Body> {
|
|||||||
App.rootContext.showMessage(message: "Invalid url config");
|
App.rootContext.showMessage(message: "Invalid url config");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ComicSource.remove(source.key);
|
ComicSourceManager().remove(source.key);
|
||||||
bool cancel = false;
|
bool cancel = false;
|
||||||
LoadingDialogController? controller;
|
LoadingDialogController? controller;
|
||||||
if (showLoading) {
|
if (showLoading) {
|
||||||
@@ -179,14 +180,14 @@ class _BodyState extends State<_Body> {
|
|||||||
controller?.close();
|
controller?.close();
|
||||||
await ComicSourceParser().parse(res.data!, source.filePath);
|
await ComicSourceParser().parse(res.data!, source.filePath);
|
||||||
await File(source.filePath).writeAsString(res.data!);
|
await File(source.filePath).writeAsString(res.data!);
|
||||||
if (ComicSource.availableUpdates.containsKey(source.key)) {
|
if (ComicSourceManager().availableUpdates.containsKey(source.key)) {
|
||||||
ComicSource.availableUpdates.remove(source.key);
|
ComicSourceManager().availableUpdates.remove(source.key);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (cancel) return;
|
if (cancel) return;
|
||||||
App.rootContext.showMessage(message: e.toString());
|
App.rootContext.showMessage(message: e.toString());
|
||||||
}
|
}
|
||||||
await ComicSource.reload();
|
await ComicSourceManager().reload();
|
||||||
App.forceRebuild();
|
App.forceRebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +305,7 @@ class _BodyState extends State<_Body> {
|
|||||||
|
|
||||||
Future<void> addSource(String js, String fileName) async {
|
Future<void> addSource(String js, String fileName) async {
|
||||||
var comicSource = await ComicSourceParser().createAndParse(js, fileName);
|
var comicSource = await ComicSourceParser().createAndParse(js, fileName);
|
||||||
ComicSource.add(comicSource);
|
ComicSourceManager().add(comicSource);
|
||||||
_addAllPagesWithComicSource(comicSource);
|
_addAllPagesWithComicSource(comicSource);
|
||||||
appdata.saveData();
|
appdata.saveData();
|
||||||
App.forceRebuild();
|
App.forceRebuild();
|
||||||
@@ -563,7 +564,7 @@ class _CheckUpdatesButtonState extends State<_CheckUpdatesButton> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showUpdateDialog() async {
|
void showUpdateDialog() async {
|
||||||
var text = ComicSource.availableUpdates.entries.map((e) {
|
var text = ComicSourceManager().availableUpdates.entries.map((e) {
|
||||||
return "${ComicSource.find(e.key)!.name}: ${e.value}";
|
return "${ComicSource.find(e.key)!.name}: ${e.value}";
|
||||||
}).join("\n");
|
}).join("\n");
|
||||||
bool doUpdate = false;
|
bool doUpdate = false;
|
||||||
@@ -592,9 +593,9 @@ class _CheckUpdatesButtonState extends State<_CheckUpdatesButton> {
|
|||||||
withProgress: true,
|
withProgress: true,
|
||||||
);
|
);
|
||||||
int current = 0;
|
int current = 0;
|
||||||
int total = ComicSource.availableUpdates.length;
|
int total = ComicSourceManager().availableUpdates.length;
|
||||||
try {
|
try {
|
||||||
var shouldUpdate = ComicSource.availableUpdates.keys.toList();
|
var shouldUpdate = ComicSourceManager().availableUpdates.keys.toList();
|
||||||
for (var key in shouldUpdate) {
|
for (var key in shouldUpdate) {
|
||||||
var source = ComicSource.find(key)!;
|
var source = ComicSource.find(key)!;
|
||||||
await _BodyState.update(source, false);
|
await _BodyState.update(source, false);
|
||||||
@@ -692,7 +693,7 @@ class _SliverComicSourceState extends State<_SliverComicSource> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var newVersion = ComicSource.availableUpdates[source.key];
|
var newVersion = ComicSourceManager().availableUpdates[source.key];
|
||||||
bool hasUpdate =
|
bool hasUpdate =
|
||||||
newVersion != null && compareSemVer(newVersion, source.version);
|
newVersion != null && compareSemVer(newVersion, source.version);
|
||||||
|
|
||||||
@@ -960,7 +961,7 @@ class _SliverComicSourceState extends State<_SliverComicSource> {
|
|||||||
source.data["account"] = null;
|
source.data["account"] = null;
|
||||||
source.account?.logout();
|
source.account?.logout();
|
||||||
source.saveData();
|
source.saveData();
|
||||||
ComicSource.notifyListeners();
|
ComicSourceManager().notifyStateChange();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
trailing: const Icon(Icons.logout),
|
trailing: const Icon(Icons.logout),
|
||||||
|
@@ -630,19 +630,19 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
comicSources = ComicSource.all().map((e) => e.name).toList();
|
comicSources = ComicSource.all().map((e) => e.name).toList();
|
||||||
ComicSource.addListener(onComicSourceChange);
|
ComicSourceManager().addListener(onComicSourceChange);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
ComicSource.removeListener(onComicSourceChange);
|
ComicSourceManager().removeListener(onComicSourceChange);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
int get _availableUpdates {
|
int get _availableUpdates {
|
||||||
int c = 0;
|
int c = 0;
|
||||||
ComicSource.availableUpdates.forEach((key, version) {
|
ComicSourceManager().availableUpdates.forEach((key, version) {
|
||||||
var source = ComicSource.find(key);
|
var source = ComicSource.find(key);
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
if (compareSemVer(version, source.version)) {
|
if (compareSemVer(version, source.version)) {
|
||||||
|
@@ -103,7 +103,7 @@ Future<void> importAppData(File file, [bool checkVersion = false]) async {
|
|||||||
await file.copy(targetFile);
|
await file.copy(targetFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await ComicSource.reload();
|
await ComicSourceManager().reload();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
cacheDir.deleteIgnoreError(recursive: true);
|
cacheDir.deleteIgnoreError(recursive: true);
|
||||||
|
@@ -19,7 +19,7 @@ class DataSync with ChangeNotifier {
|
|||||||
downloadData();
|
downloadData();
|
||||||
}
|
}
|
||||||
LocalFavoritesManager().addListener(onDataChanged);
|
LocalFavoritesManager().addListener(onDataChanged);
|
||||||
ComicSource.addListener(onDataChanged);
|
ComicSourceManager().addListener(onDataChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDataChanged() {
|
void onDataChanged() {
|
||||||
|
40
lib/utils/init.dart
Normal file
40
lib/utils/init.dart
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
/// A mixin class that provides a way to ensure the class is initialized.
|
||||||
|
abstract mixin class Init {
|
||||||
|
bool _isInit = false;
|
||||||
|
|
||||||
|
final _initCompleter = <Completer<void>>[];
|
||||||
|
|
||||||
|
/// Ensure the class is initialized.
|
||||||
|
Future<void> ensureInit() async {
|
||||||
|
if (_isInit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var completer = Completer<void>();
|
||||||
|
_initCompleter.add(completer);
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _markInit() async {
|
||||||
|
_isInit = true;
|
||||||
|
for (var completer in _initCompleter) {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
_initCompleter.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Future<void> doInit();
|
||||||
|
|
||||||
|
/// Initialize the class.
|
||||||
|
Future<void> init() async {
|
||||||
|
if (_isInit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await doInit();
|
||||||
|
await _markInit();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user