mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-05-23 07:32:18 +00:00
commit
25c1d72c8b
5 changed files with 145 additions and 167 deletions
|
|
@ -11,6 +11,7 @@ import 'package:mangayomi/models/chapter.dart';
|
|||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/download_file_screen.dart';
|
||||
import 'package:mangayomi/modules/more/providers/downloaded_only_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
|
|
@ -118,7 +119,6 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
|||
void _initializeProviders() {
|
||||
Future.microtask(() {
|
||||
if (mounted) {
|
||||
ref.read(checkForUpdateProvider(context: context));
|
||||
for (var type in ItemType.values) {
|
||||
ref.read(
|
||||
fetchItemSourcesListProvider(
|
||||
|
|
@ -169,6 +169,17 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
|||
bool isLibSwitch = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ref.listen<AsyncValue<UpdateInfo?>>(checkForUpdateProvider, (_, next) {
|
||||
next.whenData((updateInfo) {
|
||||
if (updateInfo != null && context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => DownloadFileScreen(updateAvailable: updateInfo),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ref.listen<Locale>(l10nLocaleStateProvider, (previous, next) {
|
||||
_clearCache();
|
||||
setState(() {});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
|
@ -8,6 +9,7 @@ import 'package:mangayomi/eval/model/m_bridge.dart';
|
|||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/check_for_update.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/download_file_screen.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/get_package_info.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/logs_state.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
|
|
@ -73,13 +75,33 @@ class AboutScreen extends ConsumerWidget {
|
|||
},
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
ref.read(
|
||||
checkForUpdateProvider(
|
||||
context: context,
|
||||
manualUpdate: true,
|
||||
),
|
||||
);
|
||||
onTap: () async {
|
||||
BotToast.showText(text: l10n.searching_for_updates);
|
||||
try {
|
||||
final updateInfo = await performManualUpdateCheck();
|
||||
if (updateInfo != null) {
|
||||
BotToast.showText(
|
||||
text: l10n.new_update_available,
|
||||
);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => DownloadFileScreen(
|
||||
updateAvailable: updateInfo,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
BotToast.showText(
|
||||
text: l10n.no_new_updates_available,
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
BotToast.showText(
|
||||
text: l10n.no_new_updates_available,
|
||||
);
|
||||
}
|
||||
},
|
||||
title: Text(l10n.check_for_update),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/download_file_screen.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/fetch_sources_list.dart';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'package:mangayomi/utils/extensions/string_extensions.dart';
|
||||
|
|
@ -14,43 +10,29 @@ import 'package:package_info_plus/package_info_plus.dart';
|
|||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'check_for_update.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<void> checkForUpdate(
|
||||
Ref ref, {
|
||||
BuildContext? context,
|
||||
bool? manualUpdate,
|
||||
}) async {
|
||||
manualUpdate = manualUpdate ?? false;
|
||||
final checkForUpdates = ref.read(checkForAppUpdatesProvider);
|
||||
if (!checkForUpdates && !manualUpdate) return;
|
||||
final l10n = l10nLocalizations(context!)!;
|
||||
/// Convenience alias: (version, body, htmlUrl, assets).
|
||||
typedef UpdateInfo = (String, String, String, List<dynamic>);
|
||||
|
||||
if (manualUpdate) {
|
||||
BotToast.showText(text: l10n.searching_for_updates);
|
||||
}
|
||||
/// Automatic update-check provider.
|
||||
///
|
||||
/// Respects the user's [checkForAppUpdatesProvider] preference. Returns
|
||||
/// [UpdateInfo] when a newer version exists, `null` otherwise.
|
||||
@riverpod
|
||||
Future<UpdateInfo?> checkForUpdate(Ref ref) async {
|
||||
if (!ref.read(checkForAppUpdatesProvider)) return null;
|
||||
return _getUpdateIfAvailable();
|
||||
}
|
||||
|
||||
/// Compares the running version against the latest release.
|
||||
/// Returns [UpdateInfo] when an update is available, or `null` when already
|
||||
/// up-to-date. Throws if the network request fails.
|
||||
Future<UpdateInfo?> _getUpdateIfAvailable() async {
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
if (kDebugMode) {
|
||||
log(info.data.toString());
|
||||
}
|
||||
final updateAvailable = await _checkUpdate();
|
||||
if (compareVersions(info.version, updateAvailable.$1) < 0) {
|
||||
if (manualUpdate) {
|
||||
BotToast.showText(text: l10n.new_update_available);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DownloadFileScreen(updateAvailable: updateAvailable);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if (compareVersions(info.version, updateAvailable.$1) == 0) {
|
||||
if (manualUpdate) {
|
||||
BotToast.showText(text: l10n.no_new_updates_available);
|
||||
}
|
||||
}
|
||||
final latest = await _fetchLatestRelease();
|
||||
return compareVersions(info.version, latest.$1) < 0 ? latest : null;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
|
|
@ -58,27 +40,23 @@ bool checkForAppUpdates(Ref ref) {
|
|||
return isar.settings.getSync(227)?.checkForAppUpdates ?? true;
|
||||
}
|
||||
|
||||
Future<(String, String, String, List<dynamic>)> _checkUpdate() async {
|
||||
/// Performs an update check unconditionally, ignoring the auto-update setting.
|
||||
Future<UpdateInfo?> performManualUpdateCheck() => _getUpdateIfAvailable();
|
||||
|
||||
Future<UpdateInfo> _fetchLatestRelease() async {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
try {
|
||||
final res = await http.get(
|
||||
Uri.parse(
|
||||
"https://api.github.com/repos/kodjodevf/Mangayomi/releases?page=1&per_page=10",
|
||||
),
|
||||
);
|
||||
List resListJson = jsonDecode(res.body) as List;
|
||||
return (
|
||||
resListJson.first["name"]
|
||||
.toString()
|
||||
.substringAfter('v')
|
||||
.substringBefore('-'),
|
||||
resListJson.first["body"].toString(),
|
||||
resListJson.first["html_url"].toString(),
|
||||
(resListJson.first["assets"] as List)
|
||||
.map((asset) => asset["browser_download_url"])
|
||||
.toList(),
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
final res = await http.get(
|
||||
Uri.parse(
|
||||
'https://api.github.com/repos/kodjodevf/Mangayomi/releases/latest',
|
||||
),
|
||||
);
|
||||
final release = jsonDecode(res.body) as Map<String, dynamic>;
|
||||
return (
|
||||
release['name'].toString().substringAfter('v').substringBefore('-'),
|
||||
release['body'].toString(),
|
||||
release['html_url'].toString(),
|
||||
(release['assets'] as List)
|
||||
.map((asset) => asset['browser_download_url'])
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,87 +8,58 @@ part of 'check_for_update.dart';
|
|||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
/// Automatic update-check provider.
|
||||
///
|
||||
/// Respects the user's [checkForAppUpdatesProvider] preference. Returns
|
||||
/// [UpdateInfo] when a newer version exists, `null` otherwise.
|
||||
|
||||
@ProviderFor(checkForUpdate)
|
||||
final checkForUpdateProvider = CheckForUpdateFamily._();
|
||||
final checkForUpdateProvider = CheckForUpdateProvider._();
|
||||
|
||||
/// Automatic update-check provider.
|
||||
///
|
||||
/// Respects the user's [checkForAppUpdatesProvider] preference. Returns
|
||||
/// [UpdateInfo] when a newer version exists, `null` otherwise.
|
||||
|
||||
final class CheckForUpdateProvider
|
||||
extends $FunctionalProvider<AsyncValue<void>, void, FutureOr<void>>
|
||||
with $FutureModifier<void>, $FutureProvider<void> {
|
||||
CheckForUpdateProvider._({
|
||||
required CheckForUpdateFamily super.from,
|
||||
required ({BuildContext? context, bool? manualUpdate}) super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'checkForUpdateProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<UpdateInfo?>,
|
||||
UpdateInfo?,
|
||||
FutureOr<UpdateInfo?>
|
||||
>
|
||||
with $FutureModifier<UpdateInfo?>, $FutureProvider<UpdateInfo?> {
|
||||
/// Automatic update-check provider.
|
||||
///
|
||||
/// Respects the user's [checkForAppUpdatesProvider] preference. Returns
|
||||
/// [UpdateInfo] when a newer version exists, `null` otherwise.
|
||||
CheckForUpdateProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'checkForUpdateProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$checkForUpdateHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'checkForUpdateProvider'
|
||||
''
|
||||
'$argument';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<void> $createElement($ProviderPointer pointer) =>
|
||||
$FutureProviderElement(pointer);
|
||||
$FutureProviderElement<UpdateInfo?> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<void> create(Ref ref) {
|
||||
final argument =
|
||||
this.argument as ({BuildContext? context, bool? manualUpdate});
|
||||
return checkForUpdate(
|
||||
ref,
|
||||
context: argument.context,
|
||||
manualUpdate: argument.manualUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is CheckForUpdateProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
FutureOr<UpdateInfo?> create(Ref ref) {
|
||||
return checkForUpdate(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$checkForUpdateHash() => r'8064f08768f4d98997201a2f07ba160c721a6a3f';
|
||||
|
||||
final class CheckForUpdateFamily extends $Family
|
||||
with
|
||||
$FunctionalFamilyOverride<
|
||||
FutureOr<void>,
|
||||
({BuildContext? context, bool? manualUpdate})
|
||||
> {
|
||||
CheckForUpdateFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'checkForUpdateProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
CheckForUpdateProvider call({BuildContext? context, bool? manualUpdate}) =>
|
||||
CheckForUpdateProvider._(
|
||||
argument: (context: context, manualUpdate: manualUpdate),
|
||||
from: this,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => r'checkForUpdateProvider';
|
||||
}
|
||||
String _$checkForUpdateHash() => r'7134bb3c6ac01bc16fecf975195a5231d57c6148';
|
||||
|
||||
@ProviderFor(checkForAppUpdates)
|
||||
final checkForAppUpdatesProvider = CheckForAppUpdatesProvider._();
|
||||
|
|
|
|||
|
|
@ -98,24 +98,26 @@ class _UpdatesScreenState extends BaseLibraryTabScreenState<UpdatesScreen> {
|
|||
}
|
||||
|
||||
Future<void> _updateLibrary() async {
|
||||
setState(() => _isLoading = true);
|
||||
final itemType = getCurrentItemType();
|
||||
final mangaList = isar.mangas
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.favoriteEqualTo(true)
|
||||
.and()
|
||||
.itemTypeEqualTo(itemType)
|
||||
.and()
|
||||
.isLocalArchiveEqualTo(false)
|
||||
.findAllSync();
|
||||
await updateLibrary(
|
||||
ref: ref,
|
||||
context: context,
|
||||
mangaList: mangaList,
|
||||
itemType: itemType,
|
||||
);
|
||||
setState(() => _isLoading = false);
|
||||
try {
|
||||
setState(() => _isLoading = true);
|
||||
final itemType = getCurrentItemType();
|
||||
final mangaList = await isar.mangas
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.favoriteEqualTo(true)
|
||||
.itemTypeEqualTo(itemType)
|
||||
.isLocalArchiveEqualTo(false)
|
||||
.findAll();
|
||||
if (!mounted) return;
|
||||
await updateLibrary(
|
||||
ref: ref,
|
||||
context: context,
|
||||
mangaList: mangaList,
|
||||
itemType: itemType,
|
||||
);
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _clearUpdates() async {
|
||||
|
|
@ -124,6 +126,7 @@ class _UpdatesScreenState extends BaseLibraryTabScreenState<UpdatesScreen> {
|
|||
.idIsNotNull()
|
||||
.chapter((q) => q.manga((q) => q.itemTypeEqualTo(getCurrentItemType())))
|
||||
.findAll();
|
||||
if (updates.isEmpty) return;
|
||||
final idsToDelete = <Id>[];
|
||||
isar.writeTxnSync(() {
|
||||
for (var update in updates) {
|
||||
|
|
@ -133,7 +136,7 @@ class _UpdatesScreenState extends BaseLibraryTabScreenState<UpdatesScreen> {
|
|||
.addChangedPart(ActionType.removeUpdate, update.id, "{}", false);
|
||||
}
|
||||
});
|
||||
await isar.writeTxn(() => isar.updates.deleteAll(idsToDelete));
|
||||
await isar.writeTxn(() async => await isar.updates.deleteAll(idsToDelete));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -273,25 +276,18 @@ Widget _updateNumbers(WidgetRef ref, ItemType itemType) {
|
|||
stream: isar.updates
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.chapter((q) => q.manga((q) => q.itemTypeEqualTo(itemType)))
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final entries = snapshot.data!.toList();
|
||||
return entries.isEmpty
|
||||
? SizedBox.shrink()
|
||||
: Badge(
|
||||
backgroundColor: Theme.of(context).focusColor,
|
||||
label: Text(
|
||||
entries.length.toString(),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.bodySmall!.color,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
final count = snapshot.data?.length ?? 0;
|
||||
if (count == 0) return const SizedBox.shrink();
|
||||
return Badge(
|
||||
backgroundColor: Theme.of(context).focusColor,
|
||||
label: Text(
|
||||
count.toString(),
|
||||
style: TextStyle(color: Theme.of(context).textTheme.bodySmall!.color),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue