diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1055afb4..1b5fb290 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ - + + diff --git a/android/app/src/main/kotlin/com/kodjodevf/mangayomi/MainActivity.kt b/android/app/src/main/kotlin/com/kodjodevf/mangayomi/MainActivity.kt index 5db6f132..6f361155 100644 --- a/android/app/src/main/kotlin/com/kodjodevf/mangayomi/MainActivity.kt +++ b/android/app/src/main/kotlin/com/kodjodevf/mangayomi/MainActivity.kt @@ -5,14 +5,14 @@ import libmtorrentserver.Libmtorrentserver import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.StandardMethodCodec -import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.android.FlutterFragmentActivity import androidx.core.content.FileProvider import android.content.Intent import android.os.Build import android.net.Uri import java.io.File -class MainActivity: FlutterActivity() { +class MainActivity: FlutterFragmentActivity() { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index cececc93..e1ad9066 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -41,6 +41,8 @@ fetch + NSFaceIDUsageDescription + Mangayomi needs to authenticate using Face ID. UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7e36b06a..c09882dc 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -565,5 +565,17 @@ "forceLandscapeMode": "Force landscape mode", "forceLandscapeModeSubtitle": "Force the player to use landscape orientation.", "dns_over_https": "DNS-over-HTTPS (DoH)", - "dns_provider": "DNS Provider" + "dns_provider": "DNS Provider", + "tracked": "Tracked", + "auth_unlock_msg": "Authenticate to unlock Mangayomi", + "app_locked": "Mangayomi is locked", + "auth_to_continue": "Authenticate to continue", + "authenticating": "Authenticating...", + "unlock": "Unlock", + "security": "Security", + "auth_to_change_security_setting": "Authenticate to change security settings", + "app_lock": "App lock", + "require_biometric_or_device_credential": "Require biometric or device credential to open the app", + "biometric_or_device_credential_not_available": "Biometric authentication not available on this device", + "app_lock_description": "When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background." } diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 5b01def0..11c6bd9f 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -3466,6 +3466,78 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'DNS Provider'** String get dns_provider; + + /// No description provided for @tracked. + /// + /// In en, this message translates to: + /// **'Tracked'** + String get tracked; + + /// No description provided for @auth_unlock_msg. + /// + /// In en, this message translates to: + /// **'Authenticate to unlock Mangayomi'** + String get auth_unlock_msg; + + /// No description provided for @app_locked. + /// + /// In en, this message translates to: + /// **'Mangayomi is locked'** + String get app_locked; + + /// No description provided for @auth_to_continue. + /// + /// In en, this message translates to: + /// **'Authenticate to continue'** + String get auth_to_continue; + + /// No description provided for @authenticating. + /// + /// In en, this message translates to: + /// **'Authenticating...'** + String get authenticating; + + /// No description provided for @unlock. + /// + /// In en, this message translates to: + /// **'Unlock'** + String get unlock; + + /// No description provided for @security. + /// + /// In en, this message translates to: + /// **'Security'** + String get security; + + /// No description provided for @auth_to_change_security_setting. + /// + /// In en, this message translates to: + /// **'Authenticate to change security settings'** + String get auth_to_change_security_setting; + + /// No description provided for @app_lock. + /// + /// In en, this message translates to: + /// **'App lock'** + String get app_lock; + + /// No description provided for @require_biometric_or_device_credential. + /// + /// In en, this message translates to: + /// **'Require biometric or device credential to open the app'** + String get require_biometric_or_device_credential; + + /// No description provided for @biometric_or_device_credential_not_available. + /// + /// In en, this message translates to: + /// **'Biometric authentication not available on this device'** + String get biometric_or_device_credential_not_available; + + /// No description provided for @app_lock_description. + /// + /// In en, this message translates to: + /// **'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'** + String get app_lock_description; } class _AppLocalizationsDelegate diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart index e4007989..51402152 100644 --- a/lib/l10n/generated/app_localizations_ar.dart +++ b/lib/l10n/generated/app_localizations_ar.dart @@ -1794,4 +1794,44 @@ class AppLocalizationsAr extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_as.dart b/lib/l10n/generated/app_localizations_as.dart index 9cb64c45..070f7af6 100644 --- a/lib/l10n/generated/app_localizations_as.dart +++ b/lib/l10n/generated/app_localizations_as.dart @@ -1800,4 +1800,44 @@ class AppLocalizationsAs extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 5752f9fc..434c8526 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -1815,4 +1815,44 @@ class AppLocalizationsDe extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 8444152f..311f7c4c 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -1794,4 +1794,44 @@ class AppLocalizationsEn extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart index 0121cf44..e8b7b48b 100644 --- a/lib/l10n/generated/app_localizations_es.dart +++ b/lib/l10n/generated/app_localizations_es.dart @@ -1823,6 +1823,46 @@ class AppLocalizationsEs extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } /// The translations for Spanish Castilian, as used in Latin America and the Caribbean (`es_419`). diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart index b26c2389..f468425c 100644 --- a/lib/l10n/generated/app_localizations_fr.dart +++ b/lib/l10n/generated/app_localizations_fr.dart @@ -1823,4 +1823,44 @@ class AppLocalizationsFr extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_hi.dart b/lib/l10n/generated/app_localizations_hi.dart index 5052b17c..bcc0ad1b 100644 --- a/lib/l10n/generated/app_localizations_hi.dart +++ b/lib/l10n/generated/app_localizations_hi.dart @@ -1800,4 +1800,44 @@ class AppLocalizationsHi extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_id.dart b/lib/l10n/generated/app_localizations_id.dart index f1d3bd38..08cc0e0d 100644 --- a/lib/l10n/generated/app_localizations_id.dart +++ b/lib/l10n/generated/app_localizations_id.dart @@ -1806,4 +1806,44 @@ class AppLocalizationsId extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart index 98ce71fd..5ed73dc4 100644 --- a/lib/l10n/generated/app_localizations_it.dart +++ b/lib/l10n/generated/app_localizations_it.dart @@ -1820,4 +1820,44 @@ class AppLocalizationsIt extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_ja.dart b/lib/l10n/generated/app_localizations_ja.dart index 98fa9741..9581cbae 100644 --- a/lib/l10n/generated/app_localizations_ja.dart +++ b/lib/l10n/generated/app_localizations_ja.dart @@ -1771,4 +1771,44 @@ class AppLocalizationsJa extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart index 4e726e72..c591e6fd 100644 --- a/lib/l10n/generated/app_localizations_pt.dart +++ b/lib/l10n/generated/app_localizations_pt.dart @@ -1818,6 +1818,46 @@ class AppLocalizationsPt extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } /// The translations for Portuguese, as used in Brazil (`pt_BR`). diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart index b2001537..f2430d0a 100644 --- a/lib/l10n/generated/app_localizations_ru.dart +++ b/lib/l10n/generated/app_localizations_ru.dart @@ -1823,4 +1823,44 @@ class AppLocalizationsRu extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_th.dart b/lib/l10n/generated/app_localizations_th.dart index c7a5abad..ab205547 100644 --- a/lib/l10n/generated/app_localizations_th.dart +++ b/lib/l10n/generated/app_localizations_th.dart @@ -1794,4 +1794,44 @@ class AppLocalizationsTh extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_tr.dart b/lib/l10n/generated/app_localizations_tr.dart index 5543eecd..8e6bfe76 100644 --- a/lib/l10n/generated/app_localizations_tr.dart +++ b/lib/l10n/generated/app_localizations_tr.dart @@ -1806,4 +1806,44 @@ class AppLocalizationsTr extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart index b1a1e052..579abfa1 100644 --- a/lib/l10n/generated/app_localizations_zh.dart +++ b/lib/l10n/generated/app_localizations_zh.dart @@ -1751,4 +1751,44 @@ class AppLocalizationsZh extends AppLocalizations { @override String get dns_provider => 'DNS Provider'; + + @override + String get tracked => 'Tracked'; + + @override + String get auth_unlock_msg => 'Authenticate to unlock Mangayomi'; + + @override + String get app_locked => 'Mangayomi is locked'; + + @override + String get auth_to_continue => 'Authenticate to continue'; + + @override + String get authenticating => 'Authenticating...'; + + @override + String get unlock => 'Unlock'; + + @override + String get security => 'Security'; + + @override + String get auth_to_change_security_setting => + 'Authenticate to change security settings'; + + @override + String get app_lock => 'App lock'; + + @override + String get require_biometric_or_device_credential => + 'Require biometric or device credential to open the app'; + + @override + String get biometric_or_device_credential_not_available => + 'Biometric authentication not available on this device'; + + @override + String get app_lock_description => + 'When app lock is enabled, you will be asked to authenticate \nevery time you open the app or switch back to it from the background.'; } diff --git a/lib/main.dart b/lib/main.dart index ded6415e..fad00fd2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,6 +41,8 @@ import 'package:mangayomi/utils/log/logger.dart'; import 'package:mangayomi/utils/url_protocol/api.dart'; import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart'; import 'package:mangayomi/modules/library/providers/file_scanner.dart'; +import 'package:mangayomi/modules/more/settings/security/providers/security_state_provider.dart'; +import 'package:mangayomi/modules/more/settings/security/app_lock_screen.dart'; import 'package:media_kit/media_kit.dart'; import 'package:path_provider/path_provider.dart'; import 'package:window_manager/window_manager.dart'; @@ -135,7 +137,7 @@ class MyApp extends ConsumerStatefulWidget { ConsumerState createState() => _MyAppState(); } -class _MyAppState extends ConsumerState { +class _MyAppState extends ConsumerState with WidgetsBindingObserver { late AppLinks _appLinks; StreamSubscription? _linkSubscription; Uri? lastUri; @@ -143,6 +145,7 @@ class _MyAppState extends ConsumerState { @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); initializeDateFormatting(); customDns = ref.read(customDnsStateProvider); _checkTrackerRefresh(); @@ -162,6 +165,22 @@ class _MyAppState extends ConsumerState { }); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.paused || + state == AppLifecycleState.hidden) { + if (Platform.isLinux) { + return; + } + // Lock the app when going to background (if lock is enabled) + final lockEnabled = isar.settings.getSync(227)!.appLockEnabled ?? false; + if (lockEnabled) { + ref.read(appUnlockedStateProvider.notifier).lock(); + } + } + } + @override Widget build(BuildContext context) { final followSystem = ref.watch(followSystemThemeStateProvider); @@ -180,7 +199,17 @@ class _MyAppState extends ConsumerState { locale: locale, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, - builder: BotToastInit(), + builder: Platform.isLinux + ? null + : (context, child) { + child = BotToastInit()(context, child); + final isUnlocked = ref.watch(appUnlockedStateProvider); + final lockEnabled = ref.watch(appLockEnabledStateProvider); + if (lockEnabled && !isUnlocked) { + return const AppLockScreen(); + } + return child; + }, routeInformationParser: router.routeInformationParser, routerDelegate: router.routerDelegate, routeInformationProvider: router.routeInformationProvider, @@ -191,6 +220,7 @@ class _MyAppState extends ConsumerState { @override void dispose() { + WidgetsBinding.instance.removeObserver(this); MExtensionServerPlatform(ref).stopServer(); _linkSubscription?.cancel(); discordRpc?.destroy(); diff --git a/lib/models/settings.dart b/lib/models/settings.dart index b6757b23..dc21f513 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -310,6 +310,20 @@ class Settings { List? localFolders; + bool? appLockEnabled; + + int? libraryFilterMangasCompletedType; + + int? libraryFilterAnimeCompletedType; + + int? libraryFilterNovelCompletedType; + + int? libraryFilterMangasTrackingType; + + int? libraryFilterAnimeTrackingType; + + int? libraryFilterNovelTrackingType; + Settings({ this.id = 227, this.updatedAt = 0, @@ -450,6 +464,13 @@ class Settings { this.downloadedOnlyMode = false, this.algorithmWeights, this.localFolders, + this.appLockEnabled = false, + this.libraryFilterMangasCompletedType = 0, + this.libraryFilterAnimeCompletedType = 0, + this.libraryFilterNovelCompletedType = 0, + this.libraryFilterMangasTrackingType = 0, + this.libraryFilterAnimeTrackingType = 0, + this.libraryFilterNovelTrackingType = 0, }); Settings.fromJson(Map json) { @@ -708,6 +729,13 @@ class Settings { ? AlgorithmWeights.fromJson(json['algorithmWeights']) : null; localFolders = json['localFolders']; + appLockEnabled = json['appLockEnabled']; + libraryFilterMangasCompletedType = json['libraryFilterMangasCompletedType']; + libraryFilterAnimeCompletedType = json['libraryFilterAnimeCompletedType']; + libraryFilterNovelCompletedType = json['libraryFilterNovelCompletedType']; + libraryFilterMangasTrackingType = json['libraryFilterMangasTrackingType']; + libraryFilterAnimeTrackingType = json['libraryFilterAnimeTrackingType']; + libraryFilterNovelTrackingType = json['libraryFilterNovelTrackingType']; } Map toJson() => { @@ -872,6 +900,13 @@ class Settings { if (algorithmWeights != null) 'algorithmWeights': algorithmWeights!.toJson(), 'localFolders': localFolders, + 'appLockEnabled': appLockEnabled, + 'libraryFilterMangasCompletedType': libraryFilterMangasCompletedType, + 'libraryFilterAnimeCompletedType': libraryFilterAnimeCompletedType, + 'libraryFilterNovelCompletedType': libraryFilterNovelCompletedType, + 'libraryFilterMangasTrackingType': libraryFilterMangasTrackingType, + 'libraryFilterAnimeTrackingType': libraryFilterAnimeTrackingType, + 'libraryFilterNovelTrackingType': libraryFilterNovelTrackingType, }; } diff --git a/lib/models/settings.g.dart b/lib/models/settings.g.dart index 0e60b109..e5af43ca 100644 --- a/lib/models/settings.g.dart +++ b/lib/models/settings.g.dart @@ -92,704 +92,739 @@ const SettingsSchema = CollectionSchema( name: r'appFontFamily', type: IsarType.string, ), - r'audioChannels': PropertySchema( + r'appLockEnabled': PropertySchema( id: 14, + name: r'appLockEnabled', + type: IsarType.bool, + ), + r'audioChannels': PropertySchema( + id: 15, name: r'audioChannels', type: IsarType.byte, enumMap: _SettingsaudioChannelsEnumValueMap, ), r'audioPreferredLanguages': PropertySchema( - id: 15, + id: 16, name: r'audioPreferredLanguages', type: IsarType.string, ), r'autoBackupLocation': PropertySchema( - id: 16, + id: 17, name: r'autoBackupLocation', type: IsarType.string, ), r'autoExtensionsUpdates': PropertySchema( - id: 17, + id: 18, name: r'autoExtensionsUpdates', type: IsarType.bool, ), r'autoScrollPages': PropertySchema( - id: 18, + id: 19, name: r'autoScrollPages', type: IsarType.objectList, target: r'AutoScrollPages', ), r'backgroundColor': PropertySchema( - id: 19, + id: 20, name: r'backgroundColor', type: IsarType.byte, enumMap: _SettingsbackgroundColorEnumValueMap, ), r'backupFrequency': PropertySchema( - id: 20, + id: 21, name: r'backupFrequency', type: IsarType.long, ), r'backupListOptions': PropertySchema( - id: 21, + id: 22, name: r'backupListOptions', type: IsarType.longList, ), r'btServerAddress': PropertySchema( - id: 22, + id: 23, name: r'btServerAddress', type: IsarType.string, ), r'btServerPort': PropertySchema( - id: 23, + id: 24, name: r'btServerPort', type: IsarType.long, ), r'chapterFilterBookmarkedList': PropertySchema( - id: 24, + id: 25, name: r'chapterFilterBookmarkedList', type: IsarType.objectList, target: r'ChapterFilterBookmarked', ), r'chapterFilterDownloadedList': PropertySchema( - id: 25, + id: 26, name: r'chapterFilterDownloadedList', type: IsarType.objectList, target: r'ChapterFilterDownloaded', ), r'chapterFilterUnreadList': PropertySchema( - id: 26, + id: 27, name: r'chapterFilterUnreadList', type: IsarType.objectList, target: r'ChapterFilterUnread', ), r'chapterPageIndexList': PropertySchema( - id: 27, + id: 28, name: r'chapterPageIndexList', type: IsarType.objectList, target: r'ChapterPageIndex', ), r'chapterPageUrlsList': PropertySchema( - id: 28, + id: 29, name: r'chapterPageUrlsList', type: IsarType.objectList, target: r'ChapterPageurls', ), r'checkForAppUpdates': PropertySchema( - id: 29, + id: 30, name: r'checkForAppUpdates', type: IsarType.bool, ), r'checkForExtensionUpdates': PropertySchema( - id: 30, + id: 31, name: r'checkForExtensionUpdates', type: IsarType.bool, ), r'clearChapterCacheOnAppLaunch': PropertySchema( - id: 31, + id: 32, name: r'clearChapterCacheOnAppLaunch', type: IsarType.bool, ), r'colorFilterBlendMode': PropertySchema( - id: 32, + id: 33, name: r'colorFilterBlendMode', type: IsarType.byte, enumMap: _SettingscolorFilterBlendModeEnumValueMap, ), r'concurrentDownloads': PropertySchema( - id: 33, + id: 34, name: r'concurrentDownloads', type: IsarType.long, ), r'cookiesList': PropertySchema( - id: 34, + id: 35, name: r'cookiesList', type: IsarType.objectList, target: r'MCookie', ), r'cropBorders': PropertySchema( - id: 35, + id: 36, name: r'cropBorders', type: IsarType.bool, ), r'customColorFilter': PropertySchema( - id: 36, + id: 37, name: r'customColorFilter', type: IsarType.object, target: r'CustomColorFilter', ), r'customDns': PropertySchema( - id: 37, + id: 38, name: r'customDns', type: IsarType.string, ), r'dateFormat': PropertySchema( - id: 38, + id: 39, name: r'dateFormat', type: IsarType.string, ), r'debandingType': PropertySchema( - id: 39, + id: 40, name: r'debandingType', type: IsarType.byte, enumMap: _SettingsdebandingTypeEnumValueMap, ), r'defaultDoubleTapToSkipLength': PropertySchema( - id: 40, + id: 41, name: r'defaultDoubleTapToSkipLength', type: IsarType.long, ), r'defaultPlayBackSpeed': PropertySchema( - id: 41, + id: 42, name: r'defaultPlayBackSpeed', type: IsarType.double, ), r'defaultReaderMode': PropertySchema( - id: 42, + id: 43, name: r'defaultReaderMode', type: IsarType.byte, enumMap: _SettingsdefaultReaderModeEnumValueMap, ), r'defaultSkipIntroLength': PropertySchema( - id: 43, + id: 44, name: r'defaultSkipIntroLength', type: IsarType.long, ), r'defaultSubtitleLang': PropertySchema( - id: 44, + id: 45, name: r'defaultSubtitleLang', type: IsarType.object, target: r'L10nLocale', ), r'disableSectionType': PropertySchema( - id: 45, + id: 46, name: r'disableSectionType', type: IsarType.byte, enumMap: _SettingsdisableSectionTypeEnumValueMap, ), r'displayType': PropertySchema( - id: 46, + id: 47, name: r'displayType', type: IsarType.byte, enumMap: _SettingsdisplayTypeEnumValueMap, ), r'doHEnabled': PropertySchema( - id: 47, + id: 48, name: r'doHEnabled', type: IsarType.bool, ), r'doHProviderId': PropertySchema( - id: 48, + id: 49, name: r'doHProviderId', type: IsarType.long, ), r'doubleTapAnimationSpeed': PropertySchema( - id: 49, + id: 50, name: r'doubleTapAnimationSpeed', type: IsarType.long, ), r'downloadLocation': PropertySchema( - id: 50, + id: 51, name: r'downloadLocation', type: IsarType.string, ), r'downloadOnlyOnWifi': PropertySchema( - id: 51, + id: 52, name: r'downloadOnlyOnWifi', type: IsarType.bool, ), r'downloadedOnlyMode': PropertySchema( - id: 52, + id: 53, name: r'downloadedOnlyMode', type: IsarType.bool, ), r'enableAniSkip': PropertySchema( - id: 53, + id: 54, name: r'enableAniSkip', type: IsarType.bool, ), r'enableAudioPitchCorrection': PropertySchema( - id: 54, + id: 55, name: r'enableAudioPitchCorrection', type: IsarType.bool, ), r'enableAutoSkip': PropertySchema( - id: 55, + id: 56, name: r'enableAutoSkip', type: IsarType.bool, ), r'enableCustomColorFilter': PropertySchema( - id: 56, + id: 57, name: r'enableCustomColorFilter', type: IsarType.bool, ), r'enableDiscordRpc': PropertySchema( - id: 57, + id: 58, name: r'enableDiscordRpc', type: IsarType.bool, ), r'enableGpuNext': PropertySchema( - id: 58, + id: 59, name: r'enableGpuNext', type: IsarType.bool, ), r'enableHardwareAcceleration': PropertySchema( - id: 59, + id: 60, name: r'enableHardwareAcceleration', type: IsarType.bool, ), r'enableLogs': PropertySchema( - id: 60, + id: 61, name: r'enableLogs', type: IsarType.bool, ), r'filterScanlatorList': PropertySchema( - id: 61, + id: 62, name: r'filterScanlatorList', type: IsarType.objectList, target: r'FilterScanlator', ), r'flexColorSchemeBlendLevel': PropertySchema( - id: 62, + id: 63, name: r'flexColorSchemeBlendLevel', type: IsarType.double, ), r'flexSchemeColorIndex': PropertySchema( - id: 63, + id: 64, name: r'flexSchemeColorIndex', type: IsarType.long, ), r'followSystemTheme': PropertySchema( - id: 64, + id: 65, name: r'followSystemTheme', type: IsarType.bool, ), r'forceLandscapePlayer': PropertySchema( - id: 65, + id: 66, name: r'forceLandscapePlayer', type: IsarType.bool, ), r'fullScreenPlayer': PropertySchema( - id: 66, + id: 67, name: r'fullScreenPlayer', type: IsarType.bool, ), r'fullScreenReader': PropertySchema( - id: 67, + id: 68, name: r'fullScreenReader', type: IsarType.bool, ), r'hideDiscordRpcInIncognito': PropertySchema( - id: 68, + id: 69, name: r'hideDiscordRpcInIncognito', type: IsarType.bool, ), r'hideItems': PropertySchema( - id: 69, + id: 70, name: r'hideItems', type: IsarType.stringList, ), r'hwdecMode': PropertySchema( - id: 70, + id: 71, name: r'hwdecMode', type: IsarType.string, ), r'incognitoMode': PropertySchema( - id: 71, + id: 72, name: r'incognitoMode', type: IsarType.bool, ), r'lastTrackerLibraryLocation': PropertySchema( - id: 72, + id: 73, name: r'lastTrackerLibraryLocation', type: IsarType.string, ), r'libraryDownloadedChapters': PropertySchema( - id: 73, + id: 74, name: r'libraryDownloadedChapters', type: IsarType.bool, ), r'libraryFilterAnimeBookMarkedType': PropertySchema( - id: 74, + id: 75, name: r'libraryFilterAnimeBookMarkedType', type: IsarType.long, ), + r'libraryFilterAnimeCompletedType': PropertySchema( + id: 76, + name: r'libraryFilterAnimeCompletedType', + type: IsarType.long, + ), r'libraryFilterAnimeDownloadType': PropertySchema( - id: 75, + id: 77, name: r'libraryFilterAnimeDownloadType', type: IsarType.long, ), r'libraryFilterAnimeStartedType': PropertySchema( - id: 76, + id: 78, name: r'libraryFilterAnimeStartedType', type: IsarType.long, ), + r'libraryFilterAnimeTrackingType': PropertySchema( + id: 79, + name: r'libraryFilterAnimeTrackingType', + type: IsarType.long, + ), r'libraryFilterAnimeUnreadType': PropertySchema( - id: 77, + id: 80, name: r'libraryFilterAnimeUnreadType', type: IsarType.long, ), r'libraryFilterMangasBookMarkedType': PropertySchema( - id: 78, + id: 81, name: r'libraryFilterMangasBookMarkedType', type: IsarType.long, ), + r'libraryFilterMangasCompletedType': PropertySchema( + id: 82, + name: r'libraryFilterMangasCompletedType', + type: IsarType.long, + ), r'libraryFilterMangasDownloadType': PropertySchema( - id: 79, + id: 83, name: r'libraryFilterMangasDownloadType', type: IsarType.long, ), r'libraryFilterMangasStartedType': PropertySchema( - id: 80, + id: 84, name: r'libraryFilterMangasStartedType', type: IsarType.long, ), + r'libraryFilterMangasTrackingType': PropertySchema( + id: 85, + name: r'libraryFilterMangasTrackingType', + type: IsarType.long, + ), r'libraryFilterMangasUnreadType': PropertySchema( - id: 81, + id: 86, name: r'libraryFilterMangasUnreadType', type: IsarType.long, ), r'libraryFilterNovelBookMarkedType': PropertySchema( - id: 82, + id: 87, name: r'libraryFilterNovelBookMarkedType', type: IsarType.long, ), + r'libraryFilterNovelCompletedType': PropertySchema( + id: 88, + name: r'libraryFilterNovelCompletedType', + type: IsarType.long, + ), r'libraryFilterNovelDownloadType': PropertySchema( - id: 83, + id: 89, name: r'libraryFilterNovelDownloadType', type: IsarType.long, ), r'libraryFilterNovelStartedType': PropertySchema( - id: 84, + id: 90, name: r'libraryFilterNovelStartedType', type: IsarType.long, ), + r'libraryFilterNovelTrackingType': PropertySchema( + id: 91, + name: r'libraryFilterNovelTrackingType', + type: IsarType.long, + ), r'libraryFilterNovelUnreadType': PropertySchema( - id: 85, + id: 92, name: r'libraryFilterNovelUnreadType', type: IsarType.long, ), r'libraryLocalSource': PropertySchema( - id: 86, + id: 93, name: r'libraryLocalSource', type: IsarType.bool, ), r'libraryShowCategoryTabs': PropertySchema( - id: 87, + id: 94, name: r'libraryShowCategoryTabs', type: IsarType.bool, ), r'libraryShowContinueReadingButton': PropertySchema( - id: 88, + id: 95, name: r'libraryShowContinueReadingButton', type: IsarType.bool, ), r'libraryShowLanguage': PropertySchema( - id: 89, + id: 96, name: r'libraryShowLanguage', type: IsarType.bool, ), r'libraryShowNumbersOfItems': PropertySchema( - id: 90, + id: 97, name: r'libraryShowNumbersOfItems', type: IsarType.bool, ), r'localFolders': PropertySchema( - id: 91, + id: 98, name: r'localFolders', type: IsarType.stringList, ), r'locale': PropertySchema( - id: 92, + id: 99, name: r'locale', type: IsarType.object, target: r'L10nLocale', ), r'mangaExtensionsRepo': PropertySchema( - id: 93, + id: 100, name: r'mangaExtensionsRepo', type: IsarType.objectList, target: r'Repo', ), r'mangaGridSize': PropertySchema( - id: 94, + id: 101, name: r'mangaGridSize', type: IsarType.long, ), r'mangaHomeDisplayType': PropertySchema( - id: 95, + id: 102, name: r'mangaHomeDisplayType', type: IsarType.byte, enumMap: _SettingsmangaHomeDisplayTypeEnumValueMap, ), r'markEpisodeAsSeenType': PropertySchema( - id: 96, + id: 103, name: r'markEpisodeAsSeenType', type: IsarType.long, ), r'mergeLibraryNavMobile': PropertySchema( - id: 97, + id: 104, name: r'mergeLibraryNavMobile', type: IsarType.bool, ), r'navigationOrder': PropertySchema( - id: 98, + id: 105, name: r'navigationOrder', type: IsarType.stringList, ), r'novelDisplayType': PropertySchema( - id: 99, + id: 106, name: r'novelDisplayType', type: IsarType.byte, enumMap: _SettingsnovelDisplayTypeEnumValueMap, ), r'novelExtensionsRepo': PropertySchema( - id: 100, + id: 107, name: r'novelExtensionsRepo', type: IsarType.objectList, target: r'Repo', ), r'novelFontSize': PropertySchema( - id: 101, + id: 108, name: r'novelFontSize', type: IsarType.long, ), r'novelGridSize': PropertySchema( - id: 102, + id: 109, name: r'novelGridSize', type: IsarType.long, ), r'novelLibraryDownloadedChapters': PropertySchema( - id: 103, + id: 110, name: r'novelLibraryDownloadedChapters', type: IsarType.bool, ), r'novelLibraryLocalSource': PropertySchema( - id: 104, + id: 111, name: r'novelLibraryLocalSource', type: IsarType.bool, ), r'novelLibraryShowCategoryTabs': PropertySchema( - id: 105, + id: 112, name: r'novelLibraryShowCategoryTabs', type: IsarType.bool, ), r'novelLibraryShowContinueReadingButton': PropertySchema( - id: 106, + id: 113, name: r'novelLibraryShowContinueReadingButton', type: IsarType.bool, ), r'novelLibraryShowLanguage': PropertySchema( - id: 107, + id: 114, name: r'novelLibraryShowLanguage', type: IsarType.bool, ), r'novelLibraryShowNumbersOfItems': PropertySchema( - id: 108, + id: 115, name: r'novelLibraryShowNumbersOfItems', type: IsarType.bool, ), r'novelReaderLineHeight': PropertySchema( - id: 109, + id: 116, name: r'novelReaderLineHeight', type: IsarType.double, ), r'novelReaderPadding': PropertySchema( - id: 110, + id: 117, name: r'novelReaderPadding', type: IsarType.long, ), r'novelReaderTextColor': PropertySchema( - id: 111, + id: 118, name: r'novelReaderTextColor', type: IsarType.string, ), r'novelReaderTheme': PropertySchema( - id: 112, + id: 119, name: r'novelReaderTheme', type: IsarType.string, ), r'novelRemoveExtraParagraphSpacing': PropertySchema( - id: 113, + id: 120, name: r'novelRemoveExtraParagraphSpacing', type: IsarType.bool, ), r'novelShowScrollPercentage': PropertySchema( - id: 114, + id: 121, name: r'novelShowScrollPercentage', type: IsarType.bool, ), r'novelTapToScroll': PropertySchema( - id: 115, + id: 122, name: r'novelTapToScroll', type: IsarType.bool, ), r'novelTextAlign': PropertySchema( - id: 116, + id: 123, name: r'novelTextAlign', type: IsarType.byte, enumMap: _SettingsnovelTextAlignEnumValueMap, ), r'onlyIncludePinnedSources': PropertySchema( - id: 117, + id: 124, name: r'onlyIncludePinnedSources', type: IsarType.bool, ), r'pagePreloadAmount': PropertySchema( - id: 118, + id: 125, name: r'pagePreloadAmount', type: IsarType.long, ), r'personalPageModeList': PropertySchema( - id: 119, + id: 126, name: r'personalPageModeList', type: IsarType.objectList, target: r'PersonalPageMode', ), r'personalReaderModeList': PropertySchema( - id: 120, + id: 127, name: r'personalReaderModeList', type: IsarType.objectList, target: r'PersonalReaderMode', ), r'playerSubtitleSettings': PropertySchema( - id: 121, + id: 128, name: r'playerSubtitleSettings', type: IsarType.object, target: r'PlayerSubtitleSettings', ), r'pureBlackDarkMode': PropertySchema( - id: 122, + id: 129, name: r'pureBlackDarkMode', type: IsarType.bool, ), r'relativeTimesTamps': PropertySchema( - id: 123, + id: 130, name: r'relativeTimesTamps', type: IsarType.long, ), r'rpcShowCoverImage': PropertySchema( - id: 124, + id: 131, name: r'rpcShowCoverImage', type: IsarType.bool, ), r'rpcShowReadingWatchingProgress': PropertySchema( - id: 125, + id: 132, name: r'rpcShowReadingWatchingProgress', type: IsarType.bool, ), r'rpcShowTitle': PropertySchema( - id: 126, + id: 133, name: r'rpcShowTitle', type: IsarType.bool, ), r'saveAsCBZArchive': PropertySchema( - id: 127, + id: 134, name: r'saveAsCBZArchive', type: IsarType.bool, ), r'scaleType': PropertySchema( - id: 128, + id: 135, name: r'scaleType', type: IsarType.byte, enumMap: _SettingsscaleTypeEnumValueMap, ), r'showPagesNumber': PropertySchema( - id: 129, + id: 136, name: r'showPagesNumber', type: IsarType.bool, ), r'sortChapterList': PropertySchema( - id: 130, + id: 137, name: r'sortChapterList', type: IsarType.objectList, target: r'SortChapter', ), r'sortLibraryAnime': PropertySchema( - id: 131, + id: 138, name: r'sortLibraryAnime', type: IsarType.object, target: r'SortLibraryManga', ), r'sortLibraryManga': PropertySchema( - id: 132, + id: 139, name: r'sortLibraryManga', type: IsarType.object, target: r'SortLibraryManga', ), r'sortLibraryNovel': PropertySchema( - id: 133, + id: 140, name: r'sortLibraryNovel', type: IsarType.object, target: r'SortLibraryManga', ), r'startDatebackup': PropertySchema( - id: 134, + id: 141, name: r'startDatebackup', type: IsarType.long, ), r'themeIsDark': PropertySchema( - id: 135, + id: 142, name: r'themeIsDark', type: IsarType.bool, ), r'updateProgressAfterReading': PropertySchema( - id: 136, + id: 143, name: r'updateProgressAfterReading', type: IsarType.bool, ), r'updatedAt': PropertySchema( - id: 137, + id: 144, name: r'updatedAt', type: IsarType.long, ), r'useLibass': PropertySchema( - id: 138, + id: 145, name: r'useLibass', type: IsarType.bool, ), r'useMpvConfig': PropertySchema( - id: 139, + id: 146, name: r'useMpvConfig', type: IsarType.bool, ), r'usePageTapZones': PropertySchema( - id: 140, + id: 147, name: r'usePageTapZones', type: IsarType.bool, ), r'useYUV420P': PropertySchema( - id: 141, + id: 148, name: r'useYUV420P', type: IsarType.bool, ), r'userAgent': PropertySchema( - id: 142, + id: 149, name: r'userAgent', type: IsarType.string, ), r'volumeBoostCap': PropertySchema( - id: 143, + id: 150, name: r'volumeBoostCap', type: IsarType.long, ), @@ -1312,236 +1347,243 @@ void _settingsSerialize( writer.writeBool(offsets[11], object.animeLibraryShowLanguage); writer.writeBool(offsets[12], object.animeLibraryShowNumbersOfItems); writer.writeString(offsets[13], object.appFontFamily); - writer.writeByte(offsets[14], object.audioChannels.index); - writer.writeString(offsets[15], object.audioPreferredLanguages); - writer.writeString(offsets[16], object.autoBackupLocation); - writer.writeBool(offsets[17], object.autoExtensionsUpdates); + writer.writeBool(offsets[14], object.appLockEnabled); + writer.writeByte(offsets[15], object.audioChannels.index); + writer.writeString(offsets[16], object.audioPreferredLanguages); + writer.writeString(offsets[17], object.autoBackupLocation); + writer.writeBool(offsets[18], object.autoExtensionsUpdates); writer.writeObjectList( - offsets[18], + offsets[19], allOffsets, AutoScrollPagesSchema.serialize, object.autoScrollPages, ); - writer.writeByte(offsets[19], object.backgroundColor.index); - writer.writeLong(offsets[20], object.backupFrequency); - writer.writeLongList(offsets[21], object.backupListOptions); - writer.writeString(offsets[22], object.btServerAddress); - writer.writeLong(offsets[23], object.btServerPort); + writer.writeByte(offsets[20], object.backgroundColor.index); + writer.writeLong(offsets[21], object.backupFrequency); + writer.writeLongList(offsets[22], object.backupListOptions); + writer.writeString(offsets[23], object.btServerAddress); + writer.writeLong(offsets[24], object.btServerPort); writer.writeObjectList( - offsets[24], + offsets[25], allOffsets, ChapterFilterBookmarkedSchema.serialize, object.chapterFilterBookmarkedList, ); writer.writeObjectList( - offsets[25], + offsets[26], allOffsets, ChapterFilterDownloadedSchema.serialize, object.chapterFilterDownloadedList, ); writer.writeObjectList( - offsets[26], + offsets[27], allOffsets, ChapterFilterUnreadSchema.serialize, object.chapterFilterUnreadList, ); writer.writeObjectList( - offsets[27], + offsets[28], allOffsets, ChapterPageIndexSchema.serialize, object.chapterPageIndexList, ); writer.writeObjectList( - offsets[28], + offsets[29], allOffsets, ChapterPageurlsSchema.serialize, object.chapterPageUrlsList, ); - writer.writeBool(offsets[29], object.checkForAppUpdates); - writer.writeBool(offsets[30], object.checkForExtensionUpdates); - writer.writeBool(offsets[31], object.clearChapterCacheOnAppLaunch); - writer.writeByte(offsets[32], object.colorFilterBlendMode.index); - writer.writeLong(offsets[33], object.concurrentDownloads); + writer.writeBool(offsets[30], object.checkForAppUpdates); + writer.writeBool(offsets[31], object.checkForExtensionUpdates); + writer.writeBool(offsets[32], object.clearChapterCacheOnAppLaunch); + writer.writeByte(offsets[33], object.colorFilterBlendMode.index); + writer.writeLong(offsets[34], object.concurrentDownloads); writer.writeObjectList( - offsets[34], + offsets[35], allOffsets, MCookieSchema.serialize, object.cookiesList, ); - writer.writeBool(offsets[35], object.cropBorders); + writer.writeBool(offsets[36], object.cropBorders); writer.writeObject( - offsets[36], + offsets[37], allOffsets, CustomColorFilterSchema.serialize, object.customColorFilter, ); - writer.writeString(offsets[37], object.customDns); - writer.writeString(offsets[38], object.dateFormat); - writer.writeByte(offsets[39], object.debandingType.index); - writer.writeLong(offsets[40], object.defaultDoubleTapToSkipLength); - writer.writeDouble(offsets[41], object.defaultPlayBackSpeed); - writer.writeByte(offsets[42], object.defaultReaderMode.index); - writer.writeLong(offsets[43], object.defaultSkipIntroLength); + writer.writeString(offsets[38], object.customDns); + writer.writeString(offsets[39], object.dateFormat); + writer.writeByte(offsets[40], object.debandingType.index); + writer.writeLong(offsets[41], object.defaultDoubleTapToSkipLength); + writer.writeDouble(offsets[42], object.defaultPlayBackSpeed); + writer.writeByte(offsets[43], object.defaultReaderMode.index); + writer.writeLong(offsets[44], object.defaultSkipIntroLength); writer.writeObject( - offsets[44], + offsets[45], allOffsets, L10nLocaleSchema.serialize, object.defaultSubtitleLang, ); - writer.writeByte(offsets[45], object.disableSectionType.index); - writer.writeByte(offsets[46], object.displayType.index); - writer.writeBool(offsets[47], object.doHEnabled); - writer.writeLong(offsets[48], object.doHProviderId); - writer.writeLong(offsets[49], object.doubleTapAnimationSpeed); - writer.writeString(offsets[50], object.downloadLocation); - writer.writeBool(offsets[51], object.downloadOnlyOnWifi); - writer.writeBool(offsets[52], object.downloadedOnlyMode); - writer.writeBool(offsets[53], object.enableAniSkip); - writer.writeBool(offsets[54], object.enableAudioPitchCorrection); - writer.writeBool(offsets[55], object.enableAutoSkip); - writer.writeBool(offsets[56], object.enableCustomColorFilter); - writer.writeBool(offsets[57], object.enableDiscordRpc); - writer.writeBool(offsets[58], object.enableGpuNext); - writer.writeBool(offsets[59], object.enableHardwareAcceleration); - writer.writeBool(offsets[60], object.enableLogs); + writer.writeByte(offsets[46], object.disableSectionType.index); + writer.writeByte(offsets[47], object.displayType.index); + writer.writeBool(offsets[48], object.doHEnabled); + writer.writeLong(offsets[49], object.doHProviderId); + writer.writeLong(offsets[50], object.doubleTapAnimationSpeed); + writer.writeString(offsets[51], object.downloadLocation); + writer.writeBool(offsets[52], object.downloadOnlyOnWifi); + writer.writeBool(offsets[53], object.downloadedOnlyMode); + writer.writeBool(offsets[54], object.enableAniSkip); + writer.writeBool(offsets[55], object.enableAudioPitchCorrection); + writer.writeBool(offsets[56], object.enableAutoSkip); + writer.writeBool(offsets[57], object.enableCustomColorFilter); + writer.writeBool(offsets[58], object.enableDiscordRpc); + writer.writeBool(offsets[59], object.enableGpuNext); + writer.writeBool(offsets[60], object.enableHardwareAcceleration); + writer.writeBool(offsets[61], object.enableLogs); writer.writeObjectList( - offsets[61], + offsets[62], allOffsets, FilterScanlatorSchema.serialize, object.filterScanlatorList, ); - writer.writeDouble(offsets[62], object.flexColorSchemeBlendLevel); - writer.writeLong(offsets[63], object.flexSchemeColorIndex); - writer.writeBool(offsets[64], object.followSystemTheme); - writer.writeBool(offsets[65], object.forceLandscapePlayer); - writer.writeBool(offsets[66], object.fullScreenPlayer); - writer.writeBool(offsets[67], object.fullScreenReader); - writer.writeBool(offsets[68], object.hideDiscordRpcInIncognito); - writer.writeStringList(offsets[69], object.hideItems); - writer.writeString(offsets[70], object.hwdecMode); - writer.writeBool(offsets[71], object.incognitoMode); - writer.writeString(offsets[72], object.lastTrackerLibraryLocation); - writer.writeBool(offsets[73], object.libraryDownloadedChapters); - writer.writeLong(offsets[74], object.libraryFilterAnimeBookMarkedType); - writer.writeLong(offsets[75], object.libraryFilterAnimeDownloadType); - writer.writeLong(offsets[76], object.libraryFilterAnimeStartedType); - writer.writeLong(offsets[77], object.libraryFilterAnimeUnreadType); - writer.writeLong(offsets[78], object.libraryFilterMangasBookMarkedType); - writer.writeLong(offsets[79], object.libraryFilterMangasDownloadType); - writer.writeLong(offsets[80], object.libraryFilterMangasStartedType); - writer.writeLong(offsets[81], object.libraryFilterMangasUnreadType); - writer.writeLong(offsets[82], object.libraryFilterNovelBookMarkedType); - writer.writeLong(offsets[83], object.libraryFilterNovelDownloadType); - writer.writeLong(offsets[84], object.libraryFilterNovelStartedType); - writer.writeLong(offsets[85], object.libraryFilterNovelUnreadType); - writer.writeBool(offsets[86], object.libraryLocalSource); - writer.writeBool(offsets[87], object.libraryShowCategoryTabs); - writer.writeBool(offsets[88], object.libraryShowContinueReadingButton); - writer.writeBool(offsets[89], object.libraryShowLanguage); - writer.writeBool(offsets[90], object.libraryShowNumbersOfItems); - writer.writeStringList(offsets[91], object.localFolders); + writer.writeDouble(offsets[63], object.flexColorSchemeBlendLevel); + writer.writeLong(offsets[64], object.flexSchemeColorIndex); + writer.writeBool(offsets[65], object.followSystemTheme); + writer.writeBool(offsets[66], object.forceLandscapePlayer); + writer.writeBool(offsets[67], object.fullScreenPlayer); + writer.writeBool(offsets[68], object.fullScreenReader); + writer.writeBool(offsets[69], object.hideDiscordRpcInIncognito); + writer.writeStringList(offsets[70], object.hideItems); + writer.writeString(offsets[71], object.hwdecMode); + writer.writeBool(offsets[72], object.incognitoMode); + writer.writeString(offsets[73], object.lastTrackerLibraryLocation); + writer.writeBool(offsets[74], object.libraryDownloadedChapters); + writer.writeLong(offsets[75], object.libraryFilterAnimeBookMarkedType); + writer.writeLong(offsets[76], object.libraryFilterAnimeCompletedType); + writer.writeLong(offsets[77], object.libraryFilterAnimeDownloadType); + writer.writeLong(offsets[78], object.libraryFilterAnimeStartedType); + writer.writeLong(offsets[79], object.libraryFilterAnimeTrackingType); + writer.writeLong(offsets[80], object.libraryFilterAnimeUnreadType); + writer.writeLong(offsets[81], object.libraryFilterMangasBookMarkedType); + writer.writeLong(offsets[82], object.libraryFilterMangasCompletedType); + writer.writeLong(offsets[83], object.libraryFilterMangasDownloadType); + writer.writeLong(offsets[84], object.libraryFilterMangasStartedType); + writer.writeLong(offsets[85], object.libraryFilterMangasTrackingType); + writer.writeLong(offsets[86], object.libraryFilterMangasUnreadType); + writer.writeLong(offsets[87], object.libraryFilterNovelBookMarkedType); + writer.writeLong(offsets[88], object.libraryFilterNovelCompletedType); + writer.writeLong(offsets[89], object.libraryFilterNovelDownloadType); + writer.writeLong(offsets[90], object.libraryFilterNovelStartedType); + writer.writeLong(offsets[91], object.libraryFilterNovelTrackingType); + writer.writeLong(offsets[92], object.libraryFilterNovelUnreadType); + writer.writeBool(offsets[93], object.libraryLocalSource); + writer.writeBool(offsets[94], object.libraryShowCategoryTabs); + writer.writeBool(offsets[95], object.libraryShowContinueReadingButton); + writer.writeBool(offsets[96], object.libraryShowLanguage); + writer.writeBool(offsets[97], object.libraryShowNumbersOfItems); + writer.writeStringList(offsets[98], object.localFolders); writer.writeObject( - offsets[92], + offsets[99], allOffsets, L10nLocaleSchema.serialize, object.locale, ); writer.writeObjectList( - offsets[93], + offsets[100], allOffsets, RepoSchema.serialize, object.mangaExtensionsRepo, ); - writer.writeLong(offsets[94], object.mangaGridSize); - writer.writeByte(offsets[95], object.mangaHomeDisplayType.index); - writer.writeLong(offsets[96], object.markEpisodeAsSeenType); - writer.writeBool(offsets[97], object.mergeLibraryNavMobile); - writer.writeStringList(offsets[98], object.navigationOrder); - writer.writeByte(offsets[99], object.novelDisplayType.index); + writer.writeLong(offsets[101], object.mangaGridSize); + writer.writeByte(offsets[102], object.mangaHomeDisplayType.index); + writer.writeLong(offsets[103], object.markEpisodeAsSeenType); + writer.writeBool(offsets[104], object.mergeLibraryNavMobile); + writer.writeStringList(offsets[105], object.navigationOrder); + writer.writeByte(offsets[106], object.novelDisplayType.index); writer.writeObjectList( - offsets[100], + offsets[107], allOffsets, RepoSchema.serialize, object.novelExtensionsRepo, ); - writer.writeLong(offsets[101], object.novelFontSize); - writer.writeLong(offsets[102], object.novelGridSize); - writer.writeBool(offsets[103], object.novelLibraryDownloadedChapters); - writer.writeBool(offsets[104], object.novelLibraryLocalSource); - writer.writeBool(offsets[105], object.novelLibraryShowCategoryTabs); - writer.writeBool(offsets[106], object.novelLibraryShowContinueReadingButton); - writer.writeBool(offsets[107], object.novelLibraryShowLanguage); - writer.writeBool(offsets[108], object.novelLibraryShowNumbersOfItems); - writer.writeDouble(offsets[109], object.novelReaderLineHeight); - writer.writeLong(offsets[110], object.novelReaderPadding); - writer.writeString(offsets[111], object.novelReaderTextColor); - writer.writeString(offsets[112], object.novelReaderTheme); - writer.writeBool(offsets[113], object.novelRemoveExtraParagraphSpacing); - writer.writeBool(offsets[114], object.novelShowScrollPercentage); - writer.writeBool(offsets[115], object.novelTapToScroll); - writer.writeByte(offsets[116], object.novelTextAlign.index); - writer.writeBool(offsets[117], object.onlyIncludePinnedSources); - writer.writeLong(offsets[118], object.pagePreloadAmount); + writer.writeLong(offsets[108], object.novelFontSize); + writer.writeLong(offsets[109], object.novelGridSize); + writer.writeBool(offsets[110], object.novelLibraryDownloadedChapters); + writer.writeBool(offsets[111], object.novelLibraryLocalSource); + writer.writeBool(offsets[112], object.novelLibraryShowCategoryTabs); + writer.writeBool(offsets[113], object.novelLibraryShowContinueReadingButton); + writer.writeBool(offsets[114], object.novelLibraryShowLanguage); + writer.writeBool(offsets[115], object.novelLibraryShowNumbersOfItems); + writer.writeDouble(offsets[116], object.novelReaderLineHeight); + writer.writeLong(offsets[117], object.novelReaderPadding); + writer.writeString(offsets[118], object.novelReaderTextColor); + writer.writeString(offsets[119], object.novelReaderTheme); + writer.writeBool(offsets[120], object.novelRemoveExtraParagraphSpacing); + writer.writeBool(offsets[121], object.novelShowScrollPercentage); + writer.writeBool(offsets[122], object.novelTapToScroll); + writer.writeByte(offsets[123], object.novelTextAlign.index); + writer.writeBool(offsets[124], object.onlyIncludePinnedSources); + writer.writeLong(offsets[125], object.pagePreloadAmount); writer.writeObjectList( - offsets[119], + offsets[126], allOffsets, PersonalPageModeSchema.serialize, object.personalPageModeList, ); writer.writeObjectList( - offsets[120], + offsets[127], allOffsets, PersonalReaderModeSchema.serialize, object.personalReaderModeList, ); writer.writeObject( - offsets[121], + offsets[128], allOffsets, PlayerSubtitleSettingsSchema.serialize, object.playerSubtitleSettings, ); - writer.writeBool(offsets[122], object.pureBlackDarkMode); - writer.writeLong(offsets[123], object.relativeTimesTamps); - writer.writeBool(offsets[124], object.rpcShowCoverImage); - writer.writeBool(offsets[125], object.rpcShowReadingWatchingProgress); - writer.writeBool(offsets[126], object.rpcShowTitle); - writer.writeBool(offsets[127], object.saveAsCBZArchive); - writer.writeByte(offsets[128], object.scaleType.index); - writer.writeBool(offsets[129], object.showPagesNumber); + writer.writeBool(offsets[129], object.pureBlackDarkMode); + writer.writeLong(offsets[130], object.relativeTimesTamps); + writer.writeBool(offsets[131], object.rpcShowCoverImage); + writer.writeBool(offsets[132], object.rpcShowReadingWatchingProgress); + writer.writeBool(offsets[133], object.rpcShowTitle); + writer.writeBool(offsets[134], object.saveAsCBZArchive); + writer.writeByte(offsets[135], object.scaleType.index); + writer.writeBool(offsets[136], object.showPagesNumber); writer.writeObjectList( - offsets[130], + offsets[137], allOffsets, SortChapterSchema.serialize, object.sortChapterList, ); writer.writeObject( - offsets[131], + offsets[138], allOffsets, SortLibraryMangaSchema.serialize, object.sortLibraryAnime, ); writer.writeObject( - offsets[132], + offsets[139], allOffsets, SortLibraryMangaSchema.serialize, object.sortLibraryManga, ); writer.writeObject( - offsets[133], + offsets[140], allOffsets, SortLibraryMangaSchema.serialize, object.sortLibraryNovel, ); - writer.writeLong(offsets[134], object.startDatebackup); - writer.writeBool(offsets[135], object.themeIsDark); - writer.writeBool(offsets[136], object.updateProgressAfterReading); - writer.writeLong(offsets[137], object.updatedAt); - writer.writeBool(offsets[138], object.useLibass); - writer.writeBool(offsets[139], object.useMpvConfig); - writer.writeBool(offsets[140], object.usePageTapZones); - writer.writeBool(offsets[141], object.useYUV420P); - writer.writeString(offsets[142], object.userAgent); - writer.writeLong(offsets[143], object.volumeBoostCap); + writer.writeLong(offsets[141], object.startDatebackup); + writer.writeBool(offsets[142], object.themeIsDark); + writer.writeBool(offsets[143], object.updateProgressAfterReading); + writer.writeLong(offsets[144], object.updatedAt); + writer.writeBool(offsets[145], object.useLibass); + writer.writeBool(offsets[146], object.useMpvConfig); + writer.writeBool(offsets[147], object.usePageTapZones); + writer.writeBool(offsets[148], object.useYUV420P); + writer.writeString(offsets[149], object.userAgent); + writer.writeLong(offsets[150], object.volumeBoostCap); } Settings _settingsDeserialize( @@ -1578,272 +1620,279 @@ Settings _settingsDeserialize( animeLibraryShowLanguage: reader.readBoolOrNull(offsets[11]), animeLibraryShowNumbersOfItems: reader.readBoolOrNull(offsets[12]), appFontFamily: reader.readStringOrNull(offsets[13]), + appLockEnabled: reader.readBoolOrNull(offsets[14]), audioChannels: _SettingsaudioChannelsValueEnumMap[reader.readByteOrNull( - offsets[14], + offsets[15], )] ?? AudioChannel.autoSafe, - audioPreferredLanguages: reader.readStringOrNull(offsets[15]), - autoBackupLocation: reader.readStringOrNull(offsets[16]), - autoExtensionsUpdates: reader.readBoolOrNull(offsets[17]), + audioPreferredLanguages: reader.readStringOrNull(offsets[16]), + autoBackupLocation: reader.readStringOrNull(offsets[17]), + autoExtensionsUpdates: reader.readBoolOrNull(offsets[18]), autoScrollPages: reader.readObjectList( - offsets[18], + offsets[19], AutoScrollPagesSchema.deserialize, allOffsets, AutoScrollPages(), ), backgroundColor: _SettingsbackgroundColorValueEnumMap[reader.readByteOrNull( - offsets[19], + offsets[20], )] ?? BackgroundColor.black, - backupFrequency: reader.readLongOrNull(offsets[20]), - backupListOptions: reader.readLongList(offsets[21]), - btServerAddress: reader.readStringOrNull(offsets[22]), - btServerPort: reader.readLongOrNull(offsets[23]), + backupFrequency: reader.readLongOrNull(offsets[21]), + backupListOptions: reader.readLongList(offsets[22]), + btServerAddress: reader.readStringOrNull(offsets[23]), + btServerPort: reader.readLongOrNull(offsets[24]), chapterFilterDownloadedList: reader.readObjectList( - offsets[25], + offsets[26], ChapterFilterDownloadedSchema.deserialize, allOffsets, ChapterFilterDownloaded(), ), chapterPageIndexList: reader.readObjectList( - offsets[27], + offsets[28], ChapterPageIndexSchema.deserialize, allOffsets, ChapterPageIndex(), ), chapterPageUrlsList: reader.readObjectList( - offsets[28], + offsets[29], ChapterPageurlsSchema.deserialize, allOffsets, ChapterPageurls(), ), - checkForAppUpdates: reader.readBoolOrNull(offsets[29]), - checkForExtensionUpdates: reader.readBoolOrNull(offsets[30]), - clearChapterCacheOnAppLaunch: reader.readBoolOrNull(offsets[31]), + checkForAppUpdates: reader.readBoolOrNull(offsets[30]), + checkForExtensionUpdates: reader.readBoolOrNull(offsets[31]), + clearChapterCacheOnAppLaunch: reader.readBoolOrNull(offsets[32]), colorFilterBlendMode: _SettingscolorFilterBlendModeValueEnumMap[reader.readByteOrNull( - offsets[32], + offsets[33], )] ?? ColorFilterBlendMode.none, - concurrentDownloads: reader.readLongOrNull(offsets[33]), + concurrentDownloads: reader.readLongOrNull(offsets[34]), cookiesList: reader.readObjectList( - offsets[34], + offsets[35], MCookieSchema.deserialize, allOffsets, MCookie(), ), - cropBorders: reader.readBoolOrNull(offsets[35]), + cropBorders: reader.readBoolOrNull(offsets[36]), customColorFilter: reader.readObjectOrNull( - offsets[36], + offsets[37], CustomColorFilterSchema.deserialize, allOffsets, ), - customDns: reader.readStringOrNull(offsets[37]), - dateFormat: reader.readStringOrNull(offsets[38]), + customDns: reader.readStringOrNull(offsets[38]), + dateFormat: reader.readStringOrNull(offsets[39]), debandingType: _SettingsdebandingTypeValueEnumMap[reader.readByteOrNull( - offsets[39], + offsets[40], )] ?? DebandingType.none, - defaultDoubleTapToSkipLength: reader.readLongOrNull(offsets[40]), - defaultPlayBackSpeed: reader.readDoubleOrNull(offsets[41]), + defaultDoubleTapToSkipLength: reader.readLongOrNull(offsets[41]), + defaultPlayBackSpeed: reader.readDoubleOrNull(offsets[42]), defaultReaderMode: _SettingsdefaultReaderModeValueEnumMap[reader.readByteOrNull( - offsets[42], + offsets[43], )] ?? ReaderMode.vertical, - defaultSkipIntroLength: reader.readLongOrNull(offsets[43]), + defaultSkipIntroLength: reader.readLongOrNull(offsets[44]), disableSectionType: _SettingsdisableSectionTypeValueEnumMap[reader.readByteOrNull( - offsets[45], + offsets[46], )] ?? SectionType.all, displayType: - _SettingsdisplayTypeValueEnumMap[reader.readByteOrNull(offsets[46])] ?? + _SettingsdisplayTypeValueEnumMap[reader.readByteOrNull(offsets[47])] ?? DisplayType.compactGrid, - doHEnabled: reader.readBoolOrNull(offsets[47]), - doHProviderId: reader.readLongOrNull(offsets[48]), - doubleTapAnimationSpeed: reader.readLongOrNull(offsets[49]), - downloadLocation: reader.readStringOrNull(offsets[50]), - downloadOnlyOnWifi: reader.readBoolOrNull(offsets[51]), - downloadedOnlyMode: reader.readBoolOrNull(offsets[52]), - enableAniSkip: reader.readBoolOrNull(offsets[53]), - enableAudioPitchCorrection: reader.readBoolOrNull(offsets[54]), - enableAutoSkip: reader.readBoolOrNull(offsets[55]), - enableCustomColorFilter: reader.readBoolOrNull(offsets[56]), - enableDiscordRpc: reader.readBoolOrNull(offsets[57]), - enableGpuNext: reader.readBoolOrNull(offsets[58]), - enableHardwareAcceleration: reader.readBoolOrNull(offsets[59]), - enableLogs: reader.readBoolOrNull(offsets[60]), - flexColorSchemeBlendLevel: reader.readDoubleOrNull(offsets[62]), - flexSchemeColorIndex: reader.readLongOrNull(offsets[63]), - followSystemTheme: reader.readBoolOrNull(offsets[64]), - forceLandscapePlayer: reader.readBoolOrNull(offsets[65]), - fullScreenPlayer: reader.readBoolOrNull(offsets[66]), - fullScreenReader: reader.readBoolOrNull(offsets[67]), - hideDiscordRpcInIncognito: reader.readBoolOrNull(offsets[68]), - hideItems: reader.readStringList(offsets[69]), - hwdecMode: reader.readStringOrNull(offsets[70]), + doHEnabled: reader.readBoolOrNull(offsets[48]), + doHProviderId: reader.readLongOrNull(offsets[49]), + doubleTapAnimationSpeed: reader.readLongOrNull(offsets[50]), + downloadLocation: reader.readStringOrNull(offsets[51]), + downloadOnlyOnWifi: reader.readBoolOrNull(offsets[52]), + downloadedOnlyMode: reader.readBoolOrNull(offsets[53]), + enableAniSkip: reader.readBoolOrNull(offsets[54]), + enableAudioPitchCorrection: reader.readBoolOrNull(offsets[55]), + enableAutoSkip: reader.readBoolOrNull(offsets[56]), + enableCustomColorFilter: reader.readBoolOrNull(offsets[57]), + enableDiscordRpc: reader.readBoolOrNull(offsets[58]), + enableGpuNext: reader.readBoolOrNull(offsets[59]), + enableHardwareAcceleration: reader.readBoolOrNull(offsets[60]), + enableLogs: reader.readBoolOrNull(offsets[61]), + flexColorSchemeBlendLevel: reader.readDoubleOrNull(offsets[63]), + flexSchemeColorIndex: reader.readLongOrNull(offsets[64]), + followSystemTheme: reader.readBoolOrNull(offsets[65]), + forceLandscapePlayer: reader.readBoolOrNull(offsets[66]), + fullScreenPlayer: reader.readBoolOrNull(offsets[67]), + fullScreenReader: reader.readBoolOrNull(offsets[68]), + hideDiscordRpcInIncognito: reader.readBoolOrNull(offsets[69]), + hideItems: reader.readStringList(offsets[70]), + hwdecMode: reader.readStringOrNull(offsets[71]), id: id, - incognitoMode: reader.readBoolOrNull(offsets[71]), - lastTrackerLibraryLocation: reader.readStringOrNull(offsets[72]), - libraryDownloadedChapters: reader.readBoolOrNull(offsets[73]), - libraryFilterAnimeBookMarkedType: reader.readLongOrNull(offsets[74]), - libraryFilterAnimeDownloadType: reader.readLongOrNull(offsets[75]), - libraryFilterAnimeStartedType: reader.readLongOrNull(offsets[76]), - libraryFilterAnimeUnreadType: reader.readLongOrNull(offsets[77]), - libraryFilterMangasBookMarkedType: reader.readLongOrNull(offsets[78]), - libraryFilterMangasDownloadType: reader.readLongOrNull(offsets[79]), - libraryFilterMangasStartedType: reader.readLongOrNull(offsets[80]), - libraryFilterMangasUnreadType: reader.readLongOrNull(offsets[81]), - libraryFilterNovelBookMarkedType: reader.readLongOrNull(offsets[82]), - libraryFilterNovelDownloadType: reader.readLongOrNull(offsets[83]), - libraryFilterNovelStartedType: reader.readLongOrNull(offsets[84]), - libraryFilterNovelUnreadType: reader.readLongOrNull(offsets[85]), - libraryLocalSource: reader.readBoolOrNull(offsets[86]), - libraryShowCategoryTabs: reader.readBoolOrNull(offsets[87]), - libraryShowContinueReadingButton: reader.readBoolOrNull(offsets[88]), - libraryShowLanguage: reader.readBoolOrNull(offsets[89]), - libraryShowNumbersOfItems: reader.readBoolOrNull(offsets[90]), - localFolders: reader.readStringList(offsets[91]), + incognitoMode: reader.readBoolOrNull(offsets[72]), + lastTrackerLibraryLocation: reader.readStringOrNull(offsets[73]), + libraryDownloadedChapters: reader.readBoolOrNull(offsets[74]), + libraryFilterAnimeBookMarkedType: reader.readLongOrNull(offsets[75]), + libraryFilterAnimeCompletedType: reader.readLongOrNull(offsets[76]), + libraryFilterAnimeDownloadType: reader.readLongOrNull(offsets[77]), + libraryFilterAnimeStartedType: reader.readLongOrNull(offsets[78]), + libraryFilterAnimeTrackingType: reader.readLongOrNull(offsets[79]), + libraryFilterAnimeUnreadType: reader.readLongOrNull(offsets[80]), + libraryFilterMangasBookMarkedType: reader.readLongOrNull(offsets[81]), + libraryFilterMangasCompletedType: reader.readLongOrNull(offsets[82]), + libraryFilterMangasDownloadType: reader.readLongOrNull(offsets[83]), + libraryFilterMangasStartedType: reader.readLongOrNull(offsets[84]), + libraryFilterMangasTrackingType: reader.readLongOrNull(offsets[85]), + libraryFilterMangasUnreadType: reader.readLongOrNull(offsets[86]), + libraryFilterNovelBookMarkedType: reader.readLongOrNull(offsets[87]), + libraryFilterNovelCompletedType: reader.readLongOrNull(offsets[88]), + libraryFilterNovelDownloadType: reader.readLongOrNull(offsets[89]), + libraryFilterNovelStartedType: reader.readLongOrNull(offsets[90]), + libraryFilterNovelTrackingType: reader.readLongOrNull(offsets[91]), + libraryFilterNovelUnreadType: reader.readLongOrNull(offsets[92]), + libraryLocalSource: reader.readBoolOrNull(offsets[93]), + libraryShowCategoryTabs: reader.readBoolOrNull(offsets[94]), + libraryShowContinueReadingButton: reader.readBoolOrNull(offsets[95]), + libraryShowLanguage: reader.readBoolOrNull(offsets[96]), + libraryShowNumbersOfItems: reader.readBoolOrNull(offsets[97]), + localFolders: reader.readStringList(offsets[98]), mangaExtensionsRepo: reader.readObjectList( - offsets[93], - RepoSchema.deserialize, - allOffsets, - Repo(), - ), - mangaGridSize: reader.readLongOrNull(offsets[94]), - mangaHomeDisplayType: - _SettingsmangaHomeDisplayTypeValueEnumMap[reader.readByteOrNull( - offsets[95], - )] ?? - DisplayType.comfortableGrid, - markEpisodeAsSeenType: reader.readLongOrNull(offsets[96]), - mergeLibraryNavMobile: reader.readBoolOrNull(offsets[97]), - navigationOrder: reader.readStringList(offsets[98]), - novelDisplayType: - _SettingsnovelDisplayTypeValueEnumMap[reader.readByteOrNull( - offsets[99], - )] ?? - DisplayType.comfortableGrid, - novelExtensionsRepo: reader.readObjectList( offsets[100], RepoSchema.deserialize, allOffsets, Repo(), ), - novelFontSize: reader.readLongOrNull(offsets[101]), - novelLibraryDownloadedChapters: reader.readBoolOrNull(offsets[103]), - novelLibraryLocalSource: reader.readBoolOrNull(offsets[104]), - novelLibraryShowCategoryTabs: reader.readBoolOrNull(offsets[105]), - novelLibraryShowContinueReadingButton: reader.readBoolOrNull(offsets[106]), - novelLibraryShowLanguage: reader.readBoolOrNull(offsets[107]), - novelLibraryShowNumbersOfItems: reader.readBoolOrNull(offsets[108]), - novelReaderLineHeight: reader.readDoubleOrNull(offsets[109]), - novelReaderPadding: reader.readLongOrNull(offsets[110]), - novelReaderTextColor: reader.readStringOrNull(offsets[111]), - novelReaderTheme: reader.readStringOrNull(offsets[112]), - novelRemoveExtraParagraphSpacing: reader.readBoolOrNull(offsets[113]), - novelShowScrollPercentage: reader.readBoolOrNull(offsets[114]), - novelTapToScroll: reader.readBoolOrNull(offsets[115]), + mangaGridSize: reader.readLongOrNull(offsets[101]), + mangaHomeDisplayType: + _SettingsmangaHomeDisplayTypeValueEnumMap[reader.readByteOrNull( + offsets[102], + )] ?? + DisplayType.comfortableGrid, + markEpisodeAsSeenType: reader.readLongOrNull(offsets[103]), + mergeLibraryNavMobile: reader.readBoolOrNull(offsets[104]), + navigationOrder: reader.readStringList(offsets[105]), + novelDisplayType: + _SettingsnovelDisplayTypeValueEnumMap[reader.readByteOrNull( + offsets[106], + )] ?? + DisplayType.comfortableGrid, + novelExtensionsRepo: reader.readObjectList( + offsets[107], + RepoSchema.deserialize, + allOffsets, + Repo(), + ), + novelFontSize: reader.readLongOrNull(offsets[108]), + novelLibraryDownloadedChapters: reader.readBoolOrNull(offsets[110]), + novelLibraryLocalSource: reader.readBoolOrNull(offsets[111]), + novelLibraryShowCategoryTabs: reader.readBoolOrNull(offsets[112]), + novelLibraryShowContinueReadingButton: reader.readBoolOrNull(offsets[113]), + novelLibraryShowLanguage: reader.readBoolOrNull(offsets[114]), + novelLibraryShowNumbersOfItems: reader.readBoolOrNull(offsets[115]), + novelReaderLineHeight: reader.readDoubleOrNull(offsets[116]), + novelReaderPadding: reader.readLongOrNull(offsets[117]), + novelReaderTextColor: reader.readStringOrNull(offsets[118]), + novelReaderTheme: reader.readStringOrNull(offsets[119]), + novelRemoveExtraParagraphSpacing: reader.readBoolOrNull(offsets[120]), + novelShowScrollPercentage: reader.readBoolOrNull(offsets[121]), + novelTapToScroll: reader.readBoolOrNull(offsets[122]), novelTextAlign: _SettingsnovelTextAlignValueEnumMap[reader.readByteOrNull( - offsets[116], + offsets[123], )] ?? NovelTextAlign.left, - onlyIncludePinnedSources: reader.readBoolOrNull(offsets[117]), - pagePreloadAmount: reader.readLongOrNull(offsets[118]), + onlyIncludePinnedSources: reader.readBoolOrNull(offsets[124]), + pagePreloadAmount: reader.readLongOrNull(offsets[125]), personalPageModeList: reader.readObjectList( - offsets[119], + offsets[126], PersonalPageModeSchema.deserialize, allOffsets, PersonalPageMode(), ), personalReaderModeList: reader.readObjectList( - offsets[120], + offsets[127], PersonalReaderModeSchema.deserialize, allOffsets, PersonalReaderMode(), ), playerSubtitleSettings: reader.readObjectOrNull( - offsets[121], + offsets[128], PlayerSubtitleSettingsSchema.deserialize, allOffsets, ), - pureBlackDarkMode: reader.readBoolOrNull(offsets[122]), - relativeTimesTamps: reader.readLongOrNull(offsets[123]), - rpcShowCoverImage: reader.readBoolOrNull(offsets[124]), - rpcShowReadingWatchingProgress: reader.readBoolOrNull(offsets[125]), - rpcShowTitle: reader.readBoolOrNull(offsets[126]), - saveAsCBZArchive: reader.readBoolOrNull(offsets[127]), + pureBlackDarkMode: reader.readBoolOrNull(offsets[129]), + relativeTimesTamps: reader.readLongOrNull(offsets[130]), + rpcShowCoverImage: reader.readBoolOrNull(offsets[131]), + rpcShowReadingWatchingProgress: reader.readBoolOrNull(offsets[132]), + rpcShowTitle: reader.readBoolOrNull(offsets[133]), + saveAsCBZArchive: reader.readBoolOrNull(offsets[134]), scaleType: - _SettingsscaleTypeValueEnumMap[reader.readByteOrNull(offsets[128])] ?? + _SettingsscaleTypeValueEnumMap[reader.readByteOrNull(offsets[135])] ?? ScaleType.fitScreen, - showPagesNumber: reader.readBoolOrNull(offsets[129]), + showPagesNumber: reader.readBoolOrNull(offsets[136]), sortChapterList: reader.readObjectList( - offsets[130], + offsets[137], SortChapterSchema.deserialize, allOffsets, SortChapter(), ), sortLibraryAnime: reader.readObjectOrNull( - offsets[131], + offsets[138], SortLibraryMangaSchema.deserialize, allOffsets, ), sortLibraryManga: reader.readObjectOrNull( - offsets[132], + offsets[139], SortLibraryMangaSchema.deserialize, allOffsets, ), sortLibraryNovel: reader.readObjectOrNull( - offsets[133], + offsets[140], SortLibraryMangaSchema.deserialize, allOffsets, ), - startDatebackup: reader.readLongOrNull(offsets[134]), - themeIsDark: reader.readBoolOrNull(offsets[135]), - updateProgressAfterReading: reader.readBoolOrNull(offsets[136]), - updatedAt: reader.readLongOrNull(offsets[137]), - useLibass: reader.readBoolOrNull(offsets[138]), - useMpvConfig: reader.readBoolOrNull(offsets[139]), - usePageTapZones: reader.readBoolOrNull(offsets[140]), - useYUV420P: reader.readBoolOrNull(offsets[141]), - userAgent: reader.readStringOrNull(offsets[142]), - volumeBoostCap: reader.readLongOrNull(offsets[143]), + startDatebackup: reader.readLongOrNull(offsets[141]), + themeIsDark: reader.readBoolOrNull(offsets[142]), + updateProgressAfterReading: reader.readBoolOrNull(offsets[143]), + updatedAt: reader.readLongOrNull(offsets[144]), + useLibass: reader.readBoolOrNull(offsets[145]), + useMpvConfig: reader.readBoolOrNull(offsets[146]), + usePageTapZones: reader.readBoolOrNull(offsets[147]), + useYUV420P: reader.readBoolOrNull(offsets[148]), + userAgent: reader.readStringOrNull(offsets[149]), + volumeBoostCap: reader.readLongOrNull(offsets[150]), ); object.chapterFilterBookmarkedList = reader .readObjectList( - offsets[24], + offsets[25], ChapterFilterBookmarkedSchema.deserialize, allOffsets, ChapterFilterBookmarked(), ); object.chapterFilterUnreadList = reader.readObjectList( - offsets[26], + offsets[27], ChapterFilterUnreadSchema.deserialize, allOffsets, ChapterFilterUnread(), ); object.defaultSubtitleLang = reader.readObjectOrNull( - offsets[44], + offsets[45], L10nLocaleSchema.deserialize, allOffsets, ); object.filterScanlatorList = reader.readObjectList( - offsets[61], + offsets[62], FilterScanlatorSchema.deserialize, allOffsets, FilterScanlator(), ); object.locale = reader.readObjectOrNull( - offsets[92], + offsets[99], L10nLocaleSchema.deserialize, allOffsets, ); - object.novelGridSize = reader.readLongOrNull(offsets[102]); + object.novelGridSize = reader.readLongOrNull(offsets[109]); return object; } @@ -1898,18 +1947,20 @@ P _settingsDeserializeProp

( case 13: return (reader.readStringOrNull(offset)) as P; case 14: + return (reader.readBoolOrNull(offset)) as P; + case 15: return (_SettingsaudioChannelsValueEnumMap[reader.readByteOrNull( offset, )] ?? AudioChannel.autoSafe) as P; - case 15: - return (reader.readStringOrNull(offset)) as P; case 16: return (reader.readStringOrNull(offset)) as P; case 17: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readStringOrNull(offset)) as P; case 18: + return (reader.readBoolOrNull(offset)) as P; + case 19: return (reader.readObjectList( offset, AutoScrollPagesSchema.deserialize, @@ -1917,21 +1968,21 @@ P _settingsDeserializeProp

( AutoScrollPages(), )) as P; - case 19: + case 20: return (_SettingsbackgroundColorValueEnumMap[reader.readByteOrNull( offset, )] ?? BackgroundColor.black) as P; - case 20: - return (reader.readLongOrNull(offset)) as P; case 21: - return (reader.readLongList(offset)) as P; - case 22: - return (reader.readStringOrNull(offset)) as P; - case 23: return (reader.readLongOrNull(offset)) as P; + case 22: + return (reader.readLongList(offset)) as P; + case 23: + return (reader.readStringOrNull(offset)) as P; case 24: + return (reader.readLongOrNull(offset)) as P; + case 25: return (reader.readObjectList( offset, ChapterFilterBookmarkedSchema.deserialize, @@ -1939,7 +1990,7 @@ P _settingsDeserializeProp

( ChapterFilterBookmarked(), )) as P; - case 25: + case 26: return (reader.readObjectList( offset, ChapterFilterDownloadedSchema.deserialize, @@ -1947,7 +1998,7 @@ P _settingsDeserializeProp

( ChapterFilterDownloaded(), )) as P; - case 26: + case 27: return (reader.readObjectList( offset, ChapterFilterUnreadSchema.deserialize, @@ -1955,7 +2006,7 @@ P _settingsDeserializeProp

( ChapterFilterUnread(), )) as P; - case 27: + case 28: return (reader.readObjectList( offset, ChapterPageIndexSchema.deserialize, @@ -1963,7 +2014,7 @@ P _settingsDeserializeProp

( ChapterPageIndex(), )) as P; - case 28: + case 29: return (reader.readObjectList( offset, ChapterPageurlsSchema.deserialize, @@ -1971,21 +2022,21 @@ P _settingsDeserializeProp

( ChapterPageurls(), )) as P; - case 29: - return (reader.readBoolOrNull(offset)) as P; case 30: return (reader.readBoolOrNull(offset)) as P; case 31: return (reader.readBoolOrNull(offset)) as P; case 32: + return (reader.readBoolOrNull(offset)) as P; + case 33: return (_SettingscolorFilterBlendModeValueEnumMap[reader.readByteOrNull( offset, )] ?? ColorFilterBlendMode.none) as P; - case 33: - return (reader.readLongOrNull(offset)) as P; case 34: + return (reader.readLongOrNull(offset)) as P; + case 35: return (reader.readObjectList( offset, MCookieSchema.deserialize, @@ -1993,64 +2044,62 @@ P _settingsDeserializeProp

( MCookie(), )) as P; - case 35: - return (reader.readBoolOrNull(offset)) as P; case 36: + return (reader.readBoolOrNull(offset)) as P; + case 37: return (reader.readObjectOrNull( offset, CustomColorFilterSchema.deserialize, allOffsets, )) as P; - case 37: - return (reader.readStringOrNull(offset)) as P; case 38: return (reader.readStringOrNull(offset)) as P; case 39: + return (reader.readStringOrNull(offset)) as P; + case 40: return (_SettingsdebandingTypeValueEnumMap[reader.readByteOrNull( offset, )] ?? DebandingType.none) as P; - case 40: - return (reader.readLongOrNull(offset)) as P; case 41: - return (reader.readDoubleOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 42: + return (reader.readDoubleOrNull(offset)) as P; + case 43: return (_SettingsdefaultReaderModeValueEnumMap[reader.readByteOrNull( offset, )] ?? ReaderMode.vertical) as P; - case 43: - return (reader.readLongOrNull(offset)) as P; case 44: + return (reader.readLongOrNull(offset)) as P; + case 45: return (reader.readObjectOrNull( offset, L10nLocaleSchema.deserialize, allOffsets, )) as P; - case 45: + case 46: return (_SettingsdisableSectionTypeValueEnumMap[reader.readByteOrNull( offset, )] ?? SectionType.all) as P; - case 46: + case 47: return (_SettingsdisplayTypeValueEnumMap[reader.readByteOrNull(offset)] ?? DisplayType.compactGrid) as P; - case 47: - return (reader.readBoolOrNull(offset)) as P; case 48: - return (reader.readLongOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 49: return (reader.readLongOrNull(offset)) as P; case 50: - return (reader.readStringOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 51: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readStringOrNull(offset)) as P; case 52: return (reader.readBoolOrNull(offset)) as P; case 53: @@ -2070,6 +2119,8 @@ P _settingsDeserializeProp

( case 60: return (reader.readBoolOrNull(offset)) as P; case 61: + return (reader.readBoolOrNull(offset)) as P; + case 62: return (reader.readObjectList( offset, FilterScanlatorSchema.deserialize, @@ -2077,12 +2128,10 @@ P _settingsDeserializeProp

( FilterScanlator(), )) as P; - case 62: - return (reader.readDoubleOrNull(offset)) as P; case 63: - return (reader.readLongOrNull(offset)) as P; + return (reader.readDoubleOrNull(offset)) as P; case 64: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 65: return (reader.readBoolOrNull(offset)) as P; case 66: @@ -2092,17 +2141,17 @@ P _settingsDeserializeProp

( case 68: return (reader.readBoolOrNull(offset)) as P; case 69: - return (reader.readStringList(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 70: - return (reader.readStringOrNull(offset)) as P; + return (reader.readStringList(offset)) as P; case 71: - return (reader.readBoolOrNull(offset)) as P; - case 72: return (reader.readStringOrNull(offset)) as P; - case 73: + case 72: return (reader.readBoolOrNull(offset)) as P; + case 73: + return (reader.readStringOrNull(offset)) as P; case 74: - return (reader.readLongOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 75: return (reader.readLongOrNull(offset)) as P; case 76: @@ -2126,51 +2175,37 @@ P _settingsDeserializeProp

( case 85: return (reader.readLongOrNull(offset)) as P; case 86: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 87: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 88: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 89: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 90: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 91: - return (reader.readStringList(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 92: - return (reader.readObjectOrNull( - offset, - L10nLocaleSchema.deserialize, - allOffsets, - )) - as P; + return (reader.readLongOrNull(offset)) as P; case 93: - return (reader.readObjectList( - offset, - RepoSchema.deserialize, - allOffsets, - Repo(), - )) - as P; + return (reader.readBoolOrNull(offset)) as P; case 94: - return (reader.readLongOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 95: - return (_SettingsmangaHomeDisplayTypeValueEnumMap[reader.readByteOrNull( - offset, - )] ?? - DisplayType.comfortableGrid) - as P; + return (reader.readBoolOrNull(offset)) as P; case 96: - return (reader.readLongOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 97: return (reader.readBoolOrNull(offset)) as P; case 98: return (reader.readStringList(offset)) as P; case 99: - return (_SettingsnovelDisplayTypeValueEnumMap[reader.readByteOrNull( - offset, - )] ?? - DisplayType.comfortableGrid) + return (reader.readObjectOrNull( + offset, + L10nLocaleSchema.deserialize, + allOffsets, + )) as P; case 100: return (reader.readObjectList( @@ -2183,27 +2218,41 @@ P _settingsDeserializeProp

( case 101: return (reader.readLongOrNull(offset)) as P; case 102: - return (reader.readLongOrNull(offset)) as P; + return (_SettingsmangaHomeDisplayTypeValueEnumMap[reader.readByteOrNull( + offset, + )] ?? + DisplayType.comfortableGrid) + as P; case 103: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 104: return (reader.readBoolOrNull(offset)) as P; case 105: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readStringList(offset)) as P; case 106: - return (reader.readBoolOrNull(offset)) as P; + return (_SettingsnovelDisplayTypeValueEnumMap[reader.readByteOrNull( + offset, + )] ?? + DisplayType.comfortableGrid) + as P; case 107: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readObjectList( + offset, + RepoSchema.deserialize, + allOffsets, + Repo(), + )) + as P; case 108: - return (reader.readBoolOrNull(offset)) as P; - case 109: - return (reader.readDoubleOrNull(offset)) as P; - case 110: return (reader.readLongOrNull(offset)) as P; + case 109: + return (reader.readLongOrNull(offset)) as P; + case 110: + return (reader.readBoolOrNull(offset)) as P; case 111: - return (reader.readStringOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 112: - return (reader.readStringOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 113: return (reader.readBoolOrNull(offset)) as P; case 114: @@ -2211,16 +2260,30 @@ P _settingsDeserializeProp

( case 115: return (reader.readBoolOrNull(offset)) as P; case 116: + return (reader.readDoubleOrNull(offset)) as P; + case 117: + return (reader.readLongOrNull(offset)) as P; + case 118: + return (reader.readStringOrNull(offset)) as P; + case 119: + return (reader.readStringOrNull(offset)) as P; + case 120: + return (reader.readBoolOrNull(offset)) as P; + case 121: + return (reader.readBoolOrNull(offset)) as P; + case 122: + return (reader.readBoolOrNull(offset)) as P; + case 123: return (_SettingsnovelTextAlignValueEnumMap[reader.readByteOrNull( offset, )] ?? NovelTextAlign.left) as P; - case 117: + case 124: return (reader.readBoolOrNull(offset)) as P; - case 118: + case 125: return (reader.readLongOrNull(offset)) as P; - case 119: + case 126: return (reader.readObjectList( offset, PersonalPageModeSchema.deserialize, @@ -2228,7 +2291,7 @@ P _settingsDeserializeProp

( PersonalPageMode(), )) as P; - case 120: + case 127: return (reader.readObjectList( offset, PersonalReaderModeSchema.deserialize, @@ -2236,32 +2299,32 @@ P _settingsDeserializeProp

( PersonalReaderMode(), )) as P; - case 121: + case 128: return (reader.readObjectOrNull( offset, PlayerSubtitleSettingsSchema.deserialize, allOffsets, )) as P; - case 122: - return (reader.readBoolOrNull(offset)) as P; - case 123: - return (reader.readLongOrNull(offset)) as P; - case 124: - return (reader.readBoolOrNull(offset)) as P; - case 125: - return (reader.readBoolOrNull(offset)) as P; - case 126: - return (reader.readBoolOrNull(offset)) as P; - case 127: - return (reader.readBoolOrNull(offset)) as P; - case 128: - return (_SettingsscaleTypeValueEnumMap[reader.readByteOrNull(offset)] ?? - ScaleType.fitScreen) - as P; case 129: return (reader.readBoolOrNull(offset)) as P; case 130: + return (reader.readLongOrNull(offset)) as P; + case 131: + return (reader.readBoolOrNull(offset)) as P; + case 132: + return (reader.readBoolOrNull(offset)) as P; + case 133: + return (reader.readBoolOrNull(offset)) as P; + case 134: + return (reader.readBoolOrNull(offset)) as P; + case 135: + return (_SettingsscaleTypeValueEnumMap[reader.readByteOrNull(offset)] ?? + ScaleType.fitScreen) + as P; + case 136: + return (reader.readBoolOrNull(offset)) as P; + case 137: return (reader.readObjectList( offset, SortChapterSchema.deserialize, @@ -2269,46 +2332,46 @@ P _settingsDeserializeProp

( SortChapter(), )) as P; - case 131: - return (reader.readObjectOrNull( - offset, - SortLibraryMangaSchema.deserialize, - allOffsets, - )) - as P; - case 132: - return (reader.readObjectOrNull( - offset, - SortLibraryMangaSchema.deserialize, - allOffsets, - )) - as P; - case 133: - return (reader.readObjectOrNull( - offset, - SortLibraryMangaSchema.deserialize, - allOffsets, - )) - as P; - case 134: - return (reader.readLongOrNull(offset)) as P; - case 135: - return (reader.readBoolOrNull(offset)) as P; - case 136: - return (reader.readBoolOrNull(offset)) as P; - case 137: - return (reader.readLongOrNull(offset)) as P; case 138: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readObjectOrNull( + offset, + SortLibraryMangaSchema.deserialize, + allOffsets, + )) + as P; case 139: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readObjectOrNull( + offset, + SortLibraryMangaSchema.deserialize, + allOffsets, + )) + as P; case 140: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readObjectOrNull( + offset, + SortLibraryMangaSchema.deserialize, + allOffsets, + )) + as P; case 141: - return (reader.readBoolOrNull(offset)) as P; + return (reader.readLongOrNull(offset)) as P; case 142: - return (reader.readStringOrNull(offset)) as P; + return (reader.readBoolOrNull(offset)) as P; case 143: + return (reader.readBoolOrNull(offset)) as P; + case 144: + return (reader.readLongOrNull(offset)) as P; + case 145: + return (reader.readBoolOrNull(offset)) as P; + case 146: + return (reader.readBoolOrNull(offset)) as P; + case 147: + return (reader.readBoolOrNull(offset)) as P; + case 148: + return (reader.readBoolOrNull(offset)) as P; + case 149: + return (reader.readStringOrNull(offset)) as P; + case 150: return (reader.readLongOrNull(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); @@ -3428,6 +3491,34 @@ extension SettingsQueryFilter }); } + QueryBuilder + appLockEnabledIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull(property: r'appLockEnabled'), + ); + }); + } + + QueryBuilder + appLockEnabledIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'appLockEnabled'), + ); + }); + } + + QueryBuilder appLockEnabledEqualTo( + bool? value, + ) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo(property: r'appLockEnabled', value: value), + ); + }); + } + QueryBuilder audioChannelsEqualTo( AudioChannel value, ) { @@ -7825,6 +7916,89 @@ extension SettingsQueryFilter }); } + QueryBuilder + libraryFilterAnimeCompletedTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull( + property: r'libraryFilterAnimeCompletedType', + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeCompletedTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'libraryFilterAnimeCompletedType', + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeCompletedTypeEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'libraryFilterAnimeCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeCompletedTypeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'libraryFilterAnimeCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeCompletedTypeLessThan(int? value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'libraryFilterAnimeCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeCompletedTypeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.between( + property: r'libraryFilterAnimeCompletedType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); + }); + } + QueryBuilder libraryFilterAnimeDownloadTypeIsNull() { return QueryBuilder.apply(this, (query) { @@ -7988,6 +8162,89 @@ extension SettingsQueryFilter }); } + QueryBuilder + libraryFilterAnimeTrackingTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull( + property: r'libraryFilterAnimeTrackingType', + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeTrackingTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'libraryFilterAnimeTrackingType', + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeTrackingTypeEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'libraryFilterAnimeTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeTrackingTypeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'libraryFilterAnimeTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeTrackingTypeLessThan(int? value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'libraryFilterAnimeTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterAnimeTrackingTypeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.between( + property: r'libraryFilterAnimeTrackingType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); + }); + } + QueryBuilder libraryFilterAnimeUnreadTypeIsNull() { return QueryBuilder.apply(this, (query) { @@ -8152,6 +8409,89 @@ extension SettingsQueryFilter }); } + QueryBuilder + libraryFilterMangasCompletedTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull( + property: r'libraryFilterMangasCompletedType', + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasCompletedTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'libraryFilterMangasCompletedType', + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasCompletedTypeEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'libraryFilterMangasCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasCompletedTypeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'libraryFilterMangasCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasCompletedTypeLessThan(int? value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'libraryFilterMangasCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasCompletedTypeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.between( + property: r'libraryFilterMangasCompletedType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); + }); + } + QueryBuilder libraryFilterMangasDownloadTypeIsNull() { return QueryBuilder.apply(this, (query) { @@ -8318,6 +8658,89 @@ extension SettingsQueryFilter }); } + QueryBuilder + libraryFilterMangasTrackingTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull( + property: r'libraryFilterMangasTrackingType', + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasTrackingTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'libraryFilterMangasTrackingType', + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasTrackingTypeEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'libraryFilterMangasTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasTrackingTypeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'libraryFilterMangasTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasTrackingTypeLessThan(int? value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'libraryFilterMangasTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterMangasTrackingTypeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.between( + property: r'libraryFilterMangasTrackingType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); + }); + } + QueryBuilder libraryFilterMangasUnreadTypeIsNull() { return QueryBuilder.apply(this, (query) { @@ -8481,6 +8904,89 @@ extension SettingsQueryFilter }); } + QueryBuilder + libraryFilterNovelCompletedTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull( + property: r'libraryFilterNovelCompletedType', + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelCompletedTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'libraryFilterNovelCompletedType', + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelCompletedTypeEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'libraryFilterNovelCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelCompletedTypeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'libraryFilterNovelCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelCompletedTypeLessThan(int? value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'libraryFilterNovelCompletedType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelCompletedTypeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.between( + property: r'libraryFilterNovelCompletedType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); + }); + } + QueryBuilder libraryFilterNovelDownloadTypeIsNull() { return QueryBuilder.apply(this, (query) { @@ -8644,6 +9150,89 @@ extension SettingsQueryFilter }); } + QueryBuilder + libraryFilterNovelTrackingTypeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNull( + property: r'libraryFilterNovelTrackingType', + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelTrackingTypeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'libraryFilterNovelTrackingType', + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelTrackingTypeEqualTo(int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'libraryFilterNovelTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelTrackingTypeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'libraryFilterNovelTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelTrackingTypeLessThan(int? value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'libraryFilterNovelTrackingType', + value: value, + ), + ); + }); + } + + QueryBuilder + libraryFilterNovelTrackingTypeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.between( + property: r'libraryFilterNovelTrackingType', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); + }); + } + QueryBuilder libraryFilterNovelUnreadTypeIsNull() { return QueryBuilder.apply(this, (query) { @@ -12439,6 +13028,18 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder sortByAppLockEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'appLockEnabled', Sort.asc); + }); + } + + QueryBuilder sortByAppLockEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'appLockEnabled', Sort.desc); + }); + } + QueryBuilder sortByAudioChannels() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'audioChannels', Sort.asc); @@ -13066,6 +13667,20 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder + sortByLibraryFilterAnimeCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeCompletedType', Sort.asc); + }); + } + + QueryBuilder + sortByLibraryFilterAnimeCompletedTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeCompletedType', Sort.desc); + }); + } + QueryBuilder sortByLibraryFilterAnimeDownloadType() { return QueryBuilder.apply(this, (query) { @@ -13094,6 +13709,20 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder + sortByLibraryFilterAnimeTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeTrackingType', Sort.asc); + }); + } + + QueryBuilder + sortByLibraryFilterAnimeTrackingTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeTrackingType', Sort.desc); + }); + } + QueryBuilder sortByLibraryFilterAnimeUnreadType() { return QueryBuilder.apply(this, (query) { @@ -13122,6 +13751,20 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder + sortByLibraryFilterMangasCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasCompletedType', Sort.asc); + }); + } + + QueryBuilder + sortByLibraryFilterMangasCompletedTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasCompletedType', Sort.desc); + }); + } + QueryBuilder sortByLibraryFilterMangasDownloadType() { return QueryBuilder.apply(this, (query) { @@ -13150,6 +13793,20 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder + sortByLibraryFilterMangasTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasTrackingType', Sort.asc); + }); + } + + QueryBuilder + sortByLibraryFilterMangasTrackingTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasTrackingType', Sort.desc); + }); + } + QueryBuilder sortByLibraryFilterMangasUnreadType() { return QueryBuilder.apply(this, (query) { @@ -13178,6 +13835,20 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder + sortByLibraryFilterNovelCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelCompletedType', Sort.asc); + }); + } + + QueryBuilder + sortByLibraryFilterNovelCompletedTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelCompletedType', Sort.desc); + }); + } + QueryBuilder sortByLibraryFilterNovelDownloadType() { return QueryBuilder.apply(this, (query) { @@ -13206,6 +13877,20 @@ extension SettingsQuerySortBy on QueryBuilder { }); } + QueryBuilder + sortByLibraryFilterNovelTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelTrackingType', Sort.asc); + }); + } + + QueryBuilder + sortByLibraryFilterNovelTrackingTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelTrackingType', Sort.desc); + }); + } + QueryBuilder sortByLibraryFilterNovelUnreadType() { return QueryBuilder.apply(this, (query) { @@ -13984,6 +14669,18 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder thenByAppLockEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'appLockEnabled', Sort.asc); + }); + } + + QueryBuilder thenByAppLockEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'appLockEnabled', Sort.desc); + }); + } + QueryBuilder thenByAudioChannels() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'audioChannels', Sort.asc); @@ -14623,6 +15320,20 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder + thenByLibraryFilterAnimeCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeCompletedType', Sort.asc); + }); + } + + QueryBuilder + thenByLibraryFilterAnimeCompletedTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeCompletedType', Sort.desc); + }); + } + QueryBuilder thenByLibraryFilterAnimeDownloadType() { return QueryBuilder.apply(this, (query) { @@ -14651,6 +15362,20 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder + thenByLibraryFilterAnimeTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeTrackingType', Sort.asc); + }); + } + + QueryBuilder + thenByLibraryFilterAnimeTrackingTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterAnimeTrackingType', Sort.desc); + }); + } + QueryBuilder thenByLibraryFilterAnimeUnreadType() { return QueryBuilder.apply(this, (query) { @@ -14679,6 +15404,20 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder + thenByLibraryFilterMangasCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasCompletedType', Sort.asc); + }); + } + + QueryBuilder + thenByLibraryFilterMangasCompletedTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasCompletedType', Sort.desc); + }); + } + QueryBuilder thenByLibraryFilterMangasDownloadType() { return QueryBuilder.apply(this, (query) { @@ -14707,6 +15446,20 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder + thenByLibraryFilterMangasTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasTrackingType', Sort.asc); + }); + } + + QueryBuilder + thenByLibraryFilterMangasTrackingTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterMangasTrackingType', Sort.desc); + }); + } + QueryBuilder thenByLibraryFilterMangasUnreadType() { return QueryBuilder.apply(this, (query) { @@ -14735,6 +15488,20 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder + thenByLibraryFilterNovelCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelCompletedType', Sort.asc); + }); + } + + QueryBuilder + thenByLibraryFilterNovelCompletedTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelCompletedType', Sort.desc); + }); + } + QueryBuilder thenByLibraryFilterNovelDownloadType() { return QueryBuilder.apply(this, (query) { @@ -14763,6 +15530,20 @@ extension SettingsQuerySortThenBy }); } + QueryBuilder + thenByLibraryFilterNovelTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelTrackingType', Sort.asc); + }); + } + + QueryBuilder + thenByLibraryFilterNovelTrackingTypeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'libraryFilterNovelTrackingType', Sort.desc); + }); + } + QueryBuilder thenByLibraryFilterNovelUnreadType() { return QueryBuilder.apply(this, (query) { @@ -15464,6 +16245,12 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder distinctByAppLockEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'appLockEnabled'); + }); + } + QueryBuilder distinctByAudioChannels() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'audioChannels'); @@ -15812,6 +16599,13 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder + distinctByLibraryFilterAnimeCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'libraryFilterAnimeCompletedType'); + }); + } + QueryBuilder distinctByLibraryFilterAnimeDownloadType() { return QueryBuilder.apply(this, (query) { @@ -15826,6 +16620,13 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder + distinctByLibraryFilterAnimeTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'libraryFilterAnimeTrackingType'); + }); + } + QueryBuilder distinctByLibraryFilterAnimeUnreadType() { return QueryBuilder.apply(this, (query) { @@ -15840,6 +16641,13 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder + distinctByLibraryFilterMangasCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'libraryFilterMangasCompletedType'); + }); + } + QueryBuilder distinctByLibraryFilterMangasDownloadType() { return QueryBuilder.apply(this, (query) { @@ -15854,6 +16662,13 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder + distinctByLibraryFilterMangasTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'libraryFilterMangasTrackingType'); + }); + } + QueryBuilder distinctByLibraryFilterMangasUnreadType() { return QueryBuilder.apply(this, (query) { @@ -15868,6 +16683,13 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder + distinctByLibraryFilterNovelCompletedType() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'libraryFilterNovelCompletedType'); + }); + } + QueryBuilder distinctByLibraryFilterNovelDownloadType() { return QueryBuilder.apply(this, (query) { @@ -15882,6 +16704,13 @@ extension SettingsQueryWhereDistinct }); } + QueryBuilder + distinctByLibraryFilterNovelTrackingType() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'libraryFilterNovelTrackingType'); + }); + } + QueryBuilder distinctByLibraryFilterNovelUnreadType() { return QueryBuilder.apply(this, (query) { @@ -16311,6 +17140,12 @@ extension SettingsQueryProperty }); } + QueryBuilder appLockEnabledProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'appLockEnabled'); + }); + } + QueryBuilder audioChannelsProperty() { return QueryBuilder.apply(this, (query) { @@ -16713,6 +17548,13 @@ extension SettingsQueryProperty }); } + QueryBuilder + libraryFilterAnimeCompletedTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'libraryFilterAnimeCompletedType'); + }); + } + QueryBuilder libraryFilterAnimeDownloadTypeProperty() { return QueryBuilder.apply(this, (query) { @@ -16727,6 +17569,13 @@ extension SettingsQueryProperty }); } + QueryBuilder + libraryFilterAnimeTrackingTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'libraryFilterAnimeTrackingType'); + }); + } + QueryBuilder libraryFilterAnimeUnreadTypeProperty() { return QueryBuilder.apply(this, (query) { @@ -16741,6 +17590,13 @@ extension SettingsQueryProperty }); } + QueryBuilder + libraryFilterMangasCompletedTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'libraryFilterMangasCompletedType'); + }); + } + QueryBuilder libraryFilterMangasDownloadTypeProperty() { return QueryBuilder.apply(this, (query) { @@ -16755,6 +17611,13 @@ extension SettingsQueryProperty }); } + QueryBuilder + libraryFilterMangasTrackingTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'libraryFilterMangasTrackingType'); + }); + } + QueryBuilder libraryFilterMangasUnreadTypeProperty() { return QueryBuilder.apply(this, (query) { @@ -16769,6 +17632,13 @@ extension SettingsQueryProperty }); } + QueryBuilder + libraryFilterNovelCompletedTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'libraryFilterNovelCompletedType'); + }); + } + QueryBuilder libraryFilterNovelDownloadTypeProperty() { return QueryBuilder.apply(this, (query) { @@ -16783,6 +17653,13 @@ extension SettingsQueryProperty }); } + QueryBuilder + libraryFilterNovelTrackingTypeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'libraryFilterNovelTrackingType'); + }); + } + QueryBuilder libraryFilterNovelUnreadTypeProperty() { return QueryBuilder.apply(this, (query) { diff --git a/lib/modules/library/library_screen.dart b/lib/modules/library/library_screen.dart index e21cd60d..09689416 100644 --- a/lib/modules/library/library_screen.dart +++ b/lib/modules/library/library_screen.dart @@ -159,6 +159,12 @@ class _LibraryScreenState extends ConsumerState final bookmarkedFilterType = watchWithSettingsAndManga( mangaFilterBookmarkedStateProvider.call, ); + final completedFilterType = watchWithSettingsAndManga( + mangaFilterCompletedStateProvider.call, + ); + final trackingFilterType = watchWithSettingsAndManga( + mangaFilterTrackingStateProvider.call, + ); final sortType = watchWithSettings(sortLibraryMangaStateProvider.call).index as int; @@ -174,6 +180,8 @@ class _LibraryScreenState extends ConsumerState unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, reverse: reverse, downloadedChapter: downloadedChapter, continueReaderBtn: continueReaderBtn, @@ -195,6 +203,8 @@ class _LibraryScreenState extends ConsumerState unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, settings: settings, downloadedOnly: downloadedOnly, searchQuery: searchQuery, @@ -217,6 +227,8 @@ class _LibraryScreenState extends ConsumerState unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, sortType: sortType, downloadedOnly: downloadedOnly, searchQuery: searchQuery, diff --git a/lib/modules/library/providers/library_filter_provider.dart b/lib/modules/library/providers/library_filter_provider.dart index 8c04e880..e1173c20 100644 --- a/lib/modules/library/providers/library_filter_provider.dart +++ b/lib/modules/library/providers/library_filter_provider.dart @@ -2,6 +2,7 @@ import 'package:isar_community/isar.dart'; import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/download.dart'; import 'package:mangayomi/models/manga.dart'; +import 'package:mangayomi/models/track.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'library_filter_provider.g.dart'; @@ -17,10 +18,14 @@ Set downloadedChapterIds(Ref ref) { return downloads.whereType().toSet(); } +/// Pre-fetches all manga IDs that have at least one tracking entry. +@riverpod +Set trackedMangaIds(Ref ref) { + final tracks = isar.tracks.where().findAllSync(); + return tracks.map((t) => t.mangaId).whereType().toSet(); +} + /// Filters and sorts a list of [Manga] based on library filter/sort settings. -/// -/// Uses [downloadedChapterIds] for O(1) download lookups instead of -/// per-chapter Isar queries (previous behavior was O(chapters × manga)). @riverpod List filteredLibraryManga( Ref ref, { @@ -29,12 +34,15 @@ List filteredLibraryManga( required int unreadFilterType, required int startedFilterType, required int bookmarkedFilterType, + required int completedFilterType, + required int trackingFilterType, required int sortType, required bool downloadedOnly, required String searchQuery, required bool ignoreFiltersOnSearch, }) { final downloadedIds = ref.watch(downloadedChapterIdsProvider); + final trackedIds = ref.watch(trackedMangaIdsProvider); return _filterAndSortManga( data: data, @@ -42,11 +50,14 @@ List filteredLibraryManga( unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, sortType: sortType, downloadedOnly: downloadedOnly, searchQuery: searchQuery, ignoreFiltersOnSearch: ignoreFiltersOnSearch, downloadedIds: downloadedIds, + trackedIds: trackedIds, ); } @@ -71,11 +82,14 @@ List _filterAndSortManga({ required int unreadFilterType, required int startedFilterType, required int bookmarkedFilterType, + required int completedFilterType, + required int trackingFilterType, required int sortType, required bool downloadedOnly, required String searchQuery, required bool ignoreFiltersOnSearch, required Set downloadedIds, + required Set trackedIds, }) { List mangas; @@ -121,6 +135,24 @@ List _filterAndSortManga({ if (!allNotBookmarked) return false; } + // Filter by completed status + if (completedFilterType == 1) { + if (element.status != Status.completed) return false; + } else if (completedFilterType == 2) { + if (element.status == Status.completed) return false; + } + + // Filter by tracking + if (trackingFilterType == 1) { + if (element.id == null || !trackedIds.contains(element.id)) { + return false; + } + } else if (trackingFilterType == 2) { + if (element.id != null && trackedIds.contains(element.id)) { + return false; + } + } + // Search filter if (searchQuery.isNotEmpty) { if (!_matchesSearchQuery(element, searchQuery)) return false; diff --git a/lib/modules/library/providers/library_filter_provider.g.dart b/lib/modules/library/providers/library_filter_provider.g.dart index d2a560a3..2101f3ee 100644 --- a/lib/modules/library/providers/library_filter_provider.g.dart +++ b/lib/modules/library/providers/library_filter_provider.g.dart @@ -58,26 +58,63 @@ final class DownloadedChapterIdsProvider String _$downloadedChapterIdsHash() => r'a51ff78fb0ad2548c719d1ca400ae474fc01e683'; +/// Pre-fetches all manga IDs that have at least one tracking entry. + +@ProviderFor(trackedMangaIds) +final trackedMangaIdsProvider = TrackedMangaIdsProvider._(); + +/// Pre-fetches all manga IDs that have at least one tracking entry. + +final class TrackedMangaIdsProvider + extends $FunctionalProvider, Set, Set> + with $Provider> { + /// Pre-fetches all manga IDs that have at least one tracking entry. + TrackedMangaIdsProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'trackedMangaIdsProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$trackedMangaIdsHash(); + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(pointer); + + @override + Set create(Ref ref) { + return trackedMangaIds(ref); + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Set value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>(value), + ); + } +} + +String _$trackedMangaIdsHash() => r'8fd052ae3ff4e9fe47e66d5e24cd57233aa03d0a'; + /// Filters and sorts a list of [Manga] based on library filter/sort settings. -/// -/// Uses [downloadedChapterIds] for O(1) download lookups instead of -/// per-chapter Isar queries (previous behavior was O(chapters × manga)). @ProviderFor(filteredLibraryManga) final filteredLibraryMangaProvider = FilteredLibraryMangaFamily._(); /// Filters and sorts a list of [Manga] based on library filter/sort settings. -/// -/// Uses [downloadedChapterIds] for O(1) download lookups instead of -/// per-chapter Isar queries (previous behavior was O(chapters × manga)). final class FilteredLibraryMangaProvider extends $FunctionalProvider, List, List> with $Provider> { /// Filters and sorts a list of [Manga] based on library filter/sort settings. - /// - /// Uses [downloadedChapterIds] for O(1) download lookups instead of - /// per-chapter Isar queries (previous behavior was O(chapters × manga)). FilteredLibraryMangaProvider._({ required FilteredLibraryMangaFamily super.from, required ({ @@ -86,6 +123,8 @@ final class FilteredLibraryMangaProvider int unreadFilterType, int startedFilterType, int bookmarkedFilterType, + int completedFilterType, + int trackingFilterType, int sortType, bool downloadedOnly, String searchQuery, @@ -125,6 +164,8 @@ final class FilteredLibraryMangaProvider int unreadFilterType, int startedFilterType, int bookmarkedFilterType, + int completedFilterType, + int trackingFilterType, int sortType, bool downloadedOnly, String searchQuery, @@ -137,6 +178,8 @@ final class FilteredLibraryMangaProvider unreadFilterType: argument.unreadFilterType, startedFilterType: argument.startedFilterType, bookmarkedFilterType: argument.bookmarkedFilterType, + completedFilterType: argument.completedFilterType, + trackingFilterType: argument.trackingFilterType, sortType: argument.sortType, downloadedOnly: argument.downloadedOnly, searchQuery: argument.searchQuery, @@ -164,12 +207,9 @@ final class FilteredLibraryMangaProvider } String _$filteredLibraryMangaHash() => - r'34cd87ea154cc617e85572ede503b81fb36f2a97'; + r'afecb3de71f1f8c1682a0bfd9949f8a372c7d1b6'; /// Filters and sorts a list of [Manga] based on library filter/sort settings. -/// -/// Uses [downloadedChapterIds] for O(1) download lookups instead of -/// per-chapter Isar queries (previous behavior was O(chapters × manga)). final class FilteredLibraryMangaFamily extends $Family with @@ -181,6 +221,8 @@ final class FilteredLibraryMangaFamily extends $Family int unreadFilterType, int startedFilterType, int bookmarkedFilterType, + int completedFilterType, + int trackingFilterType, int sortType, bool downloadedOnly, String searchQuery, @@ -197,9 +239,6 @@ final class FilteredLibraryMangaFamily extends $Family ); /// Filters and sorts a list of [Manga] based on library filter/sort settings. - /// - /// Uses [downloadedChapterIds] for O(1) download lookups instead of - /// per-chapter Isar queries (previous behavior was O(chapters × manga)). FilteredLibraryMangaProvider call({ required List data, @@ -207,6 +246,8 @@ final class FilteredLibraryMangaFamily extends $Family required int unreadFilterType, required int startedFilterType, required int bookmarkedFilterType, + required int completedFilterType, + required int trackingFilterType, required int sortType, required bool downloadedOnly, required String searchQuery, @@ -218,6 +259,8 @@ final class FilteredLibraryMangaFamily extends $Family unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, sortType: sortType, downloadedOnly: downloadedOnly, searchQuery: searchQuery, diff --git a/lib/modules/library/providers/library_state_provider.dart b/lib/modules/library/providers/library_state_provider.dart index e6efc2e0..a2ec69e7 100644 --- a/lib/modules/library/providers/library_state_provider.dart +++ b/lib/modules/library/providers/library_state_provider.dart @@ -466,6 +466,118 @@ class MangaFilterBookmarkedState extends _$MangaFilterBookmarkedState { } } +// ── Completed filter ────────────────────────────────────────────────────────── + +@riverpod +class MangaFilterCompletedState extends _$MangaFilterCompletedState { + @override + int build({ + required List mangaList, + required ItemType itemType, + required Settings settings, + }) { + state = getType(); + return getType(); + } + + int getType() { + switch (itemType) { + case ItemType.manga: + return settings.libraryFilterMangasCompletedType ?? 0; + case ItemType.anime: + return settings.libraryFilterAnimeCompletedType ?? 0; + default: + return settings.libraryFilterNovelCompletedType ?? 0; + } + } + + void setType(int type) { + Settings appSettings = Settings(); + switch (itemType) { + case ItemType.manga: + appSettings = settings..libraryFilterMangasCompletedType = type; + break; + case ItemType.anime: + appSettings = settings..libraryFilterAnimeCompletedType = type; + break; + default: + appSettings = settings..libraryFilterNovelCompletedType = type; + } + isar.writeTxnSync(() { + isar.settings.putSync( + appSettings..updatedAt = DateTime.now().millisecondsSinceEpoch, + ); + }); + state = type; + } + + void update() { + if (state == 0) { + setType(1); + } else if (state == 1) { + setType(2); + } else { + setType(0); + } + } +} + +// ── Tracking filter ─────────────────────────────────────────────────────────── + +@riverpod +class MangaFilterTrackingState extends _$MangaFilterTrackingState { + @override + int build({ + required List mangaList, + required ItemType itemType, + required Settings settings, + }) { + state = getType(); + return getType(); + } + + int getType() { + switch (itemType) { + case ItemType.manga: + return settings.libraryFilterMangasTrackingType ?? 0; + case ItemType.anime: + return settings.libraryFilterAnimeTrackingType ?? 0; + default: + return settings.libraryFilterNovelTrackingType ?? 0; + } + } + + void setType(int type) { + Settings appSettings = Settings(); + switch (itemType) { + case ItemType.manga: + appSettings = settings..libraryFilterMangasTrackingType = type; + break; + case ItemType.anime: + appSettings = settings..libraryFilterAnimeTrackingType = type; + break; + default: + appSettings = settings..libraryFilterNovelTrackingType = type; + } + isar.writeTxnSync(() { + isar.settings.putSync( + appSettings..updatedAt = DateTime.now().millisecondsSinceEpoch, + ); + }); + state = type; + } + + void update() { + if (state == 0) { + setType(1); + } else if (state == 1) { + setType(2); + } else { + setType(0); + } + } +} + @riverpod class MangasFilterResultState extends _$MangasFilterResultState { @override @@ -502,10 +614,26 @@ class MangasFilterResultState extends _$MangasFilterResultState { settings: settings, ), ); + final completedFilterType = ref.watch( + mangaFilterCompletedStateProvider( + mangaList: mangaList, + itemType: itemType, + settings: settings, + ), + ); + final trackingFilterType = ref.watch( + mangaFilterTrackingStateProvider( + mangaList: mangaList, + itemType: itemType, + settings: settings, + ), + ); return downloadFilterType == 0 && unreadFilterType == 0 && startedFilterType == 0 && - bookmarkedFilterType == 0; + bookmarkedFilterType == 0 && + completedFilterType == 0 && + trackingFilterType == 0; } } diff --git a/lib/modules/library/providers/library_state_provider.g.dart b/lib/modules/library/providers/library_state_provider.g.dart index aa0cc259..e68428ae 100644 --- a/lib/modules/library/providers/library_state_provider.g.dart +++ b/lib/modules/library/providers/library_state_provider.g.dart @@ -693,6 +693,248 @@ abstract class _$MangaFilterBookmarkedState extends $Notifier { } } +@ProviderFor(MangaFilterCompletedState) +final mangaFilterCompletedStateProvider = MangaFilterCompletedStateFamily._(); + +final class MangaFilterCompletedStateProvider + extends $NotifierProvider { + MangaFilterCompletedStateProvider._({ + required MangaFilterCompletedStateFamily super.from, + required ({List mangaList, ItemType itemType, Settings settings}) + super.argument, + }) : super( + retry: null, + name: r'mangaFilterCompletedStateProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$mangaFilterCompletedStateHash(); + + @override + String toString() { + return r'mangaFilterCompletedStateProvider' + '' + '$argument'; + } + + @$internal + @override + MangaFilterCompletedState create() => MangaFilterCompletedState(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } + + @override + bool operator ==(Object other) { + return other is MangaFilterCompletedStateProvider && + other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$mangaFilterCompletedStateHash() => + r'8a9f60b94db16d65d29caa8598443c070f7c26e6'; + +final class MangaFilterCompletedStateFamily extends $Family + with + $ClassFamilyOverride< + MangaFilterCompletedState, + int, + int, + int, + ({List mangaList, ItemType itemType, Settings settings}) + > { + MangaFilterCompletedStateFamily._() + : super( + retry: null, + name: r'mangaFilterCompletedStateProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + MangaFilterCompletedStateProvider call({ + required List mangaList, + required ItemType itemType, + required Settings settings, + }) => MangaFilterCompletedStateProvider._( + argument: (mangaList: mangaList, itemType: itemType, settings: settings), + from: this, + ); + + @override + String toString() => r'mangaFilterCompletedStateProvider'; +} + +abstract class _$MangaFilterCompletedState extends $Notifier { + late final _$args = + ref.$arg + as ({List mangaList, ItemType itemType, Settings settings}); + List get mangaList => _$args.mangaList; + ItemType get itemType => _$args.itemType; + Settings get settings => _$args.settings; + + int build({ + required List mangaList, + required ItemType itemType, + required Settings settings, + }); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + int, + Object?, + Object? + >; + element.handleCreate( + ref, + () => build( + mangaList: _$args.mangaList, + itemType: _$args.itemType, + settings: _$args.settings, + ), + ); + } +} + +@ProviderFor(MangaFilterTrackingState) +final mangaFilterTrackingStateProvider = MangaFilterTrackingStateFamily._(); + +final class MangaFilterTrackingStateProvider + extends $NotifierProvider { + MangaFilterTrackingStateProvider._({ + required MangaFilterTrackingStateFamily super.from, + required ({List mangaList, ItemType itemType, Settings settings}) + super.argument, + }) : super( + retry: null, + name: r'mangaFilterTrackingStateProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$mangaFilterTrackingStateHash(); + + @override + String toString() { + return r'mangaFilterTrackingStateProvider' + '' + '$argument'; + } + + @$internal + @override + MangaFilterTrackingState create() => MangaFilterTrackingState(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } + + @override + bool operator ==(Object other) { + return other is MangaFilterTrackingStateProvider && + other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$mangaFilterTrackingStateHash() => + r'fe79a139011725cf0a3d735930a41e1f593f0b70'; + +final class MangaFilterTrackingStateFamily extends $Family + with + $ClassFamilyOverride< + MangaFilterTrackingState, + int, + int, + int, + ({List mangaList, ItemType itemType, Settings settings}) + > { + MangaFilterTrackingStateFamily._() + : super( + retry: null, + name: r'mangaFilterTrackingStateProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + MangaFilterTrackingStateProvider call({ + required List mangaList, + required ItemType itemType, + required Settings settings, + }) => MangaFilterTrackingStateProvider._( + argument: (mangaList: mangaList, itemType: itemType, settings: settings), + from: this, + ); + + @override + String toString() => r'mangaFilterTrackingStateProvider'; +} + +abstract class _$MangaFilterTrackingState extends $Notifier { + late final _$args = + ref.$arg + as ({List mangaList, ItemType itemType, Settings settings}); + List get mangaList => _$args.mangaList; + ItemType get itemType => _$args.itemType; + Settings get settings => _$args.settings; + + int build({ + required List mangaList, + required ItemType itemType, + required Settings settings, + }); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + int, + Object?, + Object? + >; + element.handleCreate( + ref, + () => build( + mangaList: _$args.mangaList, + itemType: _$args.itemType, + settings: _$args.settings, + ), + ); + } +} + @ProviderFor(MangasFilterResultState) final mangasFilterResultStateProvider = MangasFilterResultStateFamily._(); @@ -745,7 +987,7 @@ final class MangasFilterResultStateProvider } String _$mangasFilterResultStateHash() => - r'c6f916c35e9b7125ba073d09aa6838605b933b20'; + r'6fbbc29f7e71e5d929f49fdaecd69a665bd034fb'; final class MangasFilterResultStateFamily extends $Family with diff --git a/lib/modules/library/widgets/library_body.dart b/lib/modules/library/widgets/library_body.dart index 58beca1b..b48412b1 100644 --- a/lib/modules/library/widgets/library_body.dart +++ b/lib/modules/library/widgets/library_body.dart @@ -25,6 +25,8 @@ class LibraryBody extends ConsumerWidget { final int unreadFilterType; final int startedFilterType; final int bookmarkedFilterType; + final int completedFilterType; + final int trackingFilterType; final bool reverse; final bool downloadedChapter; final bool continueReaderBtn; @@ -45,6 +47,8 @@ class LibraryBody extends ConsumerWidget { required this.unreadFilterType, required this.startedFilterType, required this.bookmarkedFilterType, + required this.completedFilterType, + required this.trackingFilterType, required this.reverse, required this.downloadedChapter, required this.continueReaderBtn, @@ -89,6 +93,8 @@ class LibraryBody extends ConsumerWidget { unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, sortType: sortType ?? 0, downloadedOnly: downloadedOnly, searchQuery: searchQuery, @@ -149,6 +155,8 @@ class CategoryBadge extends ConsumerWidget { final int unreadFilterType; final int startedFilterType; final int bookmarkedFilterType; + final int completedFilterType; + final int trackingFilterType; final Settings settings; final bool downloadedOnly; final String searchQuery; @@ -162,6 +170,8 @@ class CategoryBadge extends ConsumerWidget { required this.unreadFilterType, required this.startedFilterType, required this.bookmarkedFilterType, + required this.completedFilterType, + required this.trackingFilterType, required this.settings, required this.downloadedOnly, required this.searchQuery, @@ -188,6 +198,8 @@ class CategoryBadge extends ConsumerWidget { unreadFilterType: unreadFilterType, startedFilterType: startedFilterType, bookmarkedFilterType: bookmarkedFilterType, + completedFilterType: completedFilterType, + trackingFilterType: trackingFilterType, sortType: sortType ?? 0, downloadedOnly: downloadedOnly, searchQuery: searchQuery, diff --git a/lib/modules/library/widgets/library_settings_sheet.dart b/lib/modules/library/widgets/library_settings_sheet.dart index 7467fa50..f13ba3b9 100644 --- a/lib/modules/library/widgets/library_settings_sheet.dart +++ b/lib/modules/library/widgets/library_settings_sheet.dart @@ -137,6 +137,48 @@ class _FilterTab extends ConsumerWidget { .update(); }, ), + ListTileChapterFilter( + label: l10n.completed, + type: ref.watch( + mangaFilterCompletedStateProvider( + itemType: itemType, + mangaList: entries, + settings: settings, + ), + ), + onTap: () { + ref + .read( + mangaFilterCompletedStateProvider( + itemType: itemType, + mangaList: entries, + settings: settings, + ).notifier, + ) + .update(); + }, + ), + ListTileChapterFilter( + label: l10n.tracked, + type: ref.watch( + mangaFilterTrackingStateProvider( + itemType: itemType, + mangaList: entries, + settings: settings, + ), + ), + onTap: () { + ref + .read( + mangaFilterTrackingStateProvider( + itemType: itemType, + mangaList: entries, + settings: settings, + ).notifier, + ) + .update(); + }, + ), ], ); } diff --git a/lib/modules/manga/detail/widgets/chapter_list_tile_widget.dart b/lib/modules/manga/detail/widgets/chapter_list_tile_widget.dart index ca848738..9779b935 100644 --- a/lib/modules/manga/detail/widgets/chapter_list_tile_widget.dart +++ b/lib/modules/manga/detail/widgets/chapter_list_tile_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mangayomi/main.dart'; import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart'; import 'package:mangayomi/modules/widgets/progress_center.dart'; import 'package:mangayomi/utils/constant.dart'; @@ -30,110 +31,194 @@ class ChapterListTileWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final l10n = l10nLocalizations(context)!; - return Container( - color: chapterList.contains(chapter) - ? context.primaryColor.withValues(alpha: 0.4) - : null, - child: GestureDetector( - onLongPress: () => _handleInteraction(ref), - onSecondaryTap: () => _handleInteraction(ref), - child: ListTile( - contentPadding: EdgeInsets.symmetric(horizontal: 15), - minLeadingWidth: 0, - horizontalTitleGap: 13, - leading: Container( - width: 2, - height: 40, - decoration: BoxDecoration( - color: chapter.isRead! - ? Colors.grey.withValues(alpha: 0.3) - : context.primaryColor, - borderRadius: BorderRadius.circular(10), + final isLongPressed = ref.watch(isLongPressedStateProvider); + return Dismissible( + key: ValueKey('chapter_swipe_${chapter.id}'), + direction: isLongPressed + ? DismissDirection.none + : DismissDirection.horizontal, + confirmDismiss: (direction) async { + if (direction == DismissDirection.startToEnd) { + // Swipe right → toggle bookmark + final chap = chapter; + isar.writeTxnSync(() { + chap.isBookmarked = !chap.isBookmarked!; + chap.updatedAt = DateTime.now().millisecondsSinceEpoch; + isar.chapters.putSync(chap); + }); + } else if (direction == DismissDirection.endToStart) { + // Swipe left → toggle read + final chap = chapter; + isar.writeTxnSync(() { + chap.isRead = !chap.isRead!; + if (!chap.isRead!) { + chap.lastPageRead = "1"; + } + chap.updatedAt = DateTime.now().millisecondsSinceEpoch; + isar.chapters.putSync(chap); + }); + } + return false; // Don't dismiss, snap back + }, + background: Container( + color: context.primaryColor, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Icon( + chapter.isBookmarked! ? Icons.bookmark_remove : Icons.bookmark_add, + color: Colors.white, + ), + ), + secondaryBackground: Container( + color: chapter.isRead! ? Colors.grey : Colors.green, + alignment: Alignment.centerRight, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Icon( + chapter.isRead! ? Icons.visibility_off : Icons.done_all, + color: Colors.white, + ), + ), + child: Container( + color: chapterList.contains(chapter) + ? context.primaryColor.withValues(alpha: 0.4) + : null, + child: GestureDetector( + onLongPress: () => _handleInteraction(ref), + onSecondaryTap: () => _handleInteraction(ref), + child: ListTile( + contentPadding: EdgeInsets.symmetric(horizontal: 15), + minLeadingWidth: 0, + horizontalTitleGap: 13, + leading: Container( + width: 2, + height: 40, + decoration: BoxDecoration( + color: chapter.isRead! + ? Colors.grey.withValues(alpha: 0.3) + : context.primaryColor, + borderRadius: BorderRadius.circular(10), + ), ), - ), - tileColor: (chapter.isFiller ?? false) - ? context.primaryColor.withValues(alpha: 0.15) - : null, - textColor: chapter.isRead! - ? context.isLight - ? Colors.black.withValues(alpha: 0.4) - : Colors.white.withValues(alpha: 0.3) - : null, - selectedColor: chapter.isRead! - ? Colors.white.withValues(alpha: 0.3) - : Colors.white, - onTap: () async => _handleInteraction(ref, context), - title: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (chapter.thumbnailUrl != null) - _thumbnailPreview(context, chapter.thumbnailUrl), - chapter.isBookmarked! - ? Icon(Icons.bookmark, size: 16, color: context.primaryColor) - : SizedBox.shrink(), - chapter.description != null - ? Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildTitle(chapter.name!, context), - Text( - chapter.description!, - style: const TextStyle(fontSize: 11), - overflow: TextOverflow.ellipsis, - ), - ], - ), - ) - : Flexible(child: _buildTitle(chapter.name!, context)), - ], - ), - subtitle: Row( - children: [ - if (chapter.isFiller ?? false) - Row( - children: [ - Icon(Icons.label, size: 16, color: context.primaryColor), - Text( - " Filler ", - style: TextStyle( - fontSize: 11, + tileColor: (chapter.isFiller ?? false) + ? context.primaryColor.withValues(alpha: 0.15) + : null, + textColor: chapter.isRead! + ? context.isLight + ? Colors.black.withValues(alpha: 0.4) + : Colors.white.withValues(alpha: 0.3) + : null, + selectedColor: chapter.isRead! + ? Colors.white.withValues(alpha: 0.3) + : Colors.white, + onTap: () async => _handleInteraction(ref, context), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (chapter.thumbnailUrl != null) + _thumbnailPreview(context, chapter.thumbnailUrl), + chapter.isBookmarked! + ? Icon( + Icons.bookmark, + size: 16, color: context.primaryColor, - ), - ), - ], - ), - if ((chapter.manga.value!.isLocalArchive ?? false) == false) - Text( - chapter.dateUpload == null || chapter.dateUpload!.isEmpty - ? "" - : dateFormat( - chapter.dateUpload!, - ref: ref, - context: context, + ) + : SizedBox.shrink(), + chapter.description != null + ? Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildTitle(chapter.name!, context), + Text( + chapter.description!, + style: const TextStyle(fontSize: 11), + overflow: TextOverflow.ellipsis, + ), + ], ), - style: const TextStyle(fontSize: 11), - ), - if (!chapter.isRead!) - if (chapter.lastPageRead!.isNotEmpty && - chapter.lastPageRead != "1") + ) + : Flexible(child: _buildTitle(chapter.name!, context)), + ], + ), + subtitle: Row( + children: [ + if (chapter.isFiller ?? false) + Row( + children: [ + Icon(Icons.label, size: 16, color: context.primaryColor), + Text( + " Filler ", + style: TextStyle( + fontSize: 11, + color: context.primaryColor, + ), + ), + ], + ), + if ((chapter.manga.value!.isLocalArchive ?? false) == false) + Text( + chapter.dateUpload == null || chapter.dateUpload!.isEmpty + ? "" + : dateFormat( + chapter.dateUpload!, + ref: ref, + context: context, + ), + style: const TextStyle(fontSize: 11), + ), + if (!chapter.isRead!) + if (chapter.lastPageRead!.isNotEmpty && + chapter.lastPageRead != "1") + Row( + children: [ + const Text(' • '), + Text( + chapter.manga.value!.itemType == ItemType.anime + ? l10n.episode_progress( + Duration( + milliseconds: int.parse( + chapter.lastPageRead!, + ), + ).toString().substringBefore("."), + ) + : l10n.page( + chapter.manga.value!.itemType == + ItemType.manga + ? chapter.lastPageRead! + : "${((double.tryParse(chapter.lastPageRead!) ?? 0) * 100).toStringAsFixed(0)} %", + ), + style: TextStyle( + fontSize: 11, + color: context.isLight + ? Colors.black.withValues(alpha: 0.4) + : Colors.white.withValues(alpha: 0.3), + ), + ), + ], + ), + if (chapter.scanlator?.isNotEmpty ?? false) Row( children: [ const Text(' • '), Text( - chapter.manga.value!.itemType == ItemType.anime - ? l10n.episode_progress( - Duration( - milliseconds: int.parse( - chapter.lastPageRead!, - ), - ).toString().substringBefore("."), - ) - : l10n.page( - chapter.manga.value!.itemType == ItemType.manga - ? chapter.lastPageRead! - : "${((double.tryParse(chapter.lastPageRead!) ?? 0) * 100).toStringAsFixed(0)} %", - ), + chapter.scanlator!, + style: TextStyle( + fontSize: 11, + color: chapter.isRead! + ? context.isLight + ? Colors.black.withValues(alpha: 0.4) + : Colors.white.withValues(alpha: 0.3) + : null, + ), + ), + ], + ), + if (chapter.downloadSize != null) + Row( + children: [ + const Text(' • '), + Text( + chapter.downloadSize!, style: TextStyle( fontSize: 11, color: context.isLight @@ -143,44 +228,13 @@ class ChapterListTileWidget extends ConsumerWidget { ), ], ), - if (chapter.scanlator?.isNotEmpty ?? false) - Row( - children: [ - const Text(' • '), - Text( - chapter.scanlator!, - style: TextStyle( - fontSize: 11, - color: chapter.isRead! - ? context.isLight - ? Colors.black.withValues(alpha: 0.4) - : Colors.white.withValues(alpha: 0.3) - : null, - ), - ), - ], - ), - if (chapter.downloadSize != null) - Row( - children: [ - const Text(' • '), - Text( - chapter.downloadSize!, - style: TextStyle( - fontSize: 11, - color: context.isLight - ? Colors.black.withValues(alpha: 0.4) - : Colors.white.withValues(alpha: 0.3), - ), - ), - ], - ), - ], + ], + ), + trailing: + !sourceExist || (chapter.manga.value!.isLocalArchive ?? false) + ? null + : ChapterPageDownload(chapter: chapter), ), - trailing: - !sourceExist || (chapter.manga.value!.isLocalArchive ?? false) - ? null - : ChapterPageDownload(chapter: chapter), ), ), ); diff --git a/lib/modules/manga/download/providers/convert_to_cbz.dart b/lib/modules/manga/download/providers/convert_to_cbz.dart index dd23379d..243a91d3 100644 --- a/lib/modules/manga/download/providers/convert_to_cbz.dart +++ b/lib/modules/manga/download/providers/convert_to_cbz.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:archive/archive_io.dart'; import 'package:flutter/foundation.dart'; @@ -5,19 +6,96 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:path/path.dart' as path; part 'convert_to_cbz.g.dart'; +/// Metadata for ComicInfo.xml generation (serializable for isolate). +class ComicInfoData { + final String? title; + final String? series; + final String? number; + final String? writer; + final String? penciller; + final String? summary; + final String? genre; + final String? translator; + final String? publishingStatusStr; + final int pageCount; + + const ComicInfoData({ + this.title, + this.series, + this.number, + this.writer, + this.penciller, + this.summary, + this.genre, + this.translator, + this.publishingStatusStr, + this.pageCount = 0, + }); +} + @riverpod Future> convertToCBZ( Ref ref, String chapterDir, String mangaDir, String chapterName, - List pageList, -) async { - return compute(_convertToCBZ, (chapterDir, mangaDir, chapterName, pageList)); + List pageList, { + ComicInfoData? comicInfo, +}) async { + return compute(_convertToCBZ, ( + chapterDir, + mangaDir, + chapterName, + pageList, + comicInfo, + )); } -List _convertToCBZ((String, String, String, List) datas) { - final (chapterDir, mangaDir, chapterName, pageList) = datas; +String _buildComicInfoXml(ComicInfoData info, int pageCount) { + final sb = StringBuffer(); + sb.writeln(''); + sb.writeln( + '', + ); + + void addTag(String tag, String? value) { + if (value != null && value.isNotEmpty) { + final escaped = _xmlEscape(value); + sb.writeln(' <$tag>$escaped'); + } + } + + addTag('Title', info.title); + addTag('Series', info.series); + addTag('Number', info.number); + addTag('Writer', info.writer); + addTag('Penciller', info.penciller); + addTag('Summary', info.summary); + addTag('Genre', info.genre); + addTag('Translator', info.translator); + if (pageCount > 0) { + sb.writeln(' $pageCount'); + } + addTag('PublishingStatusTachiyomi', info.publishingStatusStr); + + sb.writeln(''); + return sb.toString(); +} + +String _xmlEscape(String value) { + return value + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll("'", '''); +} + +List _convertToCBZ( + (String, String, String, List, ComicInfoData?) datas, +) { + final (chapterDir, mangaDir, chapterName, pageList, comicInfo) = datas; final imagesPaths = pageList.where((path) => path.endsWith('.jpg')).toList() ..sort(); @@ -39,6 +117,13 @@ List _convertToCBZ((String, String, String, List) datas) { archive.add(ArchiveFile.bytes(fileName, bytes)); includedFiles.add(imagePath); } + + // Add ComicInfo.xml if metadata is provided + if (comicInfo != null) { + final xml = _buildComicInfoXml(comicInfo, includedFiles.length); + archive.add(ArchiveFile.bytes('ComicInfo.xml', utf8.encode(xml))); + } + try { final cbzData = ZipEncoder().encode(archive); File(cbzPath).writeAsBytesSync(cbzData); diff --git a/lib/modules/manga/download/providers/convert_to_cbz.g.dart b/lib/modules/manga/download/providers/convert_to_cbz.g.dart index e7dfbcab..3dff7755 100644 --- a/lib/modules/manga/download/providers/convert_to_cbz.g.dart +++ b/lib/modules/manga/download/providers/convert_to_cbz.g.dart @@ -22,7 +22,8 @@ final class ConvertToCBZProvider with $FutureModifier>, $FutureProvider> { ConvertToCBZProvider._({ required ConvertToCBZFamily super.from, - required (String, String, String, List) super.argument, + required (String, String, String, List, {ComicInfoData? comicInfo}) + super.argument, }) : super( retry: null, name: r'convertToCBZProvider', @@ -49,13 +50,22 @@ final class ConvertToCBZProvider @override FutureOr> create(Ref ref) { - final argument = this.argument as (String, String, String, List); + final argument = + this.argument + as ( + String, + String, + String, + List, { + ComicInfoData? comicInfo, + }); return convertToCBZ( ref, argument.$1, argument.$2, argument.$3, argument.$4, + comicInfo: argument.comicInfo, ); } @@ -70,13 +80,13 @@ final class ConvertToCBZProvider } } -String _$convertToCBZHash() => r'56f4320034ec2420c8c2c2b22a2522721181ab54'; +String _$convertToCBZHash() => r'0f75969b8eccb5932089e5e269a5bba4012842b8'; final class ConvertToCBZFamily extends $Family with $FunctionalFamilyOverride< FutureOr>, - (String, String, String, List) + (String, String, String, List, {ComicInfoData? comicInfo}) > { ConvertToCBZFamily._() : super( @@ -91,9 +101,16 @@ final class ConvertToCBZFamily extends $Family String chapterDir, String mangaDir, String chapterName, - List pageList, - ) => ConvertToCBZProvider._( - argument: (chapterDir, mangaDir, chapterName, pageList), + List pageList, { + ComicInfoData? comicInfo, + }) => ConvertToCBZProvider._( + argument: ( + chapterDir, + mangaDir, + chapterName, + pageList, + comicInfo: comicInfo, + ), from: this, ); diff --git a/lib/modules/manga/download/providers/download_provider.dart b/lib/modules/manga/download/providers/download_provider.dart index 8302530c..6a4fb1c1 100644 --- a/lib/modules/manga/download/providers/download_provider.dart +++ b/lib/modules/manga/download/providers/download_provider.dart @@ -25,6 +25,7 @@ import 'package:mangayomi/services/get_chapter_pages.dart'; import 'package:mangayomi/services/http/m_client.dart'; import 'package:mangayomi/services/download_manager/m3u8/m3u8_downloader.dart'; import 'package:mangayomi/services/download_manager/m3u8/models/download.dart'; +import 'package:mangayomi/utils/chapter_recognition.dart'; import 'package:mangayomi/utils/extensions/chapter.dart'; import 'package:mangayomi/utils/extensions/string_extensions.dart'; import 'package:mangayomi/utils/headers.dart'; @@ -106,12 +107,31 @@ Future downloadChapter( Future processConvert() async { if (!ref.read(saveAsCBZArchiveStateProvider)) return; try { + // Extract chapter number from name (e.g., "Chapter 5" → "5") + final chapterNumber = ChapterRecognition().parseChapterNumber( + chapter.manga.value!.name!, + chapter.name!, + ); + + final comicInfo = ComicInfoData( + title: chapter.name, + series: manga.name, + number: chapterNumber.toString(), + writer: manga.author, + penciller: manga.artist, + summary: manga.description, + genre: manga.genre?.join(', '), + translator: chapter.scanlator, + publishingStatusStr: manga.status.name, + ); + await ref.read( convertToCBZProvider( chapterDirectory.path, mangaMainDirectory!.path, chapter.name!, pages.map((e) => e.fileName!).toList(), + comicInfo: comicInfo, ).future, ); } catch (error) { diff --git a/lib/modules/manga/download/providers/download_provider.g.dart b/lib/modules/manga/download/providers/download_provider.g.dart index 25931e17..2d3c7aa8 100644 --- a/lib/modules/manga/download/providers/download_provider.g.dart +++ b/lib/modules/manga/download/providers/download_provider.g.dart @@ -136,7 +136,7 @@ final class DownloadChapterProvider } } -String _$downloadChapterHash() => r'db235f856cf106c89d6124c361a51f2e312e9aa3'; +String _$downloadChapterHash() => r'34ecaeac678ca578ce785b8e43d089e95cba89d0'; final class DownloadChapterFamily extends $Family with diff --git a/lib/modules/more/settings/security/app_lock_screen.dart b/lib/modules/more/settings/security/app_lock_screen.dart new file mode 100644 index 00000000..9ad474be --- /dev/null +++ b/lib/modules/more/settings/security/app_lock_screen.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:mangayomi/modules/more/settings/security/providers/security_state_provider.dart'; +import 'package:mangayomi/providers/l10n_providers.dart'; + +class AppLockScreen extends ConsumerStatefulWidget { + const AppLockScreen({super.key}); + + @override + ConsumerState createState() => _AppLockScreenState(); +} + +class _AppLockScreenState extends ConsumerState { + final LocalAuthentication _localAuth = LocalAuthentication(); + bool _isAuthenticating = false; + late final l10n = context.l10n; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) => _authenticate()); + } + + Future _authenticate() async { + if (_isAuthenticating) return; + setState(() => _isAuthenticating = true); + + try { + final didAuthenticate = await _localAuth.authenticate( + localizedReason: l10n.auth_unlock_msg, + biometricOnly: false, + persistAcrossBackgrounding: true, + ); + if (didAuthenticate && mounted) { + ref.read(appUnlockedStateProvider.notifier).unlock(); + } + } catch (_) { + } finally { + if (mounted) { + setState(() => _isAuthenticating = false); + } + } + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + child: Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.lock_outline, + size: 80, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 24), + Text( + l10n.app_locked, + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 8), + Text( + l10n.auth_unlock_msg, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).hintColor, + ), + ), + const SizedBox(height: 32), + FilledButton.icon( + onPressed: _isAuthenticating ? null : _authenticate, + icon: const Icon(Icons.fingerprint), + label: Text( + _isAuthenticating ? l10n.authenticating : l10n.unlock, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/modules/more/settings/security/providers/security_state_provider.dart b/lib/modules/more/settings/security/providers/security_state_provider.dart new file mode 100644 index 00000000..b1e9b8db --- /dev/null +++ b/lib/modules/more/settings/security/providers/security_state_provider.dart @@ -0,0 +1,40 @@ +import 'package:mangayomi/main.dart'; +import 'package:mangayomi/models/settings.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +part 'security_state_provider.g.dart'; + +@riverpod +class AppLockEnabledState extends _$AppLockEnabledState { + @override + bool build() { + return isar.settings.getSync(227)!.appLockEnabled ?? false; + } + + void set(bool value) { + final settings = isar.settings.getSync(227)!; + state = value; + isar.writeTxnSync( + () => isar.settings.putSync( + settings + ..appLockEnabled = value + ..updatedAt = DateTime.now().millisecondsSinceEpoch, + ), + ); + } +} + +/// Tracks whether the app is currently unlocked. +/// Resets to false when app goes to background (if lock is enabled). +@riverpod +class AppUnlockedState extends _$AppUnlockedState { + @override + bool build() { + // If app lock is not enabled, always unlocked + final lockEnabled = isar.settings.getSync(227)!.appLockEnabled ?? false; + return !lockEnabled; + } + + void unlock() => state = true; + + void lock() => state = false; +} diff --git a/lib/modules/more/settings/security/providers/security_state_provider.g.dart b/lib/modules/more/settings/security/providers/security_state_provider.g.dart new file mode 100644 index 00000000..59e7edbb --- /dev/null +++ b/lib/modules/more/settings/security/providers/security_state_provider.g.dart @@ -0,0 +1,125 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'security_state_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(AppLockEnabledState) +final appLockEnabledStateProvider = AppLockEnabledStateProvider._(); + +final class AppLockEnabledStateProvider + extends $NotifierProvider { + AppLockEnabledStateProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'appLockEnabledStateProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$appLockEnabledStateHash(); + + @$internal + @override + AppLockEnabledState create() => AppLockEnabledState(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(bool value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$appLockEnabledStateHash() => + r'cdd466aee9037e776f5adf992e11ccedb8c58e74'; + +abstract class _$AppLockEnabledState extends $Notifier { + bool build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + bool, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} + +/// Tracks whether the app is currently unlocked. +/// Resets to false when app goes to background (if lock is enabled). + +@ProviderFor(AppUnlockedState) +final appUnlockedStateProvider = AppUnlockedStateProvider._(); + +/// Tracks whether the app is currently unlocked. +/// Resets to false when app goes to background (if lock is enabled). +final class AppUnlockedStateProvider + extends $NotifierProvider { + /// Tracks whether the app is currently unlocked. + /// Resets to false when app goes to background (if lock is enabled). + AppUnlockedStateProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'appUnlockedStateProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$appUnlockedStateHash(); + + @$internal + @override + AppUnlockedState create() => AppUnlockedState(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(bool value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$appUnlockedStateHash() => r'e5dd0982d0fc0b51cb3db8e6be04d11490d46b9b'; + +/// Tracks whether the app is currently unlocked. +/// Resets to false when app goes to background (if lock is enabled). + +abstract class _$AppUnlockedState extends $Notifier { + bool build(); + @$mustCallSuper + @override + void runBuild() { + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + bool, + Object?, + Object? + >; + element.handleCreate(ref, build); + } +} diff --git a/lib/modules/more/settings/security/security_screen.dart b/lib/modules/more/settings/security/security_screen.dart new file mode 100644 index 00000000..453239bf --- /dev/null +++ b/lib/modules/more/settings/security/security_screen.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:mangayomi/modules/more/settings/security/providers/security_state_provider.dart'; +import 'package:mangayomi/providers/l10n_providers.dart'; + +class SecurityScreen extends ConsumerStatefulWidget { + const SecurityScreen({super.key}); + + @override + ConsumerState createState() => _SecurityScreenState(); +} + +class _SecurityScreenState extends ConsumerState { + final LocalAuthentication _localAuth = LocalAuthentication(); + bool _canCheckBiometrics = false; + late final l10n = context.l10n; + @override + void initState() { + super.initState(); + _checkBiometricAvailability(); + } + + Future _checkBiometricAvailability() async { + try { + final canAuth = await _localAuth.canCheckBiometrics; + final isDeviceSupported = await _localAuth.isDeviceSupported(); + if (mounted) { + setState(() { + _canCheckBiometrics = canAuth || isDeviceSupported; + }); + } + } catch (_) { + if (mounted) { + setState(() => _canCheckBiometrics = false); + } + } + } + + Future _authenticate() async { + try { + return await _localAuth.authenticate( + localizedReason: l10n.auth_to_change_security_setting, + biometricOnly: false, + persistAcrossBackgrounding: true, + ); + } catch (_) { + return false; + } + } + + @override + Widget build(BuildContext context) { + final appLockEnabled = ref.watch(appLockEnabledStateProvider); + + return Scaffold( + appBar: AppBar(title: Text(l10n.security)), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SwitchListTile( + value: appLockEnabled, + title: Text(l10n.app_lock), + subtitle: Text( + _canCheckBiometrics + ? l10n.require_biometric_or_device_credential + : l10n.biometric_or_device_credential_not_available, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).hintColor, + ), + ), + onChanged: _canCheckBiometrics + ? (value) async { + if (value) { + final authenticated = await _authenticate(); + if (authenticated) { + ref + .read(appLockEnabledStateProvider.notifier) + .set(true); + } + } else { + final authenticated = await _authenticate(); + if (authenticated) { + ref + .read(appLockEnabledStateProvider.notifier) + .set(false); + ref.read(appUnlockedStateProvider.notifier).unlock(); + } + } + } + : null, + ), + + const Divider(), + + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + l10n.app_lock_description, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).hintColor, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/modules/more/settings/settings_screen.dart b/lib/modules/more/settings/settings_screen.dart index 18c11eae..08aba173 100644 --- a/lib/modules/more/settings/settings_screen.dart +++ b/lib/modules/more/settings/settings_screen.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart'; @@ -54,6 +56,12 @@ class SettingsScreen extends StatelessWidget { icon: Icons.explore_rounded, onTap: () => context.push('/browseS'), ), + if (!Platform.isLinux) + ListTileWidget( + title: l10n.security, + icon: Icons.security_rounded, + onTap: () => context.push('/security'), + ), ListTileWidget( title: l10n.about, icon: Icons.info_outline, diff --git a/lib/router/router.dart b/lib/router/router.dart index 72eb6d47..6dcfd159 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:bot_toast/bot_toast.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/models/settings.dart'; @@ -56,6 +57,7 @@ import 'package:mangayomi/modules/more/settings/browse/browse_screen.dart'; import 'package:mangayomi/modules/more/settings/general/general_screen.dart'; import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart'; import 'package:mangayomi/modules/more/settings/settings_screen.dart'; +import 'package:mangayomi/modules/more/settings/security/security_screen.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter/cupertino.dart'; part 'router.g.dart'; @@ -86,6 +88,7 @@ class RouterCurrentLocationState extends _$RouterCurrentLocationState { bool _didSubscribe = false; @override String? build() { + ref.keepAlive(); // Delay listener‐registration until after the first frame. if (!_didSubscribe) { _didSubscribe = true; @@ -207,6 +210,7 @@ class RouterNotifier extends ChangeNotifier { ), _genericRoute(name: "downloads", child: const DownloadsScreen()), _genericRoute(name: "dataAndStorage", child: const DataAndStorage()), + _genericRoute(name: "security", child: const SecurityScreen()), _genericRoute(name: "manageTrackers", child: const ManageTrackersScreen()), _genericRoute( name: "trackingDetail", @@ -280,36 +284,24 @@ class RouterNotifier extends ChangeNotifier { return child!; } }, - pageBuilder: (context, state) { - final pageChild = builder != null ? builder(state.extra as T) : child!; - return transitionPage(key: state.pageKey, child: pageChild); - }, + pageBuilder: (Platform.isIOS || Platform.isMacOS) + ? (context, state) { + final pageChild = builder != null + ? builder(state.extra as T) + : child!; + return transitionPage(key: state.pageKey, child: pageChild); + } + : null, ); } } Page transitionPage({required LocalKey key, required child}) { - return Platform.isIOS - ? CupertinoPage(key: key, child: child) - : CustomTransition(child: child, key: key); -} - -class CustomTransition extends CustomTransitionPage { - CustomTransition({required LocalKey super.key, required super.child}) - : super( - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition(opacity: animation, child: child); - }, - ); + return CupertinoPage(key: key, child: child); } Route createRoute({required Widget page}) { - return Platform.isIOS + return (Platform.isIOS || Platform.isMacOS) ? CupertinoPageRoute(builder: (context) => page) - : PageRouteBuilder( - pageBuilder: (context, animation, secondaryAnimation) => page, - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return FadeTransition(opacity: animation, child: child); - }, - ); + : MaterialPageRoute(builder: (context) => page); } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 0fcd0443..b122d48c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import flutter_inappwebview_macos import flutter_qjs import flutter_web_auth_2 import isar_community_flutter_libs +import local_auth_darwin import m_extension_server import media_kit_libs_macos_video import media_kit_video @@ -35,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterQjsPlugin.register(with: registry.registrar(forPlugin: "FlutterQjsPlugin")) FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) MExtensionServerPlugin.register(with: registry.registrar(forPlugin: "MExtensionServerPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 083f593f..36dce389 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -19,6 +19,9 @@ PODS: - FlutterMacOS (1.0.0) - isar_community_flutter_libs (1.0.0): - FlutterMacOS + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS - m_extension_server (0.0.1): - FlutterMacOS - media_kit_libs_macos_video (1.0.4): @@ -58,6 +61,7 @@ DEPENDENCIES: - flutter_web_auth_2 (from `Flutter/ephemeral/.symlinks/plugins/flutter_web_auth_2/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - isar_community_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_community_flutter_libs/macos`) + - local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`) - m_extension_server (from `Flutter/ephemeral/.symlinks/plugins/m_extension_server/macos`) - media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`) - media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`) @@ -97,6 +101,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral isar_community_flutter_libs: :path: Flutter/ephemeral/.symlinks/plugins/isar_community_flutter_libs/macos + local_auth_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin m_extension_server: :path: Flutter/ephemeral/.symlinks/plugins/m_extension_server/macos media_kit_libs_macos_video: @@ -135,6 +141,7 @@ SPEC CHECKSUMS: flutter_web_auth_2: 62b08da29f15a20fa63f144234622a1488d45b65 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 isar_community_flutter_libs: a631ceb5622413b56bcd0a8bf49cb55bf3d8bb2b + local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb m_extension_server: 50e95a61bbf93c9a33ddc812d0753bddf1c01456 media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65 media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758 diff --git a/pubspec.lock b/pubspec.lock index ab8688e9..1112ac43 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -946,6 +946,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + local_auth: + dependency: "direct main" + description: + name: local_auth + sha256: ae6f382f638108c6becd134318d7c3f0a93875383a54010f61d7c97ac05d5137 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: dc9663a7bc8ac33d7d988e63901974f63d527ebef260eabd19c479447cc9c911 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: a8c3d4e17454111f7fd31ff72a31222359f6059f7fe956c2dcfe0f88f49826d4 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122 + url: "https://pub.dev" + source: hosted + version: "1.1.0" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: be12c5b8ba5e64896983123655c5f67d2484ecfcc95e367952ad6e3bff94cb16 + url: "https://pub.dev" + source: hosted + version: "2.0.1" logging: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 291846fa..06e8422e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -95,6 +95,7 @@ dependencies: url: https://github.com/Schnitzel5/flutter-discord-rpc.git ref: main table_calendar: ^3.2.0 + local_auth: ^3.0.1 m_extension_server: git: url: https://github.com/kodjodevf/m_extension_server.git diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 52d61056..5931f091 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FlutterQjsPlugin")); IsarFlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); MExtensionServerPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MExtensionServerPluginCApi")); MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index f0f9961f..835195b2 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_inappwebview_windows flutter_qjs isar_community_flutter_libs + local_auth_windows m_extension_server media_kit_libs_windows_video media_kit_video