mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 19:12:04 +00:00
Merge branch 'main' into enhance/mpv
This commit is contained in:
commit
0452576de6
48 changed files with 2675 additions and 967 deletions
|
|
@ -77,6 +77,8 @@
|
|||
"clean_database_desc": "This will remove all items that are not added to the library!",
|
||||
"incognito_mode": "Incognito Mode",
|
||||
"incognito_mode_description": "Pauses reading history",
|
||||
"downloaded_only": "Downloaded only",
|
||||
"downloaded_only_description": "Only show downloaded entries in your library",
|
||||
"download_queue": "Download Queue",
|
||||
"categories": "Categories",
|
||||
"statistics": "Statistics",
|
||||
|
|
@ -226,6 +228,10 @@
|
|||
"sync_finished": "Sync finished",
|
||||
"sync_failed": "Sync failed",
|
||||
"sync_button_sync": "Sync progress",
|
||||
"sync_button_upload": "Upload only",
|
||||
"sync_button_upload_info": "This operation will fully replace the remote data with local data!",
|
||||
"sync_button_download": "Download only",
|
||||
"sync_button_download_info": "This operation will fully replace the local data with remote data!",
|
||||
"sync_on": "Enable sync",
|
||||
"sync_auto": "Auto Sync",
|
||||
"sync_auto_warning": "Auto Sync is currently an experimental feature!",
|
||||
|
|
@ -252,6 +258,7 @@
|
|||
"anime_extensions": "Anime Extensions",
|
||||
"manga_extensions": "Manga Extensions",
|
||||
"novel_extensions": "Novel Extensions",
|
||||
"extension_settings": "Extension settings",
|
||||
"anime": "Anime",
|
||||
"manga": "Manga",
|
||||
"novel": "Novel",
|
||||
|
|
@ -429,6 +436,13 @@
|
|||
"include_sensitive_settings": "Include sensitive settings (e.g., tracker login tokens)",
|
||||
"create": "Create",
|
||||
"downloads_are_limited_to_wifi": "Downloads are limited to Wi-Fi only",
|
||||
"recommendations": "Recommendations",
|
||||
"recommendations_similar": "similar",
|
||||
"recommendations_weights": "Recommendation Weights",
|
||||
"recommendations_weights_genre": "Genre Similarity",
|
||||
"recommendations_weights_setting": "Setting Similarity",
|
||||
"recommendations_weights_synopsis": "Story Similarity",
|
||||
"recommendations_weights_theme": "Theme Similarity",
|
||||
"manga_extensions_repo": "Manga extensions repo",
|
||||
"anime_extensions_repo": "Anime extensions repo",
|
||||
"novel_extensions_repo": "Novel extensions repo",
|
||||
|
|
|
|||
|
|
@ -537,6 +537,18 @@ abstract class AppLocalizations {
|
|||
/// **'Pauses reading history'**
|
||||
String get incognito_mode_description;
|
||||
|
||||
/// No description provided for @downloaded_only.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Downloaded only'**
|
||||
String get downloaded_only;
|
||||
|
||||
/// No description provided for @downloaded_only_description.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Only show downloaded entries in your library'**
|
||||
String get downloaded_only_description;
|
||||
|
||||
/// No description provided for @download_queue.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -1431,6 +1443,30 @@ abstract class AppLocalizations {
|
|||
/// **'Sync progress'**
|
||||
String get sync_button_sync;
|
||||
|
||||
/// No description provided for @sync_button_upload.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Upload only'**
|
||||
String get sync_button_upload;
|
||||
|
||||
/// No description provided for @sync_button_upload_info.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This operation will fully replace the remote data with local data!'**
|
||||
String get sync_button_upload_info;
|
||||
|
||||
/// No description provided for @sync_button_download.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Download only'**
|
||||
String get sync_button_download;
|
||||
|
||||
/// No description provided for @sync_button_download_info.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This operation will fully replace the local data with remote data!'**
|
||||
String get sync_button_download_info;
|
||||
|
||||
/// No description provided for @sync_on.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -1587,6 +1623,12 @@ abstract class AppLocalizations {
|
|||
/// **'Novel Extensions'**
|
||||
String get novel_extensions;
|
||||
|
||||
/// No description provided for @extension_settings.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Extension settings'**
|
||||
String get extension_settings;
|
||||
|
||||
/// No description provided for @anime.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -2649,6 +2691,48 @@ abstract class AppLocalizations {
|
|||
/// **'Downloads are limited to Wi-Fi only'**
|
||||
String get downloads_are_limited_to_wifi;
|
||||
|
||||
/// No description provided for @recommendations.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Recommendations'**
|
||||
String get recommendations;
|
||||
|
||||
/// No description provided for @recommendations_similar.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'similar'**
|
||||
String get recommendations_similar;
|
||||
|
||||
/// No description provided for @recommendations_weights.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Recommendation Weights'**
|
||||
String get recommendations_weights;
|
||||
|
||||
/// No description provided for @recommendations_weights_genre.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Genre Similarity'**
|
||||
String get recommendations_weights_genre;
|
||||
|
||||
/// No description provided for @recommendations_weights_setting.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Setting Similarity'**
|
||||
String get recommendations_weights_setting;
|
||||
|
||||
/// No description provided for @recommendations_weights_synopsis.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Story Similarity'**
|
||||
String get recommendations_weights_synopsis;
|
||||
|
||||
/// No description provided for @recommendations_weights_theme.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Theme Similarity'**
|
||||
String get recommendations_weights_theme;
|
||||
|
||||
/// No description provided for @manga_extensions_repo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -231,6 +231,13 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'يوقف سجل القراءة';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'قائمة الانتظار للتحميل';
|
||||
|
||||
|
|
@ -699,6 +706,20 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'مزامنة التقدم';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'تمكين المزامنة';
|
||||
|
||||
|
|
@ -783,6 +804,9 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'إضافات الروايات';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'أنمي';
|
||||
|
||||
|
|
@ -1358,6 +1382,27 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
@override
|
||||
String get downloads_are_limited_to_wifi => 'التنزيلات مقتصرة على Wi-Fi فقط';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'مستودع إضافات المانجا';
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,13 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'পঢ়াৰ ইতিহাস স্থগিত কৰে';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'ডাউনলোড শাৰী';
|
||||
|
||||
|
|
@ -701,6 +708,20 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sync progress';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Enable sync';
|
||||
|
||||
|
|
@ -786,6 +807,9 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Novel Extensions';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'এনিমে';
|
||||
|
||||
|
|
@ -1360,6 +1384,27 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Downloads are limited to Wi-Fi only';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Manga extensions repo';
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Pausiert den Leseverlauf';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Download-Warteschlange';
|
||||
|
||||
|
|
@ -703,6 +710,20 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Jetzt synchronisieren';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Sync aktivieren';
|
||||
|
||||
|
|
@ -788,6 +809,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Novel-Erweiterungen';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1369,6 +1393,27 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Downloads sind nur über WLAN verfügbar';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Manga-Erweiterungs-Repository';
|
||||
|
||||
|
|
|
|||
|
|
@ -232,6 +232,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Pauses reading history';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Download Queue';
|
||||
|
||||
|
|
@ -701,6 +708,20 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sync progress';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Enable sync';
|
||||
|
||||
|
|
@ -786,6 +807,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Novel Extensions';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1359,6 +1383,27 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Downloads are limited to Wi-Fi only';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Manga extensions repo';
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Pausa el historial de lectura';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Cola de descarga';
|
||||
|
||||
|
|
@ -705,6 +712,20 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sincronizar progreso';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Habilitar sincronización';
|
||||
|
||||
|
|
@ -790,6 +811,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Extensiones de novelas';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1373,6 +1397,27 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Las descargas están limitadas solo a Wi-Fi';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Repositorio de extensiones de manga';
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Suspend l\'historique de lecture';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'File de téléchargement';
|
||||
|
||||
|
|
@ -708,6 +715,20 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Synchroniser les progrès';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Activer la synchronisation';
|
||||
|
||||
|
|
@ -793,6 +814,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Extensions de romans';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Animé';
|
||||
|
||||
|
|
@ -1376,6 +1400,27 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Les téléchargements sont limités au Wi-Fi uniquement';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Dépôt d\'extensions de mangas';
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,13 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'पढ़ने का इतिहास रोकता है';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'डाउनलोड कतार';
|
||||
|
||||
|
|
@ -701,6 +708,20 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sync progress';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Enable sync';
|
||||
|
||||
|
|
@ -786,6 +807,9 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Novel Extensions';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'एनीमे';
|
||||
|
||||
|
|
@ -1361,6 +1385,27 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Downloads are limited to Wi-Fi only';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Manga extensions repo';
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Menghentikan catatan bacaan';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Antrian Unduhan';
|
||||
|
||||
|
|
@ -705,6 +712,20 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sinkronkan progres';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Aktifkan sinkronisasi';
|
||||
|
||||
|
|
@ -790,6 +811,9 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Ekstensi Novel';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1365,6 +1389,27 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Unduhan dibatasi hanya untuk Wi-Fi';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Repositori ekstensi manga';
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Sospende la cronologia di lettura';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Coda di download';
|
||||
|
||||
|
|
@ -705,6 +712,20 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sincronizza progressi';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Abilita sincronizzazione';
|
||||
|
||||
|
|
@ -790,6 +811,9 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Estensioni romanzo';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1373,6 +1397,27 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'I download sono limitati solo al Wi-Fi';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Repository delle estensioni manga';
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,13 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Pausa o histórico de leitura';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Fila de download';
|
||||
|
||||
|
|
@ -705,6 +712,20 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Sincronizar progresso';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Ativar sincronização';
|
||||
|
||||
|
|
@ -790,6 +811,9 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Extensões de novels';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1370,6 +1394,27 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Os downloads estão limitados apenas ao Wi-Fi';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Repositório de extensões de mangás';
|
||||
|
||||
|
|
|
|||
|
|
@ -234,6 +234,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Пауза в истории чтения';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'Очередь загрузки';
|
||||
|
||||
|
|
@ -707,6 +714,20 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'Синхронизировать прогресс';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Включить синхронизацию';
|
||||
|
||||
|
|
@ -792,6 +813,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Расширения для романов';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Аниме';
|
||||
|
||||
|
|
@ -1372,6 +1396,27 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'Загрузки ограничены только Wi-Fi';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Репозиторий расширений манги';
|
||||
|
||||
|
|
|
|||
|
|
@ -232,6 +232,13 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'หยุดประวัติการอ่านชั่วคราว';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'คิวดาวน์โหลด';
|
||||
|
||||
|
|
@ -701,6 +708,20 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'ซิงค์ความคืบหน้า';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'เปิดการซิงค์';
|
||||
|
||||
|
|
@ -785,6 +806,9 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'ส่วนขยายของนวนิยาย';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'อนิเมะ';
|
||||
|
||||
|
|
@ -1359,6 +1383,27 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'การดาวน์โหลดจำกัดเฉพาะ Wi-Fi เท่านั้น';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'ที่เก็บส่วนขยายมังงะ';
|
||||
|
||||
|
|
|
|||
|
|
@ -232,6 +232,13 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => 'Okuma geçmişini duraklatır';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => 'İndirme Kuyruğu';
|
||||
|
||||
|
|
@ -701,6 +708,20 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => 'İlerlemeyi senkronize et';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => 'Senkronizasyonu etkinleştir';
|
||||
|
||||
|
|
@ -786,6 +807,9 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => 'Hikaye Uzantıları';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => 'Anime';
|
||||
|
||||
|
|
@ -1365,6 +1389,27 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
String get downloads_are_limited_to_wifi =>
|
||||
'İndirmeler yalnızca Wi-Fi ile sınırlıdır';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => 'Manga uzantıları deposu';
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,13 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get incognito_mode_description => '暂停阅读历史';
|
||||
|
||||
@override
|
||||
String get downloaded_only => 'Downloaded only';
|
||||
|
||||
@override
|
||||
String get downloaded_only_description =>
|
||||
'Only show downloaded entries in your library';
|
||||
|
||||
@override
|
||||
String get download_queue => '下载队列';
|
||||
|
||||
|
|
@ -693,6 +700,20 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get sync_button_sync => '同步进度';
|
||||
|
||||
@override
|
||||
String get sync_button_upload => 'Upload only';
|
||||
|
||||
@override
|
||||
String get sync_button_upload_info =>
|
||||
'This operation will fully replace the remote data with local data!';
|
||||
|
||||
@override
|
||||
String get sync_button_download => 'Download only';
|
||||
|
||||
@override
|
||||
String get sync_button_download_info =>
|
||||
'This operation will fully replace the local data with remote data!';
|
||||
|
||||
@override
|
||||
String get sync_on => '启用同步';
|
||||
|
||||
|
|
@ -775,6 +796,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get novel_extensions => '小说扩展';
|
||||
|
||||
@override
|
||||
String get extension_settings => 'Extension settings';
|
||||
|
||||
@override
|
||||
String get anime => '动画';
|
||||
|
||||
|
|
@ -1333,6 +1357,27 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get downloads_are_limited_to_wifi => '下载仅限于WiFi';
|
||||
|
||||
@override
|
||||
String get recommendations => 'Recommendations';
|
||||
|
||||
@override
|
||||
String get recommendations_similar => 'similar';
|
||||
|
||||
@override
|
||||
String get recommendations_weights => 'Recommendation Weights';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_genre => 'Genre Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_setting => 'Setting Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_synopsis => 'Story Similarity';
|
||||
|
||||
@override
|
||||
String get recommendations_weights_theme => 'Theme Similarity';
|
||||
|
||||
@override
|
||||
String get manga_extensions_repo => '漫画扩展库';
|
||||
|
||||
|
|
|
|||
|
|
@ -275,6 +275,10 @@ class Settings {
|
|||
late AudioChannel audioChannels;
|
||||
|
||||
int? volumeBoostCap;
|
||||
|
||||
bool? downloadedOnlyMode;
|
||||
|
||||
late AlgorithmWeights? algorithmWeights;
|
||||
|
||||
Settings({
|
||||
this.id = 227,
|
||||
|
|
@ -399,6 +403,8 @@ class Settings {
|
|||
this.enableAudioPitchCorrection,
|
||||
this.audioChannels = AudioChannel.autoSafe,
|
||||
this.volumeBoostCap,
|
||||
this.downloadedOnlyMode = false,
|
||||
this.algorithmWeights,
|
||||
});
|
||||
|
||||
Settings.fromJson(Map<String, dynamic> json) {
|
||||
|
|
@ -630,6 +636,10 @@ class Settings {
|
|||
audioChannels = AudioChannel
|
||||
.values[json['audioChannels'] ?? AudioChannel.autoSafe.index];
|
||||
volumeBoostCap = json['volumeBoostCap'];
|
||||
downloadedOnlyMode = json['downloadedOnlyMode'];
|
||||
algorithmWeights = json['algorithmWeights'] != null
|
||||
? AlgorithmWeights.fromJson(json['algorithmWeights'])
|
||||
: null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
|
@ -776,6 +786,9 @@ class Settings {
|
|||
'enableAudioPitchCorrection': enableAudioPitchCorrection,
|
||||
'audioChannels': audioChannels.index,
|
||||
'volumeBoostCap': volumeBoostCap,
|
||||
'downloadedOnlyMode': downloadedOnlyMode,
|
||||
if (algorithmWeights != null)
|
||||
'algorithmWeights': algorithmWeights!.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1146,6 +1159,35 @@ class PlayerSubtitleSettings {
|
|||
};
|
||||
}
|
||||
|
||||
@embedded
|
||||
class AlgorithmWeights {
|
||||
int? genre;
|
||||
int? setting;
|
||||
int? synopsis;
|
||||
int? theme;
|
||||
|
||||
AlgorithmWeights({
|
||||
this.genre = 30,
|
||||
this.setting = 15,
|
||||
this.synopsis = 40,
|
||||
this.theme = 20,
|
||||
});
|
||||
|
||||
AlgorithmWeights.fromJson(Map<String, dynamic> json) {
|
||||
genre = json['genre'];
|
||||
setting = json['setting'];
|
||||
synopsis = json['synopsis'];
|
||||
theme = json['theme'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'genre': genre,
|
||||
'setting': setting,
|
||||
'synopsis': synopsis,
|
||||
'theme': theme,
|
||||
};
|
||||
}
|
||||
|
||||
enum ColorFilterBlendMode {
|
||||
none,
|
||||
multiply,
|
||||
|
|
|
|||
|
|
@ -124,12 +124,14 @@ class _BrowseScreenState extends ConsumerState<BrowseScreen>
|
|||
} else {
|
||||
context.push(
|
||||
'/globalSearch',
|
||||
extra:
|
||||
switch (_tabList[_tabBarController.index]) {
|
||||
"manga" => ItemType.manga,
|
||||
"anime" => ItemType.anime,
|
||||
_ => ItemType.novel,
|
||||
},
|
||||
extra: (
|
||||
null,
|
||||
switch (_tabList[_tabBarController.index]) {
|
||||
"manga" => ItemType.manga,
|
||||
"anime" => ItemType.anime,
|
||||
_ => ItemType.novel,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,15 +24,16 @@ import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart';
|
|||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
|
||||
class GlobalSearchScreen extends ConsumerStatefulWidget {
|
||||
final String? search;
|
||||
final ItemType itemType;
|
||||
const GlobalSearchScreen({required this.itemType, super.key});
|
||||
const GlobalSearchScreen({this.search, required this.itemType, super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<GlobalSearchScreen> createState() => _GlobalSearchScreenState();
|
||||
}
|
||||
|
||||
class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
|
||||
String query = "";
|
||||
String _query = "";
|
||||
final _textEditingController = TextEditingController();
|
||||
late final List<Source> sourceList =
|
||||
ref.read(onlyIncludePinnedSourceStateProvider)
|
||||
|
|
@ -50,8 +51,17 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
|
|||
.and()
|
||||
.itemTypeEqualTo(widget.itemType)
|
||||
.findAllSync();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_textEditingController.text = widget.search ?? "";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final query = _query.isNotEmpty ? _query : widget.search ?? "";
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Container(),
|
||||
|
|
@ -62,27 +72,27 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
|
|||
Navigator.pop(context);
|
||||
},
|
||||
onFieldSubmitted: (value) async {
|
||||
if (!(query == _textEditingController.text)) {
|
||||
if (!(_query == _textEditingController.text)) {
|
||||
setState(() {
|
||||
query = "";
|
||||
_query = "";
|
||||
});
|
||||
await Future.delayed(const Duration(milliseconds: 10));
|
||||
setState(() {
|
||||
query = value;
|
||||
_query = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
onSuffixPressed: () {
|
||||
_textEditingController.clear();
|
||||
setState(() {
|
||||
query = "";
|
||||
_query = "";
|
||||
});
|
||||
},
|
||||
controller: _textEditingController,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: query.isNotEmpty
|
||||
body: _query.isNotEmpty || widget.search != null
|
||||
? SuperListView.builder(
|
||||
itemCount: sourceList.length,
|
||||
extentPrecalculationPolicy: SuperPrecalculationPolicy(),
|
||||
|
|
@ -90,7 +100,11 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
|
|||
final source = sourceList[index];
|
||||
return SizedBox(
|
||||
height: 260,
|
||||
child: SourceSearchScreen(query: query, source: source),
|
||||
child: SourceSearchScreen(
|
||||
key: ValueKey(query),
|
||||
query: query,
|
||||
source: source,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@ import 'package:mangayomi/models/settings.dart';
|
|||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/modules/library/providers/add_torrent.dart';
|
||||
import 'package:mangayomi/modules/library/providers/local_archive.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart';
|
||||
import 'package:mangayomi/modules/more/categories/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/more/providers/downloaded_only_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/bottom_select_bar.dart';
|
||||
import 'package:mangayomi/modules/widgets/category_selection_dialog.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||
import 'package:mangayomi/modules/widgets/manga_image_card_widget.dart';
|
||||
|
|
@ -134,6 +137,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
final withoutCategories = ref.watch(
|
||||
getAllMangaWithoutCategoriesStreamProvider(itemType: widget.itemType),
|
||||
);
|
||||
final downloadedOnly = ref.watch(downloadedOnlyStateProvider);
|
||||
final mangaAll = ref.watch(
|
||||
getAllMangaStreamProvider(
|
||||
categoryId: null,
|
||||
|
|
@ -247,6 +251,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
ref: ref,
|
||||
localSource: localSource,
|
||||
settings: settings,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
}
|
||||
if (tabCount > 0 &&
|
||||
|
|
@ -279,6 +284,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
startedFilterType: startedFilterType,
|
||||
bookmarkedFilterType: bookmarkedFilterType,
|
||||
sortType: sortType,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
final withoutCategoryNumberOfItemsList =
|
||||
_filterAndSortManga(
|
||||
|
|
@ -288,6 +294,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
startedFilterType: startedFilterType,
|
||||
bookmarkedFilterType: bookmarkedFilterType,
|
||||
sortType: sortType,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
|
||||
return DefaultTabController(
|
||||
|
|
@ -370,6 +377,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
categoryId:
|
||||
entr[i - 1].id!,
|
||||
settings: settings,
|
||||
downloadedOnly:
|
||||
downloadedOnly,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -396,6 +405,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
continueReaderBtn,
|
||||
categoryId: entr[i].id!,
|
||||
settings: settings,
|
||||
downloadedOnly:
|
||||
downloadedOnly,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -432,6 +443,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
ref: ref,
|
||||
localSource: localSource,
|
||||
settings: settings,
|
||||
downloadedOnly:
|
||||
downloadedOnly,
|
||||
)
|
||||
: _bodyWithCatories(
|
||||
categoryId:
|
||||
|
|
@ -454,6 +467,8 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
ref: ref,
|
||||
localSource: localSource,
|
||||
settings: settings,
|
||||
downloadedOnly:
|
||||
downloadedOnly,
|
||||
),
|
||||
if (withoutCategory.isEmpty)
|
||||
for (
|
||||
|
|
@ -481,6 +496,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
ref: ref,
|
||||
localSource: localSource,
|
||||
settings: settings,
|
||||
downloadedOnly: downloadedOnly,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -501,6 +517,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
startedFilterType: startedFilterType,
|
||||
bookmarkedFilterType: bookmarkedFilterType,
|
||||
sortType: sortType,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: _appBar(
|
||||
|
|
@ -526,6 +543,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
ref: ref,
|
||||
localSource: localSource,
|
||||
settings: settings,
|
||||
downloadedOnly: downloadedOnly,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -554,159 +572,85 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
return const ProgressCenter();
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
|
||||
final color = Theme.of(context).textTheme.bodyLarge!.color!;
|
||||
bottomNavigationBar: Builder(
|
||||
builder: (context) {
|
||||
final mangaIds = ref.watch(mangasListStateProvider);
|
||||
return AnimatedContainer(
|
||||
curve: Curves.easeIn,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColor.withValues(alpha: 0.2),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
final color = Theme.of(context).textTheme.bodyLarge!.color!;
|
||||
return BottomSelectBar(
|
||||
isVisible: ref.watch(isLongPressedStateProvider),
|
||||
actions: [
|
||||
BottomSelectButton(
|
||||
icon: Icon(Icons.label_outline_rounded, color: color),
|
||||
onPressed: () {
|
||||
final mangaIdsList = ref.watch(mangasListStateProvider);
|
||||
final List<Manga> bulkMangas = mangaIdsList
|
||||
.map((id) => isar.mangas.getSync(id)!)
|
||||
.toList();
|
||||
showCategorySelectionDialog(
|
||||
context: context,
|
||||
ref: ref,
|
||||
itemType: widget.itemType,
|
||||
bulkMangas: bulkMangas,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
duration: const Duration(milliseconds: 100),
|
||||
height: isLongPressed ? 70 : 0,
|
||||
width: context.width(1),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
shadowColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
final mangaIdsList = ref.watch(
|
||||
mangasListStateProvider,
|
||||
);
|
||||
final List<Manga> bulkMangas = mangaIdsList
|
||||
.map((id) => isar.mangas.getSync(id)!)
|
||||
.toList();
|
||||
showCategorySelectionDialog(
|
||||
context: context,
|
||||
ref: ref,
|
||||
itemType: widget.itemType,
|
||||
bulkMangas: bulkMangas,
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.label_outline_rounded,
|
||||
color: color,
|
||||
),
|
||||
BottomSelectButton(
|
||||
icon: Icon(Icons.done_all_sharp, color: color),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(
|
||||
mangasSetIsReadStateProvider(
|
||||
mangaIds: mangaIds,
|
||||
markAsRead: true,
|
||||
).notifier,
|
||||
)
|
||||
.set();
|
||||
ref.invalidate(
|
||||
getAllMangaWithoutCategoriesStreamProvider(
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(
|
||||
mangasSetIsReadStateProvider(
|
||||
mangaIds: mangaIds,
|
||||
).notifier,
|
||||
)
|
||||
.set();
|
||||
ref.invalidate(
|
||||
getAllMangaWithoutCategoriesStreamProvider(
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
);
|
||||
ref.invalidate(
|
||||
getAllMangaStreamProvider(
|
||||
categoryId: null,
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(Icons.done_all_sharp, color: color),
|
||||
);
|
||||
ref.invalidate(
|
||||
getAllMangaStreamProvider(
|
||||
categoryId: null,
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(
|
||||
mangasSetUnReadStateProvider(
|
||||
mangaIds: mangaIds,
|
||||
).notifier,
|
||||
)
|
||||
.set();
|
||||
ref.invalidate(
|
||||
getAllMangaWithoutCategoriesStreamProvider(
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
);
|
||||
ref.invalidate(
|
||||
getAllMangaStreamProvider(
|
||||
categoryId: null,
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(Icons.remove_done_sharp, color: color),
|
||||
);
|
||||
},
|
||||
),
|
||||
BottomSelectButton(
|
||||
icon: Icon(Icons.remove_done_sharp, color: color),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(
|
||||
mangasSetIsReadStateProvider(
|
||||
mangaIds: mangaIds,
|
||||
markAsRead: false,
|
||||
).notifier,
|
||||
)
|
||||
.set();
|
||||
ref.invalidate(
|
||||
getAllMangaWithoutCategoriesStreamProvider(
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: SizedBox(
|
||||
// height: 70,
|
||||
// child: ElevatedButton(
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// elevation: 0,
|
||||
// backgroundColor: Colors.transparent,
|
||||
// shadowColor: Colors.transparent,
|
||||
// ),
|
||||
// onPressed: () {},
|
||||
// child: Icon(
|
||||
// Icons.download_outlined,
|
||||
// color: color,
|
||||
// )),
|
||||
// ),
|
||||
// ),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
_deleteManga();
|
||||
},
|
||||
child: Icon(
|
||||
Icons.delete_outline_outlined,
|
||||
color: color,
|
||||
),
|
||||
);
|
||||
ref.invalidate(
|
||||
getAllMangaStreamProvider(
|
||||
categoryId: null,
|
||||
itemType: widget.itemType,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// BottomBarAction(
|
||||
// icon: Icon(Icons.download_outlined, color: color),
|
||||
// onPressed: () {}
|
||||
// ),
|
||||
BottomSelectButton(
|
||||
icon: Icon(Icons.delete_outline_outlined, color: color),
|
||||
onPressed: () => _deleteManga(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -731,6 +675,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
required bool continueReaderBtn,
|
||||
required int categoryId,
|
||||
required Settings settings,
|
||||
required bool downloadedOnly,
|
||||
}) {
|
||||
final mangas = ref.watch(
|
||||
getAllMangaStreamProvider(
|
||||
|
|
@ -755,6 +700,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
startedFilterType: startedFilterType,
|
||||
bookmarkedFilterType: bookmarkedFilterType,
|
||||
sortType: sortType!,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
return CircleAvatar(
|
||||
backgroundColor: Theme.of(context).focusColor,
|
||||
|
|
@ -791,6 +737,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
required WidgetRef ref,
|
||||
required DisplayType displayType,
|
||||
required Settings settings,
|
||||
required bool downloadedOnly,
|
||||
}) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final mangas = ref.watch(
|
||||
|
|
@ -818,6 +765,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
startedFilterType: startedFilterType,
|
||||
bookmarkedFilterType: bookmarkedFilterType,
|
||||
sortType: sortType!,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
if (entries.isNotEmpty) {
|
||||
final entriesManga = reverse ? entries.reversed.toList() : entries;
|
||||
|
|
@ -875,6 +823,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
required WidgetRef ref,
|
||||
bool withoutCategories = false,
|
||||
required Settings settings,
|
||||
required bool downloadedOnly,
|
||||
}) {
|
||||
final sortType = ref
|
||||
.watch(
|
||||
|
|
@ -907,6 +856,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
startedFilterType: startedFilterType,
|
||||
bookmarkedFilterType: bookmarkedFilterType,
|
||||
sortType: sortType ?? 0,
|
||||
downloadedOnly: downloadedOnly,
|
||||
);
|
||||
if (entries.isNotEmpty) {
|
||||
final entriesManga = reverse ? entries.reversed.toList() : entries;
|
||||
|
|
@ -970,6 +920,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
required int startedFilterType,
|
||||
required int bookmarkedFilterType,
|
||||
required int sortType,
|
||||
bool downloadedOnly = false,
|
||||
}) {
|
||||
List<Manga>? mangas;
|
||||
final searchQuery = _textEditingController.text;
|
||||
|
|
@ -984,7 +935,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
.where((element) {
|
||||
// Filter by download
|
||||
List list = [];
|
||||
if (downloadFilterType == 1) {
|
||||
if (downloadFilterType == 1 || downloadedOnly) {
|
||||
for (var chap in element.chapters) {
|
||||
final modelChapDownload = isar.downloads
|
||||
.filter()
|
||||
|
|
@ -1206,7 +1157,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
|
||||
ref.read(mangasListStateProvider.notifier).clear();
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
|
|
@ -1859,7 +1810,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
int? categoryId,
|
||||
Settings settings,
|
||||
) {
|
||||
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
|
||||
final isLongPressed = ref.watch(isLongPressedStateProvider);
|
||||
final mangaIdsList = ref.watch(mangasListStateProvider);
|
||||
final manga = categoryId == null
|
||||
? ref.watch(
|
||||
|
|
@ -1888,7 +1839,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
ref.read(mangasListStateProvider.notifier).clear();
|
||||
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(!isLongPressed);
|
||||
},
|
||||
icon: const Icon(Icons.clear),
|
||||
|
|
@ -1913,7 +1864,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
.selectSome(manga);
|
||||
}
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
} else {
|
||||
for (var manga in data) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:mangayomi/main.dart';
|
|||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
|
@ -785,7 +786,7 @@ class MangasListState extends _$MangasListState {
|
|||
newList.add(value.id!);
|
||||
}
|
||||
if (newList.isEmpty) {
|
||||
ref.read(isLongPressedMangaStateProvider.notifier).update(false);
|
||||
ref.read(isLongPressedStateProvider.notifier).update(false);
|
||||
}
|
||||
state = newList;
|
||||
}
|
||||
|
|
@ -814,65 +815,24 @@ class MangasListState extends _$MangasListState {
|
|||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class IsLongPressedMangaState extends _$IsLongPressedMangaState {
|
||||
@override
|
||||
bool build() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void update(bool value) {
|
||||
state = value;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class MangasSetIsReadState extends _$MangasSetIsReadState {
|
||||
@override
|
||||
void build({required List<int> mangaIds}) {}
|
||||
void build({required List<int> mangaIds, required bool markAsRead}) {}
|
||||
|
||||
void set() {
|
||||
final allChapters = <Chapter>[];
|
||||
final allMangas = <Manga>[];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var mangaid in mangaIds) {
|
||||
final manga = isar.mangas.getSync(mangaid)!;
|
||||
final chapters = manga.chapters;
|
||||
if (chapters.isNotEmpty) {
|
||||
chapters.last.updateTrackChapterRead(ref);
|
||||
for (var chapter in chapters) {
|
||||
chapter.isRead = true;
|
||||
chapter.lastPageRead = "1";
|
||||
chapter.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||
chapter.manga.value = manga;
|
||||
allChapters.add(chapter);
|
||||
}
|
||||
allMangas.add(manga);
|
||||
}
|
||||
}
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(allChapters);
|
||||
isar.mangas.putAllSync(allMangas);
|
||||
});
|
||||
|
||||
ref.read(isLongPressedMangaStateProvider.notifier).update(false);
|
||||
ref.read(mangasListStateProvider.notifier).clear();
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class MangasSetUnReadState extends _$MangasSetUnReadState {
|
||||
@override
|
||||
void build({required List<int> mangaIds}) {}
|
||||
|
||||
void set() {
|
||||
final allChapters = <Chapter>[];
|
||||
final allMangas = <Manga>[];
|
||||
for (var mangaid in mangaIds) {
|
||||
final manga = isar.mangas.getSync(mangaid)!;
|
||||
for (var chapter in manga.chapters) {
|
||||
chapter.isRead = false;
|
||||
chapter.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||
if (chapters.isEmpty) continue;
|
||||
if (markAsRead) chapters.last.updateTrackChapterRead(ref);
|
||||
for (var chapter in chapters) {
|
||||
chapter.isRead = markAsRead;
|
||||
if (markAsRead) chapter.lastPageRead = "1";
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = manga;
|
||||
allChapters.add(chapter);
|
||||
}
|
||||
|
|
@ -884,7 +844,7 @@ class MangasSetUnReadState extends _$MangasSetUnReadState {
|
|||
isar.mangas.putAllSync(allMangas);
|
||||
});
|
||||
|
||||
ref.read(isLongPressedMangaStateProvider.notifier).update(false);
|
||||
ref.read(isLongPressedStateProvider.notifier).update(false);
|
||||
ref.read(mangasListStateProvider.notifier).clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2517,7 +2517,7 @@ class _SortLibraryMangaStateProviderElement
|
|||
Settings get settings => (origin as SortLibraryMangaStateProvider).settings;
|
||||
}
|
||||
|
||||
String _$mangasListStateHash() => r'ad1cc419dfd3793bfc8c90f3ce8b7726561dd9ad';
|
||||
String _$mangasListStateHash() => r'bbd2e3600ec22a774b1774ae3c221815e52bfef6';
|
||||
|
||||
/// See also [MangasListState].
|
||||
@ProviderFor(MangasListState)
|
||||
|
|
@ -2533,32 +2533,17 @@ final mangasListStateProvider =
|
|||
);
|
||||
|
||||
typedef _$MangasListState = AutoDisposeNotifier<List<int>>;
|
||||
String _$isLongPressedMangaStateHash() =>
|
||||
r'f77076b0335e92df26a75ea0c338d4214a330184';
|
||||
|
||||
/// See also [IsLongPressedMangaState].
|
||||
@ProviderFor(IsLongPressedMangaState)
|
||||
final isLongPressedMangaStateProvider =
|
||||
AutoDisposeNotifierProvider<IsLongPressedMangaState, bool>.internal(
|
||||
IsLongPressedMangaState.new,
|
||||
name: r'isLongPressedMangaStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$isLongPressedMangaStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$IsLongPressedMangaState = AutoDisposeNotifier<bool>;
|
||||
String _$mangasSetIsReadStateHash() =>
|
||||
r'8f4f7f22ea8e82cf2370fb997033e1a4ec03168f';
|
||||
r'2a1b1005e2ed5068d36188a3fb969d21b64bfef6';
|
||||
|
||||
abstract class _$MangasSetIsReadState
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
late final List<int> mangaIds;
|
||||
late final bool markAsRead;
|
||||
|
||||
void build({
|
||||
required List<int> mangaIds,
|
||||
required bool markAsRead,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2574,9 +2559,11 @@ class MangasSetIsReadStateFamily extends Family<void> {
|
|||
/// See also [MangasSetIsReadState].
|
||||
MangasSetIsReadStateProvider call({
|
||||
required List<int> mangaIds,
|
||||
required bool markAsRead,
|
||||
}) {
|
||||
return MangasSetIsReadStateProvider(
|
||||
mangaIds: mangaIds,
|
||||
markAsRead: markAsRead,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2586,6 +2573,7 @@ class MangasSetIsReadStateFamily extends Family<void> {
|
|||
) {
|
||||
return call(
|
||||
mangaIds: provider.mangaIds,
|
||||
markAsRead: provider.markAsRead,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2610,8 +2598,11 @@ class MangasSetIsReadStateProvider
|
|||
/// See also [MangasSetIsReadState].
|
||||
MangasSetIsReadStateProvider({
|
||||
required List<int> mangaIds,
|
||||
required bool markAsRead,
|
||||
}) : this._internal(
|
||||
() => MangasSetIsReadState()..mangaIds = mangaIds,
|
||||
() => MangasSetIsReadState()
|
||||
..mangaIds = mangaIds
|
||||
..markAsRead = markAsRead,
|
||||
from: mangasSetIsReadStateProvider,
|
||||
name: r'mangasSetIsReadStateProvider',
|
||||
debugGetCreateSourceHash:
|
||||
|
|
@ -2622,6 +2613,7 @@ class MangasSetIsReadStateProvider
|
|||
allTransitiveDependencies:
|
||||
MangasSetIsReadStateFamily._allTransitiveDependencies,
|
||||
mangaIds: mangaIds,
|
||||
markAsRead: markAsRead,
|
||||
);
|
||||
|
||||
MangasSetIsReadStateProvider._internal(
|
||||
|
|
@ -2632,9 +2624,11 @@ class MangasSetIsReadStateProvider
|
|||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.mangaIds,
|
||||
required this.markAsRead,
|
||||
}) : super.internal();
|
||||
|
||||
final List<int> mangaIds;
|
||||
final bool markAsRead;
|
||||
|
||||
@override
|
||||
void runNotifierBuild(
|
||||
|
|
@ -2642,6 +2636,7 @@ class MangasSetIsReadStateProvider
|
|||
) {
|
||||
return notifier.build(
|
||||
mangaIds: mangaIds,
|
||||
markAsRead: markAsRead,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2650,13 +2645,16 @@ class MangasSetIsReadStateProvider
|
|||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: MangasSetIsReadStateProvider._internal(
|
||||
() => create()..mangaIds = mangaIds,
|
||||
() => create()
|
||||
..mangaIds = mangaIds
|
||||
..markAsRead = markAsRead,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
mangaIds: mangaIds,
|
||||
markAsRead: markAsRead,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -2669,13 +2667,16 @@ class MangasSetIsReadStateProvider
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MangasSetIsReadStateProvider && other.mangaIds == mangaIds;
|
||||
return other is MangasSetIsReadStateProvider &&
|
||||
other.mangaIds == mangaIds &&
|
||||
other.markAsRead == markAsRead;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, mangaIds.hashCode);
|
||||
hash = _SystemHash.combine(hash, markAsRead.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
@ -2686,6 +2687,9 @@ class MangasSetIsReadStateProvider
|
|||
mixin MangasSetIsReadStateRef on AutoDisposeNotifierProviderRef<void> {
|
||||
/// The parameter `mangaIds` of this provider.
|
||||
List<int> get mangaIds;
|
||||
|
||||
/// The parameter `markAsRead` of this provider.
|
||||
bool get markAsRead;
|
||||
}
|
||||
|
||||
class _MangasSetIsReadStateProviderElement
|
||||
|
|
@ -2695,153 +2699,8 @@ class _MangasSetIsReadStateProviderElement
|
|||
|
||||
@override
|
||||
List<int> get mangaIds => (origin as MangasSetIsReadStateProvider).mangaIds;
|
||||
}
|
||||
|
||||
String _$mangasSetUnReadStateHash() =>
|
||||
r'09ddd287b110fd76494f9f56bd5cf76f58936f1f';
|
||||
|
||||
abstract class _$MangasSetUnReadState
|
||||
extends BuildlessAutoDisposeNotifier<void> {
|
||||
late final List<int> mangaIds;
|
||||
|
||||
void build({
|
||||
required List<int> mangaIds,
|
||||
});
|
||||
}
|
||||
|
||||
/// See also [MangasSetUnReadState].
|
||||
@ProviderFor(MangasSetUnReadState)
|
||||
const mangasSetUnReadStateProvider = MangasSetUnReadStateFamily();
|
||||
|
||||
/// See also [MangasSetUnReadState].
|
||||
class MangasSetUnReadStateFamily extends Family<void> {
|
||||
/// See also [MangasSetUnReadState].
|
||||
const MangasSetUnReadStateFamily();
|
||||
|
||||
/// See also [MangasSetUnReadState].
|
||||
MangasSetUnReadStateProvider call({
|
||||
required List<int> mangaIds,
|
||||
}) {
|
||||
return MangasSetUnReadStateProvider(
|
||||
mangaIds: mangaIds,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
MangasSetUnReadStateProvider getProviderOverride(
|
||||
covariant MangasSetUnReadStateProvider provider,
|
||||
) {
|
||||
return call(
|
||||
mangaIds: provider.mangaIds,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'mangasSetUnReadStateProvider';
|
||||
}
|
||||
|
||||
/// See also [MangasSetUnReadState].
|
||||
class MangasSetUnReadStateProvider
|
||||
extends AutoDisposeNotifierProviderImpl<MangasSetUnReadState, void> {
|
||||
/// See also [MangasSetUnReadState].
|
||||
MangasSetUnReadStateProvider({
|
||||
required List<int> mangaIds,
|
||||
}) : this._internal(
|
||||
() => MangasSetUnReadState()..mangaIds = mangaIds,
|
||||
from: mangasSetUnReadStateProvider,
|
||||
name: r'mangasSetUnReadStateProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$mangasSetUnReadStateHash,
|
||||
dependencies: MangasSetUnReadStateFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
MangasSetUnReadStateFamily._allTransitiveDependencies,
|
||||
mangaIds: mangaIds,
|
||||
);
|
||||
|
||||
MangasSetUnReadStateProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.mangaIds,
|
||||
}) : super.internal();
|
||||
|
||||
final List<int> mangaIds;
|
||||
|
||||
@override
|
||||
void runNotifierBuild(
|
||||
covariant MangasSetUnReadState notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
mangaIds: mangaIds,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(MangasSetUnReadState Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: MangasSetUnReadStateProvider._internal(
|
||||
() => create()..mangaIds = mangaIds,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
mangaIds: mangaIds,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeNotifierProviderElement<MangasSetUnReadState, void>
|
||||
createElement() {
|
||||
return _MangasSetUnReadStateProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MangasSetUnReadStateProvider && other.mangaIds == mangaIds;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, mangaIds.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin MangasSetUnReadStateRef on AutoDisposeNotifierProviderRef<void> {
|
||||
/// The parameter `mangaIds` of this provider.
|
||||
List<int> get mangaIds;
|
||||
}
|
||||
|
||||
class _MangasSetUnReadStateProviderElement
|
||||
extends AutoDisposeNotifierProviderElement<MangasSetUnReadState, void>
|
||||
with MangasSetUnReadStateRef {
|
||||
_MangasSetUnReadStateProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
List<int> get mangaIds => (origin as MangasSetUnReadStateProvider).mangaIds;
|
||||
bool get markAsRead => (origin as MangasSetIsReadStateProvider).markAsRead;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:mangayomi/models/history.dart';
|
|||
import 'package:mangayomi/modules/library/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
|
@ -52,7 +53,7 @@ class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
|
|||
Widget build(BuildContext context) {
|
||||
return Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
|
||||
final isLongPressed = ref.watch(isLongPressedStateProvider);
|
||||
final itemType = widget.itemType;
|
||||
|
||||
final gridSize = ref.watch(
|
||||
|
|
@ -430,7 +431,7 @@ class _LibraryGridViewWidgetState extends State<LibraryGridViewWidget> {
|
|||
) {
|
||||
if (!isLongPressed) {
|
||||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
ref.read(isLongPressedMangaStateProvider.notifier).update(!isLongPressed);
|
||||
ref.read(isLongPressedStateProvider.notifier).update(!isLongPressed);
|
||||
} else {
|
||||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:mangayomi/models/history.dart';
|
|||
import 'package:mangayomi/modules/library/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
|
@ -44,7 +45,7 @@ class LibraryListViewWidget extends StatelessWidget {
|
|||
bool isLocalArchive = entry.isLocalArchive ?? false;
|
||||
return Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
|
||||
final isLongPressed = ref.watch(isLongPressedStateProvider);
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.transparent,
|
||||
|
|
@ -80,7 +81,7 @@ class LibraryListViewWidget extends StatelessWidget {
|
|||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(!isLongPressed);
|
||||
} else {
|
||||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
|
|
@ -91,7 +92,7 @@ class LibraryListViewWidget extends StatelessWidget {
|
|||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(!isLongPressed);
|
||||
} else {
|
||||
ref.read(mangasListStateProvider.notifier).update(entry);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import 'package:mangayomi/models/chapter.dart';
|
|||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/more/providers/downloaded_only_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/loading_icon.dart';
|
||||
|
|
@ -23,7 +24,7 @@ import 'package:mangayomi/router/router.dart';
|
|||
import 'package:mangayomi/services/fetch_sources_list.dart';
|
||||
import 'package:mangayomi/services/sync_server.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
|
||||
|
||||
final libLocationRegex = RegExp(r"^/(Manga|Anime|Novel)Library$");
|
||||
|
|
@ -239,10 +240,16 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
|||
}
|
||||
|
||||
final incognitoMode = ref.watch(incognitoModeStateProvider);
|
||||
final isLongPressed = ref.watch(isLongPressedMangaStateProvider);
|
||||
final downloadedOnly = ref.watch(downloadedOnlyStateProvider);
|
||||
final isLongPressed = ref.watch(isLongPressedStateProvider);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (!isReadingScreen)
|
||||
_DownloadedOnlyBar(
|
||||
downloadedOnly: downloadedOnly,
|
||||
l10n: l10n,
|
||||
),
|
||||
if (!isReadingScreen)
|
||||
_IncognitoModeBar(incognitoMode: incognitoMode, l10n: l10n),
|
||||
Flexible(
|
||||
|
|
@ -526,6 +533,45 @@ class _MainScreenState extends ConsumerState<MainScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
class _DownloadedOnlyBar extends StatelessWidget {
|
||||
const _DownloadedOnlyBar({required this.downloadedOnly, required this.l10n});
|
||||
|
||||
final bool downloadedOnly;
|
||||
final dynamic l10n;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
child: AnimatedContainer(
|
||||
height: downloadedOnly
|
||||
? Platform.isAndroid || Platform.isIOS
|
||||
? MediaQuery.of(context).padding.top * 2
|
||||
: 50
|
||||
: 0,
|
||||
curve: Curves.easeIn,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
color: context.secondaryColor,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
l10n.downloaded_only,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontFamily: GoogleFonts.aBeeZee().fontFamily,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IncognitoModeBar extends StatelessWidget {
|
||||
const _IncognitoModeBar({required this.incognitoMode, required this.l10n});
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.d
|
|||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_search_widget.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_widget.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/more/providers/algorithm_weights_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_dark_mode_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart';
|
||||
import 'package:mangayomi/modules/widgets/bottom_select_bar.dart';
|
||||
import 'package:mangayomi/modules/widgets/category_selection_dialog.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
|
|
@ -624,6 +626,11 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
value: 3,
|
||||
child: Text(l10n.migrate),
|
||||
),
|
||||
if (!isLocalArchive)
|
||||
PopupMenuItem<int>(
|
||||
value: 4,
|
||||
child: Text(l10n.extension_settings),
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
|
|
@ -651,6 +658,16 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
case 3:
|
||||
context.push("/migrate", extra: widget.manga);
|
||||
break;
|
||||
case 4:
|
||||
final source = getSource(
|
||||
widget.manga!.lang!,
|
||||
widget.manga!.source!,
|
||||
);
|
||||
context.push(
|
||||
'/extension_detail',
|
||||
extra: source,
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
@ -811,8 +828,8 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: Consumer(
|
||||
builder: (context, ref, child) {
|
||||
bottomNavigationBar: Builder(
|
||||
builder: (context) {
|
||||
final chap = ref.watch(chaptersListStateProvider);
|
||||
bool getLength1 = chap.length == 1;
|
||||
bool checkFirstBookmarked =
|
||||
|
|
@ -821,340 +838,241 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
chap.isNotEmpty && chap.first.isRead! && getLength1;
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final color = Theme.of(context).textTheme.bodyLarge!.color!;
|
||||
return AnimatedContainer(
|
||||
curve: Curves.easeIn,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColor.withValues(alpha: 0.2),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
return BottomSelectBar(
|
||||
isVisible: isLongPressed,
|
||||
actions: [
|
||||
BottomSelectButton(
|
||||
icon: Icon(
|
||||
checkFirstBookmarked
|
||||
? Icons.bookmark_remove_outlined
|
||||
: Icons.bookmark_add_outlined,
|
||||
color: color,
|
||||
),
|
||||
onPressed: () {
|
||||
final chapters = ref.watch(chaptersListStateProvider);
|
||||
final List<Chapter> updatedChapters = [];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var chapter in chapters) {
|
||||
chapter.isBookmarked = !chapter.isBookmarked!;
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = widget.manga;
|
||||
updatedChapters.add(chapter);
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(updatedChapters);
|
||||
});
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref.read(chaptersListStateProvider.notifier).clear();
|
||||
},
|
||||
),
|
||||
),
|
||||
duration: const Duration(milliseconds: 100),
|
||||
height: isLongPressed ? 70 : 0,
|
||||
width: context.width(1),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
final chapters = ref.watch(
|
||||
chaptersListStateProvider,
|
||||
);
|
||||
final List<Chapter> updatedChapters = [];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var chapter in chapters) {
|
||||
chapter.isBookmarked = !chapter.isBookmarked!;
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = widget.manga;
|
||||
updatedChapters.add(chapter);
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(updatedChapters);
|
||||
});
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref
|
||||
.read(chaptersListStateProvider.notifier)
|
||||
.clear();
|
||||
},
|
||||
child: Icon(
|
||||
checkFirstBookmarked
|
||||
? Icons.bookmark_remove_outlined
|
||||
: Icons.bookmark_add_outlined,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
BottomSelectButton(
|
||||
icon: Icon(
|
||||
checkReadBookmarked
|
||||
? Icons.remove_done_sharp
|
||||
: Icons.done_all_sharp,
|
||||
color: color,
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
final chapters = ref.watch(
|
||||
chaptersListStateProvider,
|
||||
);
|
||||
final List<Chapter> updatedChapters = [];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var chapter in chapters) {
|
||||
chapter.isRead = !chapter.isRead!;
|
||||
if (!chapter.isRead!) {
|
||||
chapter.lastPageRead = "1";
|
||||
}
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = widget.manga;
|
||||
updatedChapters.add(chapter);
|
||||
if (chapter.isRead!) {
|
||||
chapter.updateTrackChapterRead(ref);
|
||||
}
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(updatedChapters);
|
||||
isar.mangas.putSync(widget.manga!);
|
||||
});
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref
|
||||
.read(chaptersListStateProvider.notifier)
|
||||
.clear();
|
||||
},
|
||||
child: Icon(
|
||||
checkReadBookmarked
|
||||
? Icons.remove_done_sharp
|
||||
: Icons.done_all_sharp,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (getLength1)
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
int index = chapters.indexOf(chap.first);
|
||||
final List<Chapter> updatedChapters = [];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
chapters[index + 1].updateTrackChapterRead(ref);
|
||||
for (
|
||||
var i = index + 1;
|
||||
i < chapters.length;
|
||||
i++
|
||||
) {
|
||||
final chapter = chapters[i];
|
||||
if (!chapter.isRead!) {
|
||||
chapter.isRead = true;
|
||||
chapter.lastPageRead = "1";
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = widget.manga;
|
||||
updatedChapters.add(chapter);
|
||||
}
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(updatedChapters);
|
||||
isar.mangas.putSync(widget.manga!);
|
||||
});
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref
|
||||
.read(chaptersListStateProvider.notifier)
|
||||
.clear();
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Icon(Icons.done_outlined, color: color),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Icon(
|
||||
Icons.arrow_downward_outlined,
|
||||
size: 11,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isLocalArchive)
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
for (var chapter in ref.watch(
|
||||
chaptersListStateProvider,
|
||||
)) {
|
||||
final entries = isar.downloads
|
||||
.filter()
|
||||
.idEqualTo(chapter.id)
|
||||
.findAllSync();
|
||||
if (entries.isEmpty ||
|
||||
!entries.first.isDownload!) {
|
||||
ref.read(
|
||||
addDownloadToQueueProvider(
|
||||
chapter: chapter,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
ref.watch(processDownloadsProvider());
|
||||
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref
|
||||
.read(chaptersListStateProvider.notifier)
|
||||
.clear();
|
||||
},
|
||||
child: Icon(Icons.download_outlined, color: color),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isLocalArchive)
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: () {
|
||||
final selectedChapters = ref.watch(
|
||||
chaptersListStateProvider,
|
||||
);
|
||||
final totalChapters =
|
||||
widget.manga!.chapters.length;
|
||||
final isLastChapters =
|
||||
selectedChapters.length == totalChapters;
|
||||
final isAnime = widget.itemType == ItemType.anime;
|
||||
final entryType = isAnime
|
||||
? l10n.episode
|
||||
: l10n.chapter;
|
||||
final pluralEntryType = isAnime
|
||||
? l10n.episodes
|
||||
: l10n.chapters;
|
||||
final mediaType = isAnime
|
||||
? l10n.anime
|
||||
: l10n.manga;
|
||||
final warningMessage = l10n
|
||||
.last_entry_delete_warning(
|
||||
totalChapters,
|
||||
entryType,
|
||||
pluralEntryType,
|
||||
mediaType,
|
||||
);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.delete_chapters),
|
||||
content: isLastChapters
|
||||
? Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
color: Colors.orange,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
warningMessage,
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final navigator = Navigator.of(
|
||||
context,
|
||||
);
|
||||
await isar.writeTxn(() async {
|
||||
final idsToDelete =
|
||||
selectedChapters
|
||||
.map((c) => c.id!)
|
||||
.toList();
|
||||
await isar.chapters.deleteAll(
|
||||
idsToDelete,
|
||||
);
|
||||
});
|
||||
if (!mounted) return;
|
||||
ref
|
||||
.read(
|
||||
isLongPressedStateProvider
|
||||
.notifier,
|
||||
)
|
||||
.update(false);
|
||||
ref
|
||||
.read(
|
||||
chaptersListStateProvider
|
||||
.notifier,
|
||||
)
|
||||
.clear();
|
||||
navigator.pop();
|
||||
if (isLastChapters) {
|
||||
navigator.pop();
|
||||
Future.delayed(
|
||||
const Duration(
|
||||
milliseconds: 350,
|
||||
),
|
||||
() {
|
||||
isar.writeTxn(
|
||||
() => isar.mangas.delete(
|
||||
widget.manga!.id!,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onPressed: () {
|
||||
final chapters = ref.watch(chaptersListStateProvider);
|
||||
final List<Chapter> updatedChapters = [];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
for (var chapter in chapters) {
|
||||
chapter.isRead = !chapter.isRead!;
|
||||
if (!chapter.isRead!) {
|
||||
chapter.lastPageRead = "1";
|
||||
}
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = widget.manga;
|
||||
updatedChapters.add(chapter);
|
||||
if (chapter.isRead!) {
|
||||
chapter.updateTrackChapterRead(ref);
|
||||
}
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(updatedChapters);
|
||||
isar.mangas.putSync(widget.manga!);
|
||||
});
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref.read(chaptersListStateProvider.notifier).clear();
|
||||
},
|
||||
),
|
||||
if (getLength1)
|
||||
BottomSelectButton(
|
||||
icon: Stack(
|
||||
children: [
|
||||
Icon(Icons.done_outlined, color: color),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Icon(
|
||||
Icons.delete_outline_outlined,
|
||||
Icons.arrow_downward_outlined,
|
||||
size: 11,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
int index = chapters.indexOf(chap.first);
|
||||
final List<Chapter> updatedChapters = [];
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
chapters[index + 1].updateTrackChapterRead(ref);
|
||||
for (var i = index + 1; i < chapters.length; i++) {
|
||||
final chapter = chapters[i];
|
||||
if (!chapter.isRead!) {
|
||||
chapter.isRead = true;
|
||||
chapter.lastPageRead = "1";
|
||||
chapter.updatedAt = now;
|
||||
chapter.manga.value = widget.manga;
|
||||
updatedChapters.add(chapter);
|
||||
}
|
||||
}
|
||||
isar.writeTxnSync(() {
|
||||
isar.chapters.putAllSync(updatedChapters);
|
||||
isar.mangas.putSync(widget.manga!);
|
||||
});
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref.read(chaptersListStateProvider.notifier).clear();
|
||||
},
|
||||
),
|
||||
if (!isLocalArchive)
|
||||
BottomSelectButton(
|
||||
icon: Icon(Icons.download_outlined, color: color),
|
||||
onPressed: () {
|
||||
for (var chapter in ref.watch(
|
||||
chaptersListStateProvider,
|
||||
)) {
|
||||
final entries = isar.downloads
|
||||
.filter()
|
||||
.idEqualTo(chapter.id)
|
||||
.findAllSync();
|
||||
if (entries.isEmpty || !entries.first.isDownload!) {
|
||||
ref.read(
|
||||
addDownloadToQueueProvider(chapter: chapter),
|
||||
);
|
||||
}
|
||||
}
|
||||
ref.watch(processDownloadsProvider());
|
||||
|
||||
ref
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
ref.read(chaptersListStateProvider.notifier).clear();
|
||||
},
|
||||
),
|
||||
if (isLocalArchive)
|
||||
BottomSelectButton(
|
||||
icon: Icon(Icons.delete_outline_outlined, color: color),
|
||||
onPressed: () {
|
||||
final selectedChapters = ref.watch(
|
||||
chaptersListStateProvider,
|
||||
);
|
||||
final totalChapters = widget.manga!.chapters.length;
|
||||
final isLastChapters =
|
||||
selectedChapters.length == totalChapters;
|
||||
final isAnime = widget.itemType == ItemType.anime;
|
||||
final entryType = isAnime ? l10n.episode : l10n.chapter;
|
||||
final pluralEntryType = isAnime
|
||||
? l10n.episodes
|
||||
: l10n.chapters;
|
||||
final mediaType = isAnime ? l10n.anime : l10n.manga;
|
||||
final warningMessage = l10n.last_entry_delete_warning(
|
||||
totalChapters,
|
||||
entryType,
|
||||
pluralEntryType,
|
||||
mediaType,
|
||||
);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.delete_chapters),
|
||||
content: isLastChapters
|
||||
? Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.warning_amber_rounded,
|
||||
color: Colors.orange,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
warningMessage,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final navigator = Navigator.of(context);
|
||||
await isar.writeTxn(() async {
|
||||
final idsToDelete = selectedChapters
|
||||
.map((c) => c.id!)
|
||||
.toList();
|
||||
await isar.chapters.deleteAll(
|
||||
idsToDelete,
|
||||
);
|
||||
});
|
||||
if (!mounted) return;
|
||||
ref
|
||||
.read(
|
||||
isLongPressedStateProvider
|
||||
.notifier,
|
||||
)
|
||||
.update(false);
|
||||
ref
|
||||
.read(
|
||||
chaptersListStateProvider
|
||||
.notifier,
|
||||
)
|
||||
.clear();
|
||||
navigator.pop();
|
||||
if (isLastChapters) {
|
||||
navigator.pop();
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 350),
|
||||
() {
|
||||
isar.writeTxn(
|
||||
() => isar.mangas.delete(
|
||||
widget.manga!.id!,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -1694,6 +1612,37 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
SizedBox(
|
||||
width: context.width(1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: OutlinedButton.icon(
|
||||
style: ButtonStyle(
|
||||
shape: WidgetStatePropertyAll(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
final algorithmWeights = ref.read(
|
||||
algorithmWeightsStateProvider,
|
||||
);
|
||||
context.push(
|
||||
"/recommendations",
|
||||
extra: (
|
||||
widget.manga!.name,
|
||||
widget.manga!.itemType,
|
||||
algorithmWeights,
|
||||
),
|
||||
);
|
||||
},
|
||||
label: Text(l10n.recommendations),
|
||||
icon: Icon(Icons.arrow_right_alt_outlined),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!context.isTablet)
|
||||
Column(
|
||||
children: [
|
||||
|
|
|
|||
383
lib/modules/manga/detail/widgets/recommendation_screen.dart
Normal file
383
lib/modules/manga/detail/widgets/recommendation_screen.dart
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_extended_image_provider.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/recommendation.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:marquee/marquee.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
|
||||
class RecommendationScreen extends StatefulWidget {
|
||||
final String name;
|
||||
final ItemType itemType;
|
||||
final AlgorithmWeights algorithmWeights;
|
||||
|
||||
const RecommendationScreen({
|
||||
super.key,
|
||||
required this.name,
|
||||
required this.itemType,
|
||||
required this.algorithmWeights,
|
||||
});
|
||||
|
||||
@override
|
||||
State<RecommendationScreen> createState() => _RecommendationScreenState();
|
||||
}
|
||||
|
||||
class _RecommendationScreenState extends State<RecommendationScreen> {
|
||||
String _errorMessage = "";
|
||||
bool _isLoading = true;
|
||||
List<RecommendationResult>? data;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_init();
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
try {
|
||||
_errorMessage = "";
|
||||
data = await getRecommendations(
|
||||
widget.name,
|
||||
widget.itemType,
|
||||
widget.algorithmWeights,
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_errorMessage = e.toString();
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.recommendations)),
|
||||
body: Padding(
|
||||
padding: EdgeInsetsGeometry.all(5),
|
||||
child: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Builder(
|
||||
builder: (context) {
|
||||
if (_errorMessage.isNotEmpty) {
|
||||
return Center(child: Text(_errorMessage));
|
||||
}
|
||||
if (data != null && data!.isNotEmpty) {
|
||||
return SuperListView.builder(
|
||||
extentPrecalculationPolicy: SuperPrecalculationPolicy(),
|
||||
itemCount: data!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final recommendation = data![index];
|
||||
return ListTile(
|
||||
onTap: () => context.push(
|
||||
'/globalSearch',
|
||||
extra: (
|
||||
recommendation.titleEnglish ??
|
||||
recommendation.titleRomaji ??
|
||||
recommendation.titleNative,
|
||||
widget.itemType,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
if (recommendation.imgURLs.isNotEmpty)
|
||||
_thumbnailPreview(
|
||||
context,
|
||||
recommendation.imgURLs.first,
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
recommendation.description != null
|
||||
? Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildTitle(
|
||||
recommendation.titleEnglish ??
|
||||
recommendation.titleRomaji ??
|
||||
recommendation.titleNative ??
|
||||
"",
|
||||
context,
|
||||
),
|
||||
Text(
|
||||
recommendation.description!,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
),
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Flexible(
|
||||
child: _buildTitle(
|
||||
recommendation.titleEnglish ??
|
||||
recommendation.titleRomaji ??
|
||||
recommendation.titleNative ??
|
||||
"",
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
child: recommendation.genres.isEmpty
|
||||
? const SizedBox(height: 15)
|
||||
: context.isTablet
|
||||
? Wrap(
|
||||
children: [
|
||||
for (
|
||||
var i = 0;
|
||||
i < recommendation.genres.length;
|
||||
i++
|
||||
)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 2,
|
||||
right: 2,
|
||||
bottom: 5,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 30,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors
|
||||
.grey
|
||||
.withValues(
|
||||
alpha: 0.2,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
5,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: null,
|
||||
child: Text(
|
||||
recommendation.genres[i],
|
||||
style: TextStyle(
|
||||
fontSize: 11.5,
|
||||
color: context.isLight
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
children: [
|
||||
for (
|
||||
var i = 0;
|
||||
i <
|
||||
recommendation
|
||||
.genres
|
||||
.length;
|
||||
i++
|
||||
)
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
left: 2,
|
||||
right: 2,
|
||||
bottom: 5,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 30,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors
|
||||
.grey
|
||||
.withValues(
|
||||
alpha: 0.2,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
5,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
recommendation
|
||||
.genres[i],
|
||||
style: TextStyle(
|
||||
fontSize: 11.5,
|
||||
color: context.isLight
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
Text(
|
||||
"${recommendation.score}% ${l10n.recommendations_similar}",
|
||||
style: TextStyle(
|
||||
background: Paint()
|
||||
..color = Theme.of(context)
|
||||
.scaffoldBackgroundColor
|
||||
.withValues(alpha: 0.75)
|
||||
..strokeWidth = 30.0
|
||||
..strokeJoin = StrokeJoin.round
|
||||
..style = PaintingStyle.stroke,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
return Center(child: Text(l10n.no_result));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTitle(String text, BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
// Make sure that (constraints.maxWidth - (35 + 5)) is strictly positive.
|
||||
final double availableWidth = constraints.maxWidth - (35 + 5);
|
||||
final textPainter =
|
||||
TextPainter(
|
||||
text: TextSpan(text: text, style: const TextStyle(fontSize: 13)),
|
||||
maxLines: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout(
|
||||
maxWidth: availableWidth > 0 ? availableWidth : 1.0,
|
||||
); // - Download icon size (download_page_widget.dart, Widget Build SizedBox width: 35)
|
||||
|
||||
final isOverflowing = textPainter.didExceedMaxLines;
|
||||
|
||||
if (isOverflowing) {
|
||||
return SizedBox(
|
||||
height: 20,
|
||||
child: Marquee(
|
||||
text: text,
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
|
||||
blankSpace: 40.0,
|
||||
velocity: 30.0,
|
||||
pauseAfterRound: const Duration(seconds: 1),
|
||||
startPadding: 10.0,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _thumbnailPreview(BuildContext context, String? imageUrl) {
|
||||
final imageProvider = CustomExtendedNetworkImageProvider(
|
||||
toImgUrl(imageUrl ?? ""),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_openImage(context, imageProvider);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 150,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
image: DecorationImage(image: imageProvider, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _openImage(BuildContext context, ImageProvider imageProvider) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Stack(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: PhotoViewGallery.builder(
|
||||
backgroundDecoration: const BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
itemCount: 1,
|
||||
builder: (context, index) {
|
||||
return PhotoViewGalleryPageOptions(
|
||||
imageProvider: imageProvider,
|
||||
minScale: PhotoViewComputedScale.contained,
|
||||
maxScale: 2.0,
|
||||
);
|
||||
},
|
||||
loadingBuilder: (context, event) {
|
||||
return const ProgressCenter();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SuperPrecalculationPolicy extends ExtentPrecalculationPolicy {
|
||||
@override
|
||||
bool shouldPrecalculateExtents(ExtentPrecalculationContext context) {
|
||||
return context.numberOfItems < 100;
|
||||
}
|
||||
}
|
||||
|
|
@ -642,7 +642,7 @@ class _MangaChapterPageGalleryState
|
|||
reverse: _isReverseHorizontal,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
canScrollPage: (_) {
|
||||
return _horizontalScaleValue == 1.0;
|
||||
return true;
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
if (index < _uChapDataPreload.length &&
|
||||
|
|
@ -695,9 +695,7 @@ class _MangaChapterPageGalleryState
|
|||
reverse: _isReverseHorizontal,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
canScrollPage: (gestureDetails) {
|
||||
return gestureDetails != null
|
||||
? !(gestureDetails.totalScale! > 1.0)
|
||||
: true;
|
||||
return true;
|
||||
},
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (_uChapDataPreload[index]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/widgets/downloaded_only_widget.dart';
|
||||
import 'package:mangayomi/modules/more/widgets/incognito_mode_widget.dart';
|
||||
import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
|
||||
class MoreScreen extends StatelessWidget {
|
||||
class MoreScreen extends ConsumerStatefulWidget {
|
||||
const MoreScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<MoreScreen> createState() => MoreScreenState();
|
||||
}
|
||||
|
||||
class MoreScreenState extends ConsumerState<MoreScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context);
|
||||
final hiddenItems = ref.watch(hideItemsStateProvider);
|
||||
return Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
|
|
@ -37,8 +46,17 @@ class MoreScreen extends StatelessWidget {
|
|||
// onChanged: (value) {},
|
||||
// ),
|
||||
// ),
|
||||
const DownloadedOnlyWidget(),
|
||||
const IncognitoModeWidget(),
|
||||
const Divider(),
|
||||
if (hiddenItems.contains("/history"))
|
||||
ListTileWidget(
|
||||
onTap: () {
|
||||
context.push('/history');
|
||||
},
|
||||
icon: Icons.history,
|
||||
title: l10n!.history,
|
||||
),
|
||||
ListTileWidget(
|
||||
onTap: () {
|
||||
context.push('/downloadQueue');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'algorithm_weights_state_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class AlgorithmWeightsState extends _$AlgorithmWeightsState {
|
||||
@override
|
||||
AlgorithmWeights build() {
|
||||
return isar.settings.getSync(227)!.algorithmWeights ?? AlgorithmWeights();
|
||||
}
|
||||
|
||||
void set(AlgorithmWeights value) {
|
||||
final settings = isar.settings.getSync(227)!;
|
||||
state = value;
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(
|
||||
settings
|
||||
..algorithmWeights = state
|
||||
..updatedAt = DateTime.now().millisecondsSinceEpoch,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void setWeights({int? genre, int? setting, int? synopsis, int? theme}) {
|
||||
set(
|
||||
AlgorithmWeights(
|
||||
genre: genre ?? state.genre,
|
||||
setting: setting ?? state.setting,
|
||||
synopsis: synopsis ?? state.synopsis,
|
||||
theme: theme ?? state.theme,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'algorithm_weights_state_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$algorithmWeightsStateHash() =>
|
||||
r'5c20cb9b195a73161b485e082ad024b138c3da9c';
|
||||
|
||||
/// See also [AlgorithmWeightsState].
|
||||
@ProviderFor(AlgorithmWeightsState)
|
||||
final algorithmWeightsStateProvider = AutoDisposeNotifierProvider<
|
||||
AlgorithmWeightsState, AlgorithmWeights>.internal(
|
||||
AlgorithmWeightsState.new,
|
||||
name: r'algorithmWeightsStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$algorithmWeightsStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AlgorithmWeightsState = AutoDisposeNotifier<AlgorithmWeights>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'downloaded_only_state_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class DownloadedOnlyState extends _$DownloadedOnlyState {
|
||||
@override
|
||||
bool build() {
|
||||
return isar.settings.getSync(227)!.downloadedOnlyMode ?? false;
|
||||
}
|
||||
|
||||
void setDownloadedOnly(bool value) {
|
||||
final settings = isar.settings.getSync(227)!;
|
||||
state = value;
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(
|
||||
settings
|
||||
..downloadedOnlyMode = state
|
||||
..updatedAt = DateTime.now().millisecondsSinceEpoch,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'downloaded_only_state_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$downloadedOnlyStateHash() =>
|
||||
r'09c451617c435ca59554546f5d3090d20c961bfe';
|
||||
|
||||
/// See also [DownloadedOnlyState].
|
||||
@ProviderFor(DownloadedOnlyState)
|
||||
final downloadedOnlyStateProvider =
|
||||
AutoDisposeNotifierProvider<DownloadedOnlyState, bool>.internal(
|
||||
DownloadedOnlyState.new,
|
||||
name: r'downloadedOnlyStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$downloadedOnlyStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$DownloadedOnlyState = AutoDisposeNotifier<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
@ -7,7 +7,7 @@ part 'incognito_mode_state_provider.g.dart';
|
|||
class IncognitoModeState extends _$IncognitoModeState {
|
||||
@override
|
||||
bool build() {
|
||||
return isar.settings.getSync(227)!.incognitoMode!;
|
||||
return isar.settings.getSync(227)!.incognitoMode ?? false;
|
||||
}
|
||||
|
||||
void setIncognitoMode(bool value) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'incognito_mode_state_provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$incognitoModeStateHash() =>
|
||||
r'149c4dcbc434fb6efc883e196392320bdc7c0821';
|
||||
r'3858256a820eef632d3df57533f2aad14f555b22';
|
||||
|
||||
/// See also [IncognitoModeState].
|
||||
@ProviderFor(IncognitoModeState)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ class _CustomNavigationSettingsState
|
|||
[
|
||||
"/more",
|
||||
"/browse",
|
||||
"/history",
|
||||
].any((element) => element == navigation)
|
||||
? null
|
||||
: (value) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,38 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/more/providers/algorithm_weights_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/general/providers/general_state_provider.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
class GeneralScreen extends ConsumerWidget {
|
||||
class GeneralScreen extends ConsumerStatefulWidget {
|
||||
const GeneralScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<GeneralScreen> createState() => _GeneralStateScreen();
|
||||
}
|
||||
|
||||
class _GeneralStateScreen extends ConsumerState<GeneralScreen> {
|
||||
int _genre = 0;
|
||||
int _setting = 0;
|
||||
int _synopsis = 0;
|
||||
int _theme = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final algorithmWeights = ref.read(algorithmWeightsStateProvider);
|
||||
_genre = algorithmWeights.genre!;
|
||||
_setting = algorithmWeights.setting!;
|
||||
_synopsis = algorithmWeights.synopsis!;
|
||||
_theme = algorithmWeights.theme!;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context);
|
||||
final enableDiscordRpc = ref.watch(enableDiscordRpcStateProvider);
|
||||
final hideDiscordRpcInIncognito = ref.watch(
|
||||
|
|
@ -24,6 +48,199 @@ class GeneralScreen extends ConsumerWidget {
|
|||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(20.0),
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 3.0, color: context.primaryColor),
|
||||
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.recommendations_weights,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
final defaultWeights = AlgorithmWeights();
|
||||
setState(() {
|
||||
_genre = defaultWeights.genre!;
|
||||
_setting = defaultWeights.setting!;
|
||||
_synopsis = defaultWeights.synopsis!;
|
||||
_theme = defaultWeights.theme!;
|
||||
});
|
||||
ref
|
||||
.read(algorithmWeightsStateProvider.notifier)
|
||||
.set(defaultWeights);
|
||||
},
|
||||
label: Text(context.l10n.reset),
|
||||
icon: const Icon(Icons.restore),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.recommendations_weights_genre),
|
||||
Text(
|
||||
(_genre / 100).toStringAsFixed(2),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
overlayShape: const RoundSliderOverlayShape(
|
||||
overlayRadius: 5.0,
|
||||
),
|
||||
),
|
||||
child: Slider.adaptive(
|
||||
min: 0,
|
||||
max: 100,
|
||||
value: _genre.toDouble(),
|
||||
onChanged: (value) {
|
||||
HapticFeedback.vibrate();
|
||||
setState(() {
|
||||
_genre = value.toInt();
|
||||
});
|
||||
},
|
||||
onChangeEnd: (value) => ref
|
||||
.read(algorithmWeightsStateProvider.notifier)
|
||||
.setWeights(genre: _genre),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.recommendations_weights_setting),
|
||||
Text(
|
||||
(_setting / 100).toStringAsFixed(2),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
overlayShape: const RoundSliderOverlayShape(
|
||||
overlayRadius: 5.0,
|
||||
),
|
||||
),
|
||||
child: Slider.adaptive(
|
||||
min: 0,
|
||||
max: 100,
|
||||
value: _setting.toDouble(),
|
||||
onChanged: (value) {
|
||||
HapticFeedback.vibrate();
|
||||
setState(() {
|
||||
_setting = value.toInt();
|
||||
});
|
||||
},
|
||||
onChangeEnd: (value) => ref
|
||||
.read(algorithmWeightsStateProvider.notifier)
|
||||
.setWeights(setting: _setting),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.recommendations_weights_synopsis),
|
||||
Text(
|
||||
(_synopsis / 100).toStringAsFixed(2),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
overlayShape: const RoundSliderOverlayShape(
|
||||
overlayRadius: 5.0,
|
||||
),
|
||||
),
|
||||
child: Slider.adaptive(
|
||||
min: 0,
|
||||
max: 100,
|
||||
value: _synopsis.toDouble(),
|
||||
onChanged: (value) {
|
||||
HapticFeedback.vibrate();
|
||||
setState(() {
|
||||
_synopsis = value.toInt();
|
||||
});
|
||||
},
|
||||
onChangeEnd: (value) => ref
|
||||
.read(algorithmWeightsStateProvider.notifier)
|
||||
.setWeights(synopsis: _synopsis),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.recommendations_weights_theme),
|
||||
Text(
|
||||
(_theme / 100).toStringAsFixed(2),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.secondaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
overlayShape: const RoundSliderOverlayShape(
|
||||
overlayRadius: 5.0,
|
||||
),
|
||||
),
|
||||
child: Slider.adaptive(
|
||||
min: 0,
|
||||
max: 100,
|
||||
value: _theme.toDouble(),
|
||||
onChanged: (value) {
|
||||
HapticFeedback.vibrate();
|
||||
setState(() {
|
||||
_theme = value.toInt();
|
||||
});
|
||||
},
|
||||
onChangeEnd: (value) => ref
|
||||
.read(algorithmWeightsStateProvider.notifier)
|
||||
.setWeights(theme: _theme),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SwitchListTile(
|
||||
value: enableDiscordRpc,
|
||||
title: Text(l10n.enable_discord_rpc),
|
||||
|
|
|
|||
|
|
@ -116,7 +116,9 @@ class SyncScreen extends ConsumerWidget {
|
|||
title: Text(l10n.sync_auto),
|
||||
subtitle: Text(
|
||||
autoSyncOptions.entries
|
||||
.where((o) => o.value == syncPreference.autoSyncFrequency)
|
||||
.where(
|
||||
(o) => o.value == syncPreference.autoSyncFrequency,
|
||||
)
|
||||
.first
|
||||
.key,
|
||||
style: TextStyle(
|
||||
|
|
@ -150,29 +152,35 @@ class SyncScreen extends ConsumerWidget {
|
|||
SwitchListTile(
|
||||
value: syncPreference.syncHistories,
|
||||
title: Text(context.l10n.sync_enable_histories),
|
||||
onChanged: syncPreference.syncOn ? (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncHistories(value);
|
||||
} : null,
|
||||
onChanged: syncPreference.syncOn
|
||||
? (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncHistories(value);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
SwitchListTile(
|
||||
value: syncPreference.syncUpdates,
|
||||
title: Text(context.l10n.sync_enable_updates),
|
||||
onChanged: syncPreference.syncOn ? (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncUpdates(value);
|
||||
} : null,
|
||||
onChanged: syncPreference.syncOn
|
||||
? (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncUpdates(value);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
SwitchListTile(
|
||||
value: syncPreference.syncSettings,
|
||||
title: Text(context.l10n.sync_enable_settings),
|
||||
onChanged: syncPreference.syncOn ? (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncSettings(value);
|
||||
} : null,
|
||||
onChanged: syncPreference.syncOn
|
||||
? (value) {
|
||||
ref
|
||||
.read(SynchingProvider(syncId: 1).notifier)
|
||||
.setSyncSettings(value);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
|
|
@ -288,6 +296,40 @@ class SyncScreen extends ConsumerWidget {
|
|||
Text(l10n.sync_button_sync),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: !syncPreference.syncOn || !isLogged
|
||||
? null
|
||||
: () => _showConfirmDialog(context, ref, true),
|
||||
icon: Icon(
|
||||
Icons.file_upload_outlined,
|
||||
color: !syncPreference.syncOn || !isLogged
|
||||
? context.secondaryColor
|
||||
: context.primaryColor,
|
||||
),
|
||||
),
|
||||
Text(l10n.sync_button_upload),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: !syncPreference.syncOn || !isLogged
|
||||
? null
|
||||
: () => _showConfirmDialog(context, ref, false),
|
||||
icon: Icon(
|
||||
Icons.file_download_outlined,
|
||||
color: !syncPreference.syncOn || !isLogged
|
||||
? context.secondaryColor
|
||||
: context.primaryColor,
|
||||
),
|
||||
),
|
||||
Text(l10n.sync_button_download),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
|
@ -298,172 +340,220 @@ class SyncScreen extends ConsumerWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showDialogLogin(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
SyncPreference syncPreference,
|
||||
) {
|
||||
final serverController = TextEditingController(text: syncPreference.server);
|
||||
final emailController = TextEditingController(text: syncPreference.email);
|
||||
final passwordController = TextEditingController();
|
||||
String server = "";
|
||||
String email = "";
|
||||
String password = "";
|
||||
String errorMessage = "";
|
||||
bool isLoading = false;
|
||||
bool obscureText = true;
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
Future<void> _showConfirmDialog(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
bool isUpload,
|
||||
) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.login_into("SyncServer"),
|
||||
style: const TextStyle(fontSize: 30),
|
||||
content: Text(
|
||||
isUpload
|
||||
? context.l10n.sync_button_upload_info
|
||||
: context.l10n.sync_button_download_info,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: SizedBox(
|
||||
height: 400,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: serverController,
|
||||
autofocus: true,
|
||||
onChanged: (value) => setState(() {
|
||||
server = value;
|
||||
}),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.sync_server,
|
||||
filled: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(width: 0.4),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(context.l10n.cancel),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: emailController,
|
||||
autofocus: true,
|
||||
onChanged: (value) => setState(() {
|
||||
email = value;
|
||||
}),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.email_adress,
|
||||
filled: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(width: 0.4),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: obscureText,
|
||||
onChanged: (value) => setState(() {
|
||||
password = value;
|
||||
}),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.sync_password,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () => setState(() {
|
||||
obscureText = !obscureText;
|
||||
}),
|
||||
icon: Icon(
|
||||
obscureText
|
||||
? Icons.visibility_outlined
|
||||
: Icons.visibility_off_outlined,
|
||||
),
|
||||
),
|
||||
filled: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(width: 0.4),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(errorMessage, style: const TextStyle(color: Colors.red)),
|
||||
const SizedBox(height: 30),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: SizedBox(
|
||||
width: context.width(1),
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: isLoading
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
final res = await ref
|
||||
.read(syncServerProvider(syncId: 1).notifier)
|
||||
.login(l10n, server, email, password);
|
||||
if (!res.$1) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
errorMessage = res.$2;
|
||||
});
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: isLoading
|
||||
? const CircularProgressIndicator()
|
||||
: Text(l10n.login),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(syncServerProvider(syncId: 1).notifier)
|
||||
.startSync(
|
||||
context.l10n,
|
||||
false,
|
||||
upload: isUpload,
|
||||
download: !isUpload,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(context.l10n.dialog_confirm),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
void _showDialogLogin(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
SyncPreference syncPreference,
|
||||
) {
|
||||
final serverController = TextEditingController(text: syncPreference.server);
|
||||
final emailController = TextEditingController(text: syncPreference.email);
|
||||
final passwordController = TextEditingController();
|
||||
String server = "";
|
||||
String email = "";
|
||||
String password = "";
|
||||
String errorMessage = "";
|
||||
bool isLoading = false;
|
||||
bool obscureText = true;
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.login_into("SyncServer"),
|
||||
style: const TextStyle(fontSize: 30),
|
||||
),
|
||||
content: SizedBox(
|
||||
height: 400,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: serverController,
|
||||
autofocus: true,
|
||||
onChanged: (value) => setState(() {
|
||||
server = value;
|
||||
}),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.sync_server,
|
||||
filled: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(width: 0.4),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: emailController,
|
||||
autofocus: true,
|
||||
onChanged: (value) => setState(() {
|
||||
email = value;
|
||||
}),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.email_adress,
|
||||
filled: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(width: 0.4),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: obscureText,
|
||||
onChanged: (value) => setState(() {
|
||||
password = value;
|
||||
}),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.sync_password,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () => setState(() {
|
||||
obscureText = !obscureText;
|
||||
}),
|
||||
icon: Icon(
|
||||
obscureText
|
||||
? Icons.visibility_outlined
|
||||
: Icons.visibility_off_outlined,
|
||||
),
|
||||
),
|
||||
filled: false,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(width: 0.4),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(errorMessage, style: const TextStyle(color: Colors.red)),
|
||||
const SizedBox(height: 30),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: SizedBox(
|
||||
width: context.width(1),
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: isLoading
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
final res = await ref
|
||||
.read(
|
||||
syncServerProvider(syncId: 1).notifier,
|
||||
)
|
||||
.login(l10n, server, email, password);
|
||||
if (!res.$1) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
errorMessage = res.$2;
|
||||
});
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: isLoading
|
||||
? const CircularProgressIndicator()
|
||||
: Text(l10n.login),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
lib/modules/more/widgets/downloaded_only_widget.dart
Normal file
29
lib/modules/more/widgets/downloaded_only_widget.dart
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/modules/more/providers/downloaded_only_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
|
||||
class DownloadedOnlyWidget extends ConsumerWidget {
|
||||
const DownloadedOnlyWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = l10nLocalizations(context);
|
||||
final downloadedOnly = ref.watch(downloadedOnlyStateProvider);
|
||||
return ListTileWidget(
|
||||
onTap: () => ref
|
||||
.read(downloadedOnlyStateProvider.notifier)
|
||||
.setDownloadedOnly(!downloadedOnly),
|
||||
icon: Icons.cloud_off_outlined,
|
||||
subtitle: l10n!.downloaded_only_description,
|
||||
title: l10n.downloaded_only,
|
||||
trailing: Switch(
|
||||
value: downloadedOnly,
|
||||
onChanged: (value) => ref
|
||||
.read(downloadedOnlyStateProvider.notifier)
|
||||
.setDownloadedOnly(value),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
66
lib/modules/widgets/bottom_select_bar.dart
Normal file
66
lib/modules/widgets/bottom_select_bar.dart
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
/// Bar, that appears at the bottom of the screen when long-pressing (selecting)
|
||||
/// a Manga/Anime/Novel or Chapter/Episode
|
||||
class BottomSelectBar extends StatelessWidget {
|
||||
final bool isVisible;
|
||||
final List<BottomSelectButton> actions;
|
||||
|
||||
const BottomSelectBar({
|
||||
super.key,
|
||||
required this.isVisible,
|
||||
required this.actions,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedContainer(
|
||||
curve: Curves.easeIn,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColor.withValues(alpha: 0.2),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
duration: const Duration(milliseconds: 100),
|
||||
height: isVisible ? 70 : 0,
|
||||
width: context.width(1),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: actions,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Button for the BottomSelectBar
|
||||
class BottomSelectButton extends StatelessWidget {
|
||||
final Widget icon;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
const BottomSelectButton({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import 'package:mangayomi/models/category.dart';
|
|||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/library/providers/library_state_provider.dart';
|
||||
import 'package:mangayomi/modules/library/widgets/list_tile_manga_category.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
|
@ -151,7 +152,7 @@ void showCategorySelectionDialog({
|
|||
if (isBulk) {
|
||||
ref.read(mangasListStateProvider.notifier).clear();
|
||||
ref
|
||||
.read(isLongPressedMangaStateProvider.notifier)
|
||||
.read(isLongPressedStateProvider.notifier)
|
||||
.update(false);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:bot_toast/bot_toast.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
|
|
@ -13,6 +14,7 @@ import 'package:mangayomi/modules/browse/extension/widgets/create_extension.dart
|
|||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
import 'package:mangayomi/modules/calendar/calendar_screen.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/migrate_screen.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/recommendation_screen.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/create_backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/data_and_storage.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/custom_navigation_settings.dart';
|
||||
|
|
@ -174,9 +176,9 @@ class RouterNotifier extends ChangeNotifier {
|
|||
name: "extension_detail",
|
||||
builder: (source) => ExtensionDetail(source: source),
|
||||
),
|
||||
_genericRoute<ItemType>(
|
||||
_genericRoute<(String?, ItemType)>(
|
||||
name: "globalSearch",
|
||||
builder: (itemType) => GlobalSearchScreen(itemType: itemType),
|
||||
builder: (data) => GlobalSearchScreen(search: data.$1, itemType: data.$2),
|
||||
),
|
||||
_genericRoute(name: "about", child: const AboutScreen()),
|
||||
_genericRoute(name: "track", child: const TrackScreen()),
|
||||
|
|
@ -246,6 +248,14 @@ class RouterNotifier extends ChangeNotifier {
|
|||
name: "migrate/tracker",
|
||||
builder: (data) => MigrationScreen(manga: data.$1, trackSearch: data.$2),
|
||||
),
|
||||
_genericRoute<(String, ItemType, AlgorithmWeights)>(
|
||||
name: "recommendations",
|
||||
builder: (data) => RecommendationScreen(
|
||||
name: data.$1,
|
||||
itemType: data.$2,
|
||||
algorithmWeights: data.$3,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
GoRoute _genericRoute<T>({
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'aniskip.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$aniSkipHash() => r'2e5d19b025a2207ff64da7bf7908450ea9e5ff8c';
|
||||
String _$aniSkipHash() => r'887869b54e2e151633efd46da83bde845e14f421';
|
||||
|
||||
/// See also [AniSkip].
|
||||
@ProviderFor(AniSkip)
|
||||
|
|
|
|||
146
lib/services/recommendation.dart
Normal file
146
lib/services/recommendation.dart
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import 'dart:convert';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
|
||||
Future<List<RecommendationResult>?> getRecommendations(
|
||||
String name,
|
||||
ItemType itemType,
|
||||
AlgorithmWeights algorithmWeights,
|
||||
) async {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
try {
|
||||
final mediaId = await _getSuggest(http, name, itemType);
|
||||
return _getRecommendation(
|
||||
http,
|
||||
mediaId ?? name,
|
||||
itemType,
|
||||
algorithmWeights,
|
||||
);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<RecommendationResult>?> _getRecommendation(
|
||||
InterceptedClient http,
|
||||
String mediaId,
|
||||
ItemType itemType,
|
||||
AlgorithmWeights algorithmWeights,
|
||||
) async {
|
||||
final url =
|
||||
"https://anibrain.ai/api/-/recommender/recs/${itemType != ItemType.anime ? "manga" : "anime"}";
|
||||
final res = await http.get(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
"priority": "u=1, i",
|
||||
"Referer": "https://anibrain.ai/",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
|
||||
},
|
||||
params: {
|
||||
"filterCountry": '[]',
|
||||
"filterFormat": '${_fillerType(itemType).map((e) => '"$e"').toList()}',
|
||||
"filterGenre": '{}',
|
||||
"filterTag": '{"max":{},"min":{}}',
|
||||
"filterRelease": '[1930,${DateTime.now().year}]',
|
||||
"filterScore": 0,
|
||||
"algorithmWeights": _algorithmWeights(algorithmWeights),
|
||||
"mediaId": mediaId,
|
||||
"mediaType": _mediaType(itemType),
|
||||
"adult": false,
|
||||
"page": 1,
|
||||
},
|
||||
);
|
||||
final data = json.decode(res.body) as Map<String, dynamic>;
|
||||
return (data["data"] as List?)
|
||||
?.map((e) => RecommendationResult.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<String?> _getSuggest(
|
||||
InterceptedClient http,
|
||||
String name,
|
||||
ItemType itemType,
|
||||
) async {
|
||||
final url =
|
||||
"https://anibrain.ai/api/-/recommender/autosuggest?searchValue=$name&mediaType=${_mediaType(itemType)}&adult=false";
|
||||
final res = await http.get(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
"priority": "u=1, i",
|
||||
"Referer": "https://anibrain.ai/recommender/manga",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
|
||||
},
|
||||
);
|
||||
final data = json.decode(res.body) as Map<String, dynamic>;
|
||||
final list = (data["data"] as List?)?.map((e) => e["id"]);
|
||||
return list?.firstOrNull;
|
||||
}
|
||||
|
||||
String _algorithmWeights(AlgorithmWeights algorithmWeights) {
|
||||
final genre = ((algorithmWeights.genre ?? 30) / 100).toStringAsFixed(2);
|
||||
final setting = ((algorithmWeights.setting ?? 15) / 100).toStringAsFixed(2);
|
||||
final synopsis = ((algorithmWeights.synopsis ?? 40) / 100).toStringAsFixed(2);
|
||||
final theme = ((algorithmWeights.theme ?? 20) / 100).toStringAsFixed(2);
|
||||
return '{"genre":$genre,"setting":$setting,"synopsis":$synopsis,"theme":$theme}';
|
||||
}
|
||||
|
||||
String _mediaType(ItemType itemType) {
|
||||
return switch (itemType) {
|
||||
ItemType.manga => "MANGA",
|
||||
ItemType.anime => "ANIME",
|
||||
ItemType.novel => "NOVEL",
|
||||
};
|
||||
}
|
||||
|
||||
List<String> _fillerType(ItemType itemType) {
|
||||
return switch (itemType) {
|
||||
ItemType.manga => ["MANGA"],
|
||||
ItemType.anime => ["movie", "ona", "tv"],
|
||||
ItemType.novel => ["NOVEL"],
|
||||
};
|
||||
}
|
||||
|
||||
class RecommendationResult {
|
||||
final String id;
|
||||
final int? anilistId;
|
||||
final int? myanimelistId;
|
||||
final int score;
|
||||
final String? titleRomaji;
|
||||
final String? titleEnglish;
|
||||
final String? titleNative;
|
||||
final String? description;
|
||||
final List<String> imgURLs;
|
||||
final List<String> genres;
|
||||
|
||||
RecommendationResult({
|
||||
required this.id,
|
||||
this.anilistId,
|
||||
this.myanimelistId,
|
||||
required this.score,
|
||||
this.titleRomaji,
|
||||
this.titleEnglish,
|
||||
this.titleNative,
|
||||
this.description,
|
||||
required this.imgURLs,
|
||||
required this.genres,
|
||||
});
|
||||
|
||||
factory RecommendationResult.fromJson(Map<String, dynamic> json) {
|
||||
return RecommendationResult(
|
||||
id: json["id"],
|
||||
anilistId: json["anilistId"],
|
||||
myanimelistId: json["myanimelistId"],
|
||||
score: json["score"],
|
||||
titleRomaji: json["titleRomaji"],
|
||||
titleEnglish: json["titleEnglish"],
|
||||
titleNative: json["titleNative"],
|
||||
description: json["description"],
|
||||
imgURLs: json["imgURLs"]?.cast<String>() ?? [],
|
||||
genres: json["genres"]?.cast<String>() ?? [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ class SyncServer extends _$SyncServer {
|
|||
String username,
|
||||
String password,
|
||||
) async {
|
||||
server = server[server.length - 1] == '/'
|
||||
server = server.isNotEmpty && server[server.length - 1] == '/'
|
||||
? server.substring(0, server.length - 1)
|
||||
: server;
|
||||
try {
|
||||
|
|
@ -67,7 +67,12 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> startSync(AppLocalizations l10n, bool silent) async {
|
||||
Future<void> startSync(
|
||||
AppLocalizations l10n,
|
||||
bool silent, {
|
||||
bool upload = false,
|
||||
bool download = false,
|
||||
}) async {
|
||||
if (!silent) {
|
||||
botToast(l10n.sync_starting, second: 500);
|
||||
}
|
||||
|
|
@ -75,27 +80,46 @@ class SyncServer extends _$SyncServer {
|
|||
final syncPreference = ref.read(synchingProvider(syncId: syncId));
|
||||
final syncNotifier = ref.read(synchingProvider(syncId: syncId).notifier);
|
||||
|
||||
final resultManga = await _syncManga(l10n, syncNotifier);
|
||||
final resultManga = await _syncManga(
|
||||
l10n,
|
||||
syncNotifier,
|
||||
download: download,
|
||||
upload: upload,
|
||||
);
|
||||
if (!resultManga) {
|
||||
botToast(l10n.sync_failed, second: 5);
|
||||
return;
|
||||
}
|
||||
if (syncPreference.syncHistories) {
|
||||
final resultHistory = await _syncHistory(l10n, syncNotifier);
|
||||
final resultHistory = await _syncHistory(
|
||||
l10n,
|
||||
syncNotifier,
|
||||
download: download,
|
||||
upload: upload,
|
||||
);
|
||||
if (!resultHistory) {
|
||||
botToast(l10n.sync_failed, second: 5);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (syncPreference.syncUpdates) {
|
||||
final resultUpdate = await _syncUpdate(l10n, syncNotifier);
|
||||
final resultUpdate = await _syncUpdate(
|
||||
l10n,
|
||||
syncNotifier,
|
||||
download: download,
|
||||
upload: upload,
|
||||
);
|
||||
if (!resultUpdate) {
|
||||
botToast(l10n.sync_failed, second: 5);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (syncPreference.syncSettings) {
|
||||
final resultSettings = await _syncSettings(l10n);
|
||||
final resultSettings = await _syncSettings(
|
||||
l10n,
|
||||
download: download,
|
||||
upload: upload,
|
||||
);
|
||||
if (!resultSettings) {
|
||||
botToast(l10n.sync_failed, second: 5);
|
||||
return;
|
||||
|
|
@ -111,8 +135,13 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> _syncManga(AppLocalizations l10n, Synching syncNotifier) async {
|
||||
final mangaData = _getMangaData();
|
||||
Future<bool> _syncManga(
|
||||
AppLocalizations l10n,
|
||||
Synching syncNotifier, {
|
||||
bool upload = false,
|
||||
bool download = false,
|
||||
}) async {
|
||||
final mangaData = _getMangaData(upload: upload, download: download);
|
||||
final accessToken = _getAccessToken();
|
||||
var response = await http.post(
|
||||
Uri.parse('${_getServer()}$_syncMangaUrl'),
|
||||
|
|
@ -127,11 +156,20 @@ class SyncServer extends _$SyncServer {
|
|||
return false;
|
||||
}
|
||||
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertCategories(jsonData, syncNotifier);
|
||||
await _upsertManga(jsonData, syncNotifier);
|
||||
await _upsertChapters(jsonData, syncNotifier);
|
||||
await _upsertTracks(jsonData, syncNotifier);
|
||||
if (!upload) {
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertCategories(jsonData, syncNotifier);
|
||||
await _upsertManga(jsonData, syncNotifier);
|
||||
await _upsertChapters(jsonData, syncNotifier);
|
||||
await _upsertTracks(jsonData, syncNotifier);
|
||||
} else {
|
||||
await syncNotifier.clearChangedParts([
|
||||
ActionType.removeCategory,
|
||||
ActionType.removeItem,
|
||||
ActionType.removeChapter,
|
||||
ActionType.removeTrack,
|
||||
], true);
|
||||
}
|
||||
|
||||
syncNotifier.setLastSyncManga(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
|
|
@ -140,9 +178,11 @@ class SyncServer extends _$SyncServer {
|
|||
|
||||
Future<bool> _syncHistory(
|
||||
AppLocalizations l10n,
|
||||
Synching syncNotifier,
|
||||
) async {
|
||||
final historyData = _getHistoryData();
|
||||
Synching syncNotifier, {
|
||||
bool upload = false,
|
||||
bool download = false,
|
||||
}) async {
|
||||
final historyData = _getHistoryData(upload: upload, download: download);
|
||||
final accessToken = _getAccessToken();
|
||||
var response = await http.post(
|
||||
Uri.parse('${_getServer()}$_syncHistoryUrl'),
|
||||
|
|
@ -157,16 +197,25 @@ class SyncServer extends _$SyncServer {
|
|||
return false;
|
||||
}
|
||||
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertHistories(jsonData, syncNotifier);
|
||||
if (!upload) {
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertHistories(jsonData, syncNotifier);
|
||||
} else {
|
||||
await syncNotifier.clearChangedParts([ActionType.removeHistory], true);
|
||||
}
|
||||
|
||||
syncNotifier.setLastSyncHistory(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> _syncUpdate(AppLocalizations l10n, Synching syncNotifier) async {
|
||||
final updateData = _getUpdateData();
|
||||
Future<bool> _syncUpdate(
|
||||
AppLocalizations l10n,
|
||||
Synching syncNotifier, {
|
||||
bool upload = false,
|
||||
bool download = false,
|
||||
}) async {
|
||||
final updateData = _getUpdateData(upload: upload, download: download);
|
||||
final accessToken = _getAccessToken();
|
||||
var response = await http.post(
|
||||
Uri.parse('${_getServer()}$_syncUpdateUrl'),
|
||||
|
|
@ -181,16 +230,24 @@ class SyncServer extends _$SyncServer {
|
|||
return false;
|
||||
}
|
||||
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertUpdates(jsonData, syncNotifier);
|
||||
if (!upload) {
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertUpdates(jsonData, syncNotifier);
|
||||
} else {
|
||||
await syncNotifier.clearChangedParts([ActionType.removeUpdate], true);
|
||||
}
|
||||
|
||||
syncNotifier.setLastSyncUpdate(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> _syncSettings(AppLocalizations l10n) async {
|
||||
final settingsData = _getSettingsData();
|
||||
Future<bool> _syncSettings(
|
||||
AppLocalizations l10n, {
|
||||
bool upload = false,
|
||||
bool download = false,
|
||||
}) async {
|
||||
final settingsData = _getSettingsData(download: download);
|
||||
final accessToken = _getAccessToken();
|
||||
var response = await http.post(
|
||||
Uri.parse('${_getServer()}$_syncSettingsUrl'),
|
||||
|
|
@ -205,8 +262,10 @@ class SyncServer extends _$SyncServer {
|
|||
return false;
|
||||
}
|
||||
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertSettings(jsonData);
|
||||
if (!upload) {
|
||||
final jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
await _upsertSettings(jsonData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -424,36 +483,61 @@ class SyncServer extends _$SyncServer {
|
|||
});
|
||||
}
|
||||
|
||||
String _getMangaData() {
|
||||
String _getMangaData({bool upload = false, bool download = false}) {
|
||||
Map<String, dynamic> data = {};
|
||||
data["categories"] = _getCategories();
|
||||
data["deleted_categories"] = _getDeletedObjects(ActionType.removeCategory);
|
||||
data["manga"] = _getManga();
|
||||
data["deleted_manga"] = _getDeletedObjects(ActionType.removeItem);
|
||||
data["chapters"] = _getChapters();
|
||||
data["deleted_chapters"] = _getDeletedObjects(ActionType.removeChapter);
|
||||
data["tracks"] = _getTracks();
|
||||
data["deleted_tracks"] = _getDeletedObjects(ActionType.removeTrack);
|
||||
data["categories"] = download ? [] : _getCategories();
|
||||
data["deleted_categories"] = download
|
||||
? []
|
||||
: _getDeletedObjects(ActionType.removeCategory);
|
||||
data["manga"] = download ? [] : _getManga();
|
||||
data["deleted_manga"] = download
|
||||
? []
|
||||
: _getDeletedObjects(ActionType.removeItem);
|
||||
data["chapters"] = download ? [] : _getChapters();
|
||||
data["deleted_chapters"] = download
|
||||
? []
|
||||
: _getDeletedObjects(ActionType.removeChapter);
|
||||
data["tracks"] = download ? [] : _getTracks();
|
||||
data["deleted_tracks"] = download
|
||||
? []
|
||||
: _getDeletedObjects(ActionType.removeTrack);
|
||||
if (upload) {
|
||||
data["resetAll"] = true;
|
||||
}
|
||||
return jsonEncode(data);
|
||||
}
|
||||
|
||||
String _getHistoryData() {
|
||||
String _getHistoryData({bool upload = false, bool download = false}) {
|
||||
Map<String, dynamic> data = {};
|
||||
data["histories"] = _getHistories();
|
||||
data["deleted_histories"] = _getDeletedObjects(ActionType.removeHistory);
|
||||
data["histories"] = download ? [] : _getHistories();
|
||||
data["deleted_histories"] = download
|
||||
? []
|
||||
: _getDeletedObjects(ActionType.removeHistory);
|
||||
if (upload) {
|
||||
data["resetAll"] = true;
|
||||
}
|
||||
return jsonEncode(data);
|
||||
}
|
||||
|
||||
String _getUpdateData() {
|
||||
String _getUpdateData({bool upload = false, bool download = false}) {
|
||||
Map<String, dynamic> data = {};
|
||||
data["updates"] = _getUpdates();
|
||||
data["deleted_updates"] = _getDeletedObjects(ActionType.removeUpdate);
|
||||
data["updates"] = download ? [] : _getUpdates();
|
||||
data["deleted_updates"] = download
|
||||
? []
|
||||
: _getDeletedObjects(ActionType.removeUpdate);
|
||||
if (upload) {
|
||||
data["resetAll"] = true;
|
||||
}
|
||||
return jsonEncode(data);
|
||||
}
|
||||
|
||||
String _getSettingsData() {
|
||||
String _getSettingsData({bool download = false}) {
|
||||
Map<String, dynamic> data = {};
|
||||
data["settings"] = isar.settings.getSync(227)!..updatedAt ??= DateTime.now().millisecondsSinceEpoch..cookiesList = [];
|
||||
if (!download) {
|
||||
data["settings"] = isar.settings.getSync(227)!
|
||||
..updatedAt ??= DateTime.now().millisecondsSinceEpoch
|
||||
..cookiesList = [];
|
||||
}
|
||||
return jsonEncode(data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'anilist.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$anilistHash() => r'fafb964252b3a5741e981cb8c2f0f2090b3b86ae';
|
||||
String _$anilistHash() => r'c786a526fdacc875e4a7e00886b2bda546eafeae';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ class Kitsu extends _$Kitsu {
|
|||
mediaId: jsonRes['id'],
|
||||
summary: jsonRes['synopsis'] ?? "",
|
||||
totalChapter: (jsonRes[totalChapter] ?? 0),
|
||||
coverUrl: jsonRes['posterImage']['original'] ?? "",
|
||||
coverUrl: jsonRes['posterImage']?['original'] ?? "",
|
||||
title: jsonRes['canonicalTitle'],
|
||||
startDate: "",
|
||||
publishingType: (jsonRes["subtype"] ?? ""),
|
||||
|
|
@ -192,22 +192,23 @@ class Kitsu extends _$Kitsu {
|
|||
final mediaId = jsonRes['id'] is String
|
||||
? int.parse(jsonRes['id'])
|
||||
: jsonRes['id'];
|
||||
final score = jsonRes['attributes']['averageRating'] is String
|
||||
? double.parse(jsonRes['attributes']['averageRating'])
|
||||
: jsonRes['attributes']['averageRating'];
|
||||
final attributes = jsonRes['attributes'];
|
||||
final score = attributes['averageRating'] is String
|
||||
? double.parse(attributes['averageRating'])
|
||||
: attributes['averageRating'];
|
||||
return TrackSearch(
|
||||
libraryId: mediaId,
|
||||
syncId: syncId,
|
||||
trackingUrl: _mediaUrl(isManga ? 'manga' : 'anime', mediaId),
|
||||
mediaId: mediaId,
|
||||
summary: jsonRes['attributes']['synopsis'] ?? "",
|
||||
totalChapter: (jsonRes['attributes'][totalChapter] ?? 0),
|
||||
coverUrl: jsonRes['attributes']['posterImage']['original'] ?? "",
|
||||
title: jsonRes['attributes']['canonicalTitle'],
|
||||
summary: attributes['synopsis'] ?? "",
|
||||
totalChapter: (attributes[totalChapter] ?? 0),
|
||||
coverUrl: attributes['posterImage']?['original'] ?? "",
|
||||
title: attributes['canonicalTitle'],
|
||||
startDate: "",
|
||||
score: score,
|
||||
publishingType: (jsonRes['attributes']['subtype'] ?? ""),
|
||||
publishingStatus: jsonRes['attributes']['endDate'] == null
|
||||
publishingType: (attributes['subtype'] ?? ""),
|
||||
publishingStatus: attributes['endDate'] == null
|
||||
? "Publishing"
|
||||
: "Finished",
|
||||
);
|
||||
|
|
@ -250,7 +251,7 @@ class Kitsu extends _$Kitsu {
|
|||
trackingUrl: _mediaUrl(type, id),
|
||||
summary: included['synopsis'] ?? "",
|
||||
totalChapter: included[totalChapter] ?? 0,
|
||||
coverUrl: included['posterImage']['original'] ?? "",
|
||||
coverUrl: included['posterImage']?['original'] ?? "",
|
||||
title: included['canonicalTitle'],
|
||||
startDate: "",
|
||||
publishingType: (included["subtype"] ?? ""),
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
mediaId: res["id"],
|
||||
summary: res["synopsis"] ?? "",
|
||||
totalChapter: res[contentUnit],
|
||||
coverUrl: res["main_picture"]["large"] ?? "",
|
||||
coverUrl: res["main_picture"]?["large"] ?? "",
|
||||
title: res["title"],
|
||||
startDate: res["start_date"] ?? "",
|
||||
publishingType: res["media_type"].toString().replaceAll("_", " "),
|
||||
|
|
@ -197,7 +197,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
mediaId: node["id"],
|
||||
summary: node["synopsis"] ?? "",
|
||||
totalChapter: node[contentUnit],
|
||||
coverUrl: node["main_picture"]["large"] ?? "",
|
||||
coverUrl: node["main_picture"]?["large"] ?? "",
|
||||
title: node["title"],
|
||||
score: (node["mean"] as num?)?.toDouble(),
|
||||
startDate: node["start_date"] ?? "",
|
||||
|
|
@ -237,7 +237,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
mediaId: node["id"],
|
||||
summary: node["synopsis"] ?? "",
|
||||
totalChapter: node[contentUnit],
|
||||
coverUrl: node["main_picture"]["large"] ?? "",
|
||||
coverUrl: node["main_picture"]?["large"] ?? "",
|
||||
title: node["title"],
|
||||
score: (node["mean"] as num?)?.toDouble(),
|
||||
startDate: node["start_date"] ?? "",
|
||||
|
|
|
|||
Loading…
Reference in a new issue