diff --git a/assets/translation.json b/assets/translation.json index 27b2a8a..eb2d5c4 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -315,7 +315,10 @@ "Tags": "标签", "Authors": "作者", "Comics": "漫画", - "Imported @a comics": "已导入 @a 本漫画" + "Imported @a comics": "已导入 @a 本漫画", + "New Version": "新版本", + "@c updates": "@c 项更新", + "No updates": "无更新" }, "zh_TW": { "Home": "首頁", @@ -633,6 +636,9 @@ "Tags": "標籤", "Authors": "作者", "Comics": "漫畫", - "Imported @a comics": "已匯入 @a 部漫畫" + "Imported @a comics": "已匯入 @a 部漫畫", + "New Version": "新版本", + "@c updates": "@c 項更新", + "No updates": "無更新" } } \ No newline at end of file diff --git a/lib/foundation/comic_source/comic_source.dart b/lib/foundation/comic_source/comic_source.dart index ec5386e..0a175b8 100644 --- a/lib/foundation/comic_source/comic_source.dart +++ b/lib/foundation/comic_source/comic_source.dart @@ -136,6 +136,8 @@ class ComicSource { notifyListeners(); } + static final availableUpdates = {}; + static bool get isEmpty => _sources.isEmpty; /// Name of this source. diff --git a/lib/foundation/comic_source/parser.dart b/lib/foundation/comic_source/parser.dart index ea1b3b1..c104d4a 100644 --- a/lib/foundation/comic_source/parser.dart +++ b/lib/foundation/comic_source/parser.dart @@ -1,5 +1,6 @@ part of 'comic_source.dart'; +/// return true if ver1 > ver2 bool compareSemVer(String ver1, String ver2) { ver1 = ver1.replaceFirst("-", "."); ver2 = ver2.replaceFirst("-", "."); diff --git a/lib/pages/comic_source_page.dart b/lib/pages/comic_source_page.dart index 9e7c3f6..8c471a2 100644 --- a/lib/pages/comic_source_page.dart +++ b/lib/pages/comic_source_page.dart @@ -14,17 +14,15 @@ import 'package:venera/utils/translations.dart'; class ComicSourcePage extends StatefulWidget { const ComicSourcePage({super.key}); - static Future checkComicSourceUpdate([bool implicit = false]) async { + static Future checkComicSourceUpdate() async { if (ComicSource.all().isEmpty) { - return; + return 0; } - var controller = implicit ? null : showLoadingDialog(App.rootContext); var dio = AppDio(); var res = await dio.get( "https://raw.githubusercontent.com/venera-app/venera-configs/master/index.json"); if (res.statusCode != 200) { - App.rootContext.showMessage(message: "Network error".tl); - return; + return -1; } var list = jsonDecode(res.data!) as List; var versions = {}; @@ -34,34 +32,17 @@ class ComicSourcePage extends StatefulWidget { var shouldUpdate = []; for (var source in ComicSource.all()) { if (versions.containsKey(source.key) && - versions[source.key] != source.version) { + compareSemVer(versions[source.key]!, source.version)) { shouldUpdate.add(source.key); } } - controller?.close(); - if (shouldUpdate.isEmpty) { - if (!implicit) { - App.rootContext.showMessage(message: "No Update Available".tl); + if (shouldUpdate.isNotEmpty) { + for (var key in shouldUpdate) { + ComicSource.availableUpdates[key] = versions[key]!; } - return; + ComicSource.notifyListeners(); } - var msg = ""; - for (var key in shouldUpdate) { - msg += "${ComicSource.find(key)?.name}: v${versions[key]}\n"; - } - msg = msg.trim(); - await showConfirmDialog( - context: App.rootContext, - title: "Updates Available".tl, - content: msg, - confirmText: "Update", - onConfirm: () async { - for (var key in shouldUpdate) { - var source = ComicSource.find(key); - await _BodyState.update(source!); - } - }, - ); + return shouldUpdate.length; } @override @@ -90,6 +71,22 @@ class _Body extends StatefulWidget { class _BodyState extends State<_Body> { var url = ""; + void updateUI() { + setState(() {}); + } + + @override + void initState() { + super.initState(); + ComicSource.addListener(updateUI); + } + + @override + void dispose() { + super.dispose(); + ComicSource.removeListener(updateUI); + } + @override Widget build(BuildContext context) { return SmoothCustomScrollView( @@ -102,12 +99,36 @@ class _BodyState extends State<_Body> { } Widget buildSource(BuildContext context, ComicSource source) { + var newVersion = ComicSource.availableUpdates[source.key]; + bool hasUpdate = + newVersion != null && compareSemVer(newVersion, source.version); return SliverToBoxAdapter( child: Column( children: [ const Divider(), ListTile( - title: Text(source.name), + title: Row( + children: [ + Text(source.name), + const SizedBox(width: 6), + if (hasUpdate) + Tooltip( + message: newVersion, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: context.colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + "New Version".tl, + style: const TextStyle(fontSize: 13), + ), + ), + ) + ], + ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -299,6 +320,9 @@ class _BodyState extends State<_Body> { controller.close(); await ComicSourceParser().parse(res.data!, source.filePath); await File(source.filePath).writeAsString(res.data!); + if (ComicSource.availableUpdates.containsKey(source.key)) { + ComicSource.availableUpdates.remove(source.key); + } } catch (e) { if (cancel) return; App.rootContext.showMessage(message: e.toString()); @@ -368,10 +392,7 @@ class _BodyState extends State<_Body> { ), ListTile( title: Text("Check updates".tl), - trailing: buildButton( - onPressed: () => ComicSourcePage.checkComicSourceUpdate(false), - child: Text("Check".tl), - ), + trailing: _CheckUpdatesButton(), ), const SizedBox(height: 8), ], @@ -619,3 +640,40 @@ class __EditFilePageState extends State<_EditFilePage> { ); } } + +class _CheckUpdatesButton extends StatefulWidget { + const _CheckUpdatesButton(); + + @override + State<_CheckUpdatesButton> createState() => _CheckUpdatesButtonState(); +} + +class _CheckUpdatesButtonState extends State<_CheckUpdatesButton> { + bool isLoading = false; + + void check() async { + setState(() { + isLoading = true; + }); + var count = await ComicSourcePage.checkComicSourceUpdate(); + if (count == -1) { + context.showMessage(message: "Network error".tl); + } else if (count == 0) { + context.showMessage(message: "No updates".tl); + } else { + context.showMessage(message: "@c updates".tlParams({"c": count})); + } + setState(() { + isLoading = false; + }); + } + + @override + Widget build(BuildContext context) { + return Button.normal( + onPressed: check, + isLoading: isLoading, + child: Text("Check".tl), + ).fixHeight(32); + } +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 595f3fe..e9579c3 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -696,6 +696,30 @@ class _ComicSourceWidgetState extends State<_ComicSourceWidget> { }).toList(), ).paddingHorizontal(16).paddingBottom(16), ), + if (ComicSource.availableUpdates.isNotEmpty) + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + border: Border.all( + color: context.colorScheme.outlineVariant, + width: 0.6, + ), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.update, color: context.colorScheme.primary, size: 20,), + const SizedBox(width: 8), + Text("@c updates".tlParams({ + 'c': ComicSource.availableUpdates.length, + }), style: ts.withColor(context.colorScheme.primary),), + ], + ), + ).toAlign(Alignment.centerLeft).paddingHorizontal(16).paddingBottom(8), ], ), ), diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index fdd4e7b..aa95c35 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -37,9 +37,6 @@ class _MainPageState extends State { } void checkUpdates() async { - if (!appdata.settings['checkUpdateOnStart']) { - return; - } var lastCheck = appdata.implicitData['lastCheckUpdate'] ?? 0; var now = DateTime.now().millisecondsSinceEpoch; if (now - lastCheck < 24 * 60 * 60 * 1000) { @@ -47,9 +44,11 @@ class _MainPageState extends State { } appdata.implicitData['lastCheckUpdate'] = now; appdata.writeImplicitData(); - await Future.delayed(const Duration(milliseconds: 300)); - await checkUpdateUi(false); - await ComicSourcePage.checkComicSourceUpdate(true); + ComicSourcePage.checkComicSourceUpdate(); + if (appdata.settings['checkUpdateOnStart']) { + await Future.delayed(const Duration(milliseconds: 300)); + await checkUpdateUi(false); + } } @override diff --git a/pubspec.lock b/pubspec.lock index 478f5c9..8270f48 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -307,8 +307,8 @@ packages: dependency: "direct main" description: path: "." - ref: "841ef5f77e5fdfd79e3eb2fa07ece4d46787285b" - resolved-ref: "841ef5f77e5fdfd79e3eb2fa07ece4d46787285b" + ref: b33344797f1d2469339e0e1b75f5f954f1da224c + resolved-ref: b33344797f1d2469339e0e1b75f5f954f1da224c url: "https://github.com/wgh136/flutter_7zip" source: git version: "0.0.1"