Merge branch 'main' into refactor/refactor-extensions-service

This commit is contained in:
Moustapha Kodjo Amadou 2024-12-20 09:15:26 +01:00 committed by GitHub
commit 9d712654a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 2430 additions and 957 deletions

View file

@ -17,6 +17,7 @@ import 'package:mangayomi/eval/model/m_provider.dart';
import 'package:mangayomi/eval/javascript/http.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/modules/browse/extension/providers/extension_preferences_providers.dart';
import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:mangayomi/utils/log/log.dart';
class $MProvider extends MProvider with $Bridge<MProvider> {
@ -478,15 +479,7 @@ class $MProvider extends MProvider with $Bridge<MProvider> {
return $Future.wrap(runtime.evaluateAsync(args[0]!.$reified).then((value) => $String(value.stringResult)));
}),
'getUrlWithoutDomain' => $Function((_, __, List<$Value?> args) {
final uri = Uri.parse(args[0]!.$value.replaceAll(' ', '%20'));
String out = uri.path;
if (uri.query.isNotEmpty) {
out += '?${uri.query}';
}
if (uri.fragment.isNotEmpty) {
out += '#${uri.fragment}';
}
return $String(out);
return $String((args[0]!.$value as String).getUrlWithoutDomain);
}),
'parseHtml' => $Function((_, __, List<$Value?> args) {
final res = MBridge.parsHtml(args[0]!.$reified);

View file

@ -333,5 +333,18 @@
"or": "أو",
"advanced": "متقدم",
"use_native_http_client": "استخدام عميل HTTP الأصلي",
"use_native_http_client_info": "يدعم تلقائيًا ميزات المنصة مثل الشبكات الافتراضية الخاصة (VPNs)، ويدعم ميزات HTTP أكثر مثل HTTP/3 ومعالجة إعادة التوجيه المخصصة"
"use_native_http_client_info": "يدعم تلقائيًا ميزات المنصة مثل الشبكات الافتراضية الخاصة (VPNs)، ويدعم ميزات HTTP أكثر مثل HTTP/3 ومعالجة إعادة التوجيه المخصصة",
"unwatched": "لم يشاهد",
"last_watched": "آخر مشاهدة",
"unwatched_count": "عدد غير المشاهد",
"latest_episode": "الحلقة الأخيرة",
"downloaded_episodes": "الحلقات المحملة",
"show_continue_watching_buttons": "إظهار أزرار المتابعة",
"by_episode_number": "حسب رقم الحلقة",
"episode_number": "رقم الحلقة",
"total_episodes": "إجمالي الحلقات",
"next_episode": "الحلقة التالية",
"next_5_episodes": "الحلقات الخمس التالية",
"next_10_episodes": "العشر حلقات التالية",
"next_25_episodes": "الخمسة وعشرون حلقة التالية"
}

View file

@ -10,14 +10,18 @@
"filter": "Filter",
"downloaded": "Heruntergeladen",
"unread": "Ungelesen",
"unwatched": "Nicht angeschaut",
"started": "Begonnen",
"bookmarked": "Markiert",
"sort": "Sortieren",
"alphabetically": "Alphabetisch",
"last_read": "Zuletzt gelesen",
"last_watched": "Zuletzt angeschaut",
"last_update_check": "Letzte Aktualisierungsprüfung",
"unread_count": "Anzahl Ungelesenes",
"unread_count": "Ungelesene Anzahl",
"unwatched_count": "Ungesehene Anzahl",
"latest_chapter": "Letztes Kapitel",
"latest_episode": "Letzte Episode",
"date_added": "Hinzugefügt am",
"display": "Anzeige",
"display_mode": "Anzeigemodus",
@ -27,13 +31,15 @@
"list": "Liste",
"badges": "Abzeichen",
"downloaded_chapters": "Heruntergeladene Kapitel",
"downloaded_episodes": "Heruntergeladene Episoden",
"language": "Sprache",
"local_source": "Lokale Quelle",
"tabs": "Tabs",
"show_category_tabs": "Kategorietabs anzeigen",
"show_numbers_of_items": "Artikelanzahl anzeigen",
"other": "Andere",
"show_continue_reading_buttons": "Weiterlesen-Schaltflächen anzeigen",
"show_continue_reading_buttons": "Weiterlesen-Buttons anzeigen",
"show_continue_watching_buttons": "Weiterschauen-Buttons anzeigen",
"empty_library": "Leere Bibliothek",
"search": "Suche...",
"no_recent_updates": "Keine kürzlichen Aktualisierungen",
@ -132,9 +138,11 @@
"reset": "Zurücksetzen",
"by_source": "Nach Quelle",
"by_chapter_number": "Nach Kapitelnummer",
"by_episode_number": "Nach Episodennummer",
"by_upload_date": "Nach Upload-Datum",
"source_title": "Quellentitel",
"chapter_number": "Kapitelnummer",
"episode_number": "Episodennummer",
"share": "Teilen",
"n_chapters": "{number} Kapitel",
"no_description": "Keine Beschreibung",
@ -164,6 +172,7 @@
"delete_chapters": "Kapitel löschen?",
"default0": "Standard",
"total_chapters": "Gesamtkapitel",
"total_episodes": "Gesamtepisoden",
"import_local_file": "Lokale Datei importieren",
"import_files": "Dateien",
"nothing_read_recently": "Kürzlich nichts gelesen",
@ -319,6 +328,10 @@
"next_5_chapters": "Nächsten 5 Kapitel",
"next_10_chapters": "Nächsten 10 Kapitel",
"next_25_chapters": "Nächsten 25 Kapitel",
"next_episode": "Nächste Episode",
"next_5_episodes": "Nächsten 5 Episoden",
"next_10_episodes": "Nächsten 10 Episoden",
"next_25_episodes": "Nächsten 25 Episoden",
"cover_saved": "Titelbild gespeichert",
"set_as_cover": "Als Titelbild festlegen",
"use_this_as_cover_art": "Dies als Titelbild verwenden?",

View file

@ -10,14 +10,18 @@
"filter": "Filter",
"downloaded": "Downloaded",
"unread": "Unread",
"unwatched": "Unwatched",
"started": "Started",
"bookmarked": "Bookmarked",
"sort": "Sort",
"alphabetically": "Alphabetically",
"last_read": "Last read",
"last_watched": "Last watched",
"last_update_check": "Last update check",
"unread_count": "Unread count",
"unwatched_count": "Unwatched count",
"latest_chapter": "Latest chapter",
"latest_episode": "Latest episode",
"date_added": "Date added",
"display": "Display",
"display_mode": "Display mode",
@ -27,6 +31,7 @@
"list": "List",
"badges": "Badges",
"downloaded_chapters": "Downloaded chapters",
"downloaded_episodes": "Downloaded episodes",
"language": "Language",
"local_source": "Local source",
"tabs": "Tabs",
@ -34,6 +39,7 @@
"show_numbers_of_items": "Show numbers of items",
"other": "Other",
"show_continue_reading_buttons": "Show continue reading buttons",
"show_continue_watching_buttons": "Show continue watching buttons",
"empty_library": "Empty library",
"search": "Search...",
"no_recent_updates": "No recent updates",
@ -132,9 +138,11 @@
"reset": "Reset",
"by_source": "By source",
"by_chapter_number": "By chapter number",
"by_episode_number": "By episode number",
"by_upload_date": "By upload date",
"source_title": "Source title",
"chapter_number": "Chapter number",
"episode_number": "Episode number",
"share": "Share",
"n_chapters": "{number} chapters",
"no_description": "No description",
@ -164,6 +172,7 @@
"delete_chapters": "Delete Chapter?",
"default0": "Default",
"total_chapters": "Total Chapters",
"total_episodes": "Total episodes",
"import_local_file": "Import Local file",
"import_files": "Files",
"nothing_read_recently": "Nothing read recently",
@ -319,6 +328,10 @@
"next_5_chapters": "Next 5 chapters",
"next_10_chapters": "Next 10 chapters",
"next_25_chapters": "Next 25 chapters",
"next_episode": "Next episode",
"next_5_episodes": "Next 5 episodes",
"next_10_episodes": "Next 10 episodes",
"next_25_episodes": "Next 25 episodes",
"cover_saved": "Cover saved",
"set_as_cover": "Set as cover",
"use_this_as_cover_art": "Use this as cover art?",

View file

@ -333,5 +333,18 @@
"or": "O",
"advanced": "Avanzado",
"use_native_http_client": "Utilizar cliente HTTP nativo",
"use_native_http_client_info": "admite automáticamente las características de la plataforma como VPNs, admite más características HTTP como HTTP/3 y manejo de redirección personalizada"
"use_native_http_client_info": "admite automáticamente las características de la plataforma como VPNs, admite más características HTTP como HTTP/3 y manejo de redirección personalizada",
"unwatched": "No visto",
"last_watched": "Visto por última vez",
"unwatched_count": "Número no visto",
"latest_episode": "Último episodio",
"downloaded_episodes": "Episodios descargados",
"show_continue_watching_buttons": "Mostrar botones de continuar viendo",
"by_episode_number": "Por número de episodio",
"episode_number": "Número de episodio",
"total_episodes": "Total de episodios",
"next_episode": "Siguiente episodio",
"next_5_episodes": "Siguientes 5 episodios",
"next_10_episodes": "Siguientes 10 episodios",
"next_25_episodes": "Siguientes 25 episodios"
}

View file

@ -333,5 +333,18 @@
"or": "O",
"advanced": "Avanzado",
"use_native_http_client": "Usar cliente HTTP nativo",
"use_native_http_client_info": "soporta automáticamente características de la plataforma como VPNs, soporta más características HTTP como HTTP/3 y manejo de redirección personalizado"
"use_native_http_client_info": "soporta automáticamente características de la plataforma como VPNs, soporta más características HTTP como HTTP/3 y manejo de redirección personalizado",
"unwatched": "No visto",
"last_watched": "Visto por última vez",
"unwatched_count": "Número no visto",
"latest_episode": "Último episodio",
"downloaded_episodes": "Episodios descargados",
"show_continue_watching_buttons": "Mostrar botones de continuar viendo",
"by_episode_number": "Por número de episodio",
"episode_number": "Número de episodio",
"total_episodes": "Total de episodios",
"next_episode": "Siguiente episodio",
"next_5_episodes": "Siguientes 5 episodios",
"next_10_episodes": "Siguientes 10 episodios",
"next_25_episodes": "Siguientes 25 episodios"
}

View file

@ -334,5 +334,18 @@
"or": "OU",
"advanced": "Avancé",
"use_native_http_client": "Utiliser le client HTTP natif",
"use_native_http_client_info": "Il supporte automatiquement les fonctionnalités de la plateforme telles que les VPN, et prend en charge plus de fonctionnalités HTTP telles que HTTP/3 et la gestion personnalisée des redirections."
"use_native_http_client_info": "Il supporte automatiquement les fonctionnalités de la plateforme telles que les VPN, et prend en charge plus de fonctionnalités HTTP telles que HTTP/3 et la gestion personnalisée des redirections.",
"unwatched": "Non regardé",
"last_watched": "Dernièrement regardé",
"unwatched_count": "Nombre non vu",
"latest_episode": "Dernier épisode",
"downloaded_episodes": "Épisodes téléchargés",
"show_continue_watching_buttons": "Afficher les boutons de reprise",
"by_episode_number": "Par numéro d'épisode",
"episode_number": "Numéro d'épisode",
"total_episodes": "Total des épisodes",
"next_episode": "Épisode suivant",
"next_5_episodes": "5 épisodes suivants",
"next_10_episodes": "10 épisodes suivants",
"next_25_episodes": "25 épisodes suivants"
}

View file

@ -333,5 +333,18 @@
"or": "ATAU",
"advanced": "Lanjutan",
"use_native_http_client": "Gunakan klien http asli",
"use_native_http_client_info": "secara otomatis mendukung fitur platform seperti VPN, mendukung lebih banyak fitur HTTP seperti HTTP/3 dan penanganan pengalihan khusus"
"use_native_http_client_info": "secara otomatis mendukung fitur platform seperti VPN, mendukung lebih banyak fitur HTTP seperti HTTP/3 dan penanganan pengalihan khusus",
"unwatched": "Belum ditonton",
"last_watched": "Terakhir ditonton",
"unwatched_count": "Jumlah belum ditonton",
"latest_episode": "Episode terbaru",
"downloaded_episodes": "Episode yang diunduh",
"show_continue_watching_buttons": "Tampilkan tombol lanjut menonton",
"by_episode_number": "Menurut nomor episode",
"episode_number": "Nomor episode",
"total_episodes": "Total episode",
"next_episode": "Episode berikutnya",
"next_5_episodes": "5 episode berikutnya",
"next_10_episodes": "10 episode berikutnya",
"next_25_episodes": "25 episode berikutnya"
}

View file

@ -333,5 +333,18 @@
"or": "O",
"advanced": "Avanzate",
"use_native_http_client": "Usa il client HTTP nativo",
"use_native_http_client_info": "Supporta automaticamente le funzionalità della piattaforma come le VPN, supporta più funzionalità HTTP come HTTP/3 e la gestione dei reindirizzamenti personalizzati."
"use_native_http_client_info": "Supporta automaticamente le funzionalità della piattaforma come le VPN, supporta più funzionalità HTTP come HTTP/3 e la gestione dei reindirizzamenti personalizzati.",
"unwatched": "Non guardato",
"last_watched": "Ultima visione",
"unwatched_count": "Conteggio non guardati",
"latest_episode": "Ultimo episodio",
"downloaded_episodes": "Episodi scaricati",
"show_continue_watching_buttons": "Mostra pulsanti continua a guardare",
"by_episode_number": "Per numero di episodio",
"episode_number": "Numero dell'episodio",
"total_episodes": "Episodi totali",
"next_episode": "Prossimo episodio",
"next_5_episodes": "Prossimi 5 episodi",
"next_10_episodes": "Prossimi 10 episodi",
"next_25_episodes": "Prossimi 25 episodi"
}

View file

@ -333,5 +333,18 @@
"or": "OU",
"advanced": "Avançado",
"use_native_http_client": "Usar cliente HTTP nativo",
"use_native_http_client_info": "ele suporta automaticamente recursos da plataforma, como VPNs, suporta mais recursos HTTP, como HTTP/3 e manuseio de redirecionamentos personalizados"
"use_native_http_client_info": "ele suporta automaticamente recursos da plataforma, como VPNs, suporta mais recursos HTTP, como HTTP/3 e manuseio de redirecionamentos personalizados",
"unwatched": "Não assistido",
"last_watched": "Último assistido",
"unwatched_count": "Contagem não assistidos",
"latest_episode": "Último episódio",
"downloaded_episodes": "Episódios baixados",
"show_continue_watching_buttons": "Mostrar botões para continuar assistindo",
"by_episode_number": "Por número do episódio",
"episode_number": "Número do episódio",
"total_episodes": "Total de episódios",
"next_episode": "Próximo episódio",
"next_5_episodes": "Próximos 5 episódios",
"next_10_episodes": "Próximos 10 episódios",
"next_25_episodes": "Próximos 25 episódios"
}

View file

@ -333,5 +333,18 @@
"or": "OU",
"advanced": "Avançado",
"use_native_http_client": "Usar cliente HTTP nativo",
"use_native_http_client_info": "ele suporta automaticamente recursos da plataforma como VPNs, suporta mais recursos HTTP, como HTTP/3 e manuseio personalizado de redirecionamento"
"use_native_http_client_info": "ele suporta automaticamente recursos da plataforma como VPNs, suporta mais recursos HTTP, como HTTP/3 e manuseio personalizado de redirecionamento",
"unwatched": "Não assistido",
"last_watched": "Último assistido",
"unwatched_count": "Contagem não assistidos",
"latest_episode": "Último episódio",
"downloaded_episodes": "Episódios baixados",
"show_continue_watching_buttons": "Mostrar botões para continuar assistindo",
"by_episode_number": "Por número do episódio",
"episode_number": "Número do episódio",
"total_episodes": "Total de episódios",
"next_episode": "Próximo episódio",
"next_5_episodes": "Próximos 5 episódios",
"next_10_episodes": "Próximos 10 episódios",
"next_25_episodes": "Próximos 25 episódios"
}

View file

@ -333,5 +333,18 @@
"or": "ИЛИ",
"advanced": "Продвинутые",
"use_native_http_client": "Использовать нативный HTTP-клиент",
"use_native_http_client_info": "он автоматически поддерживает функции платформы, такие как VPN, поддерживает больше функций HTTP, таких как HTTP/3 и пользовательская обработка перенаправлений"
"use_native_http_client_info": "он автоматически поддерживает функции платформы, такие как VPN, поддерживает больше функций HTTP, таких как HTTP/3 и пользовательская обработка перенаправлений",
"unwatched": "Непросмотренный",
"last_watched": "Последний просмотр",
"unwatched_count": "Количество непросмотренных",
"latest_episode": "Последний эпизод",
"downloaded_episodes": "Загруженные эпизоды",
"show_continue_watching_buttons": "Показать кнопки продолжения просмотра",
"by_episode_number": "По номеру эпизода",
"episode_number": "Номер эпизода",
"total_episodes": "Общее количество эпизодов",
"next_episode": "Следующий эпизод",
"next_5_episodes": "Следующие 5 эпизодов",
"next_10_episodes": "Следующие 10 эпизодов",
"next_25_episodes": "Следующие 25 эпизодов"
}

View file

@ -335,5 +335,18 @@
"or": "หรือ",
"advanced": "ขั้นสูง",
"use_native_http_client": "ใช้ไคลเอนต์ HTTP พื้นเมือง",
"use_native_http_client_info": "รองรับฟีเจอร์ของแพลตฟอร์มโดยอัตโนมัติ เช่น VPNs รองรับฟีเจอร์ HTTP มากขึ้น เช่น HTTP/3 และการจัดการการเปลี่ยนเส้นทางที่กำหนดเอง"
"use_native_http_client_info": "รองรับฟีเจอร์ของแพลตฟอร์มโดยอัตโนมัติ เช่น VPNs รองรับฟีเจอร์ HTTP มากขึ้น เช่น HTTP/3 และการจัดการการเปลี่ยนเส้นทางที่กำหนดเอง",
"unwatched": "ยังไม่ได้ดู",
"last_watched": "ดูครั้งสุดท้าย",
"unwatched_count": "จำนวนที่ยังไม่ได้ดู",
"latest_episode": "ตอนล่าสุด",
"downloaded_episodes": "ตอนที่ดาวน์โหลด",
"show_continue_watching_buttons": "แสดงปุ่มดูต่อไป",
"by_episode_number": "ตามหมายเลขตอน",
"episode_number": "หมายเลขตอน",
"total_episodes": "รวมตอน",
"next_episode": "ตอนถัดไป",
"next_5_episodes": "5 ตอนถัดไป",
"next_10_episodes": "10 ตอนถัดไป",
"next_25_episodes": "25 ตอนถัดไป"
}

View file

@ -333,5 +333,18 @@
"or": "VEYA",
"advanced": "Gelişmiş",
"use_native_http_client": "Yerel http istemcisini kullan",
"use_native_http_client_info": "otomatik olarak VPN'ler gibi platform özelliklerini destekler, HTTP/3 gibi daha fazla HTTP özelliğini ve özel yönlendirme işlemlerini destekler"
"use_native_http_client_info": "otomatik olarak VPN'ler gibi platform özelliklerini destekler, HTTP/3 gibi daha fazla HTTP özelliğini ve özel yönlendirme işlemlerini destekler",
"unwatched": "İzlenmemiş",
"last_watched": "Son İzlenen",
"unwatched_count": "İzlenmemiş Sayısı",
"latest_episode": "Son Bölüm",
"downloaded_episodes": "İndirilen Bölümler",
"show_continue_watching_buttons": "İzlemeye Devam Et Düğmelerini Göster",
"by_episode_number": "Bölüm Sayısına Göre",
"episode_number": "Bölüm Sayısı",
"total_episodes": "Toplam Bölümler",
"next_episode": "Sonraki Bölüm",
"next_5_episodes": "Sonraki 5 Bölüm",
"next_10_episodes": "Sonraki 10 Bölüm",
"next_25_episodes": "Sonraki 25 Bölüm"
}

View file

@ -335,5 +335,18 @@
"or": "或",
"advanced": "高级",
"use_native_http_client": "使用本地 HTTP 客户端",
"use_native_http_client_info": "它自动支持平台特性,如 VPN支持更多 HTTP 特性,如 HTTP/3 和自定义重定向处理"
"use_native_http_client_info": "它自动支持平台特性,如 VPN支持更多 HTTP 特性,如 HTTP/3 和自定义重定向处理",
"unwatched": "未观看",
"last_watched": "上次观看",
"unwatched_count": "未观看计数",
"latest_episode": "最新一集",
"downloaded_episodes": "下载的剧集",
"show_continue_watching_buttons": "显示继续观看按钮",
"by_episode_number": "按集数",
"episode_number": "集数",
"total_episodes": "总集数",
"next_episode": "下一集",
"next_5_episodes": "接下来的 5 集",
"next_10_episodes": "接下来的 10 集",
"next_25_episodes": "接下来的 25 集"
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -227,6 +227,7 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
},
sourceExist: widget.sourceExist,
checkForUpdate: widget.checkForUpdate,
isManga: widget.manga.isManga!,
),
);
}

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/eval/dart/model/m_bridge.dart';
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
import 'package:mangayomi/modules/more/backup_and_restore/providers/auto_backup.dart';
import 'package:mangayomi/modules/more/backup_and_restore/providers/backup.dart';
@ -17,7 +17,8 @@ class BackupAndRestore extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final backupFrequency = ref.watch(backupFrequencyStateProvider);
final backupFrequencyOptions = ref.watch(backupFrequencyOptionsStateProvider);
final backupFrequencyOptions =
ref.watch(backupFrequencyOptionsStateProvider);
final autoBackupLocation = ref.watch(autoBackupLocationStateProvider);
ref.read(autoBackupLocationStateProvider.notifier).refresh();
@ -73,24 +74,32 @@ class BackupAndRestore extends ConsumerWidget {
},
child: Text(
l10n.cancel,
style: TextStyle(color: context.primaryColor),
style: TextStyle(
color: context.primaryColor),
)),
TextButton(
onPressed: () async {
String? result;
if (Platform.isIOS) {
result = (await StorageProvider().getIosBackupDirectory())!.path;
result = (await StorageProvider()
.getIosBackupDirectory())!
.path;
} else {
result = await FilePicker.platform.getDirectoryPath();
result = await FilePicker.platform
.getDirectoryPath();
}
if (result != null && context.mounted) {
ref.watch(doBackUpProvider(list: indexList, path: result, context: context));
ref.watch(doBackUpProvider(
list: indexList,
path: result,
context: context));
}
},
child: Text(
l10n.ok,
style: TextStyle(color: context.primaryColor),
style: TextStyle(
color: context.primaryColor),
)),
],
)
@ -120,12 +129,15 @@ class BackupAndRestore extends ConsumerWidget {
children: [
Row(
children: [
Icon(Icons.info_outline_rounded, color: context.secondaryColor),
Icon(Icons.info_outline_rounded,
color: context.secondaryColor),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Text(l10n.restore_backup_warning_title),
padding:
const EdgeInsets.symmetric(vertical: 5),
child:
Text(l10n.restore_backup_warning_title),
),
],
)),
@ -139,16 +151,22 @@ class BackupAndRestore extends ConsumerWidget {
},
child: Text(
l10n.cancel,
style: TextStyle(color: context.primaryColor),
style:
TextStyle(color: context.primaryColor),
)),
TextButton(
onPressed: () async {
try {
FilePickerResult? result =
await FilePicker.platform.pickFiles(allowMultiple: false);
await FilePicker.platform
.pickFiles(allowMultiple: false,
type: FileType.custom,
allowedExtensions: ["backup"]);
if (result != null && context.mounted) {
ref.watch(doRestoreProvider(path: result.files.first.path!, context: context));
ref.watch(doRestoreProvider(
path: result.files.first.path!,
context: context));
}
if (!context.mounted) return;
Navigator.pop(context);
@ -159,7 +177,8 @@ class BackupAndRestore extends ConsumerWidget {
},
child: Text(
l10n.ok,
style: TextStyle(color: context.primaryColor),
style:
TextStyle(color: context.primaryColor),
)),
],
)
@ -177,7 +196,9 @@ class BackupAndRestore extends ConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
child: Row(
children: [
Text(l10n.automatic_backups, style: TextStyle(fontSize: 13, color: context.primaryColor)),
Text(l10n.automatic_backups,
style:
TextStyle(fontSize: 13, color: context.primaryColor)),
],
),
),
@ -201,7 +222,10 @@ class BackupAndRestore extends ConsumerWidget {
value: index,
groupValue: backupFrequency,
onChanged: (value) {
ref.read(backupFrequencyStateProvider.notifier).set(value!);
ref
.read(backupFrequencyStateProvider
.notifier)
.set(value!);
Navigator.pop(context);
},
title: Row(
@ -220,7 +244,8 @@ class BackupAndRestore extends ConsumerWidget {
},
child: Text(
l10n.cancel,
style: TextStyle(color: context.primaryColor),
style:
TextStyle(color: context.primaryColor),
)),
],
)
@ -240,12 +265,16 @@ class BackupAndRestore extends ConsumerWidget {
String? result = await FilePicker.platform.getDirectoryPath();
if (result != null) {
ref.read(autoBackupLocationStateProvider.notifier).set(result);
ref
.read(autoBackupLocationStateProvider.notifier)
.set(result);
}
},
title: Text(l10n.backup_location),
subtitle: Text(
autoBackupLocation.$2.isEmpty ? autoBackupLocation.$1 : autoBackupLocation.$2,
autoBackupLocation.$2.isEmpty
? autoBackupLocation.$1
: autoBackupLocation.$2,
style: TextStyle(fontSize: 11, color: context.secondaryColor),
),
),
@ -295,16 +324,22 @@ class BackupAndRestore extends ConsumerWidget {
},
child: Text(
l10n.cancel,
style: TextStyle(color: context.primaryColor),
style: TextStyle(
color: context.primaryColor),
)),
TextButton(
onPressed: () async {
ref.read(backupFrequencyOptionsStateProvider.notifier).set(indexList);
ref
.read(
backupFrequencyOptionsStateProvider
.notifier)
.set(indexList);
Navigator.pop(context);
},
child: Text(
l10n.ok,
style: TextStyle(color: context.primaryColor),
style: TextStyle(
color: context.primaryColor),
)),
],
)
@ -325,12 +360,14 @@ class BackupAndRestore extends ConsumerWidget {
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Icon(Icons.info_outline_rounded, color: context.secondaryColor),
Icon(Icons.info_outline_rounded,
color: context.secondaryColor),
],
),
),
subtitle: Text(l10n.backup_and_restore_warning_info,
style: TextStyle(fontSize: 11, color: context.secondaryColor)),
style:
TextStyle(fontSize: 11, color: context.secondaryColor)),
)
],
),
@ -354,5 +391,12 @@ List<String> _getList(BuildContext context) {
List<String> _getBackupFrequencyList(BuildContext context) {
final l10n = l10nLocalizations(context)!;
return [l10n.off, l10n.every_6_hours, l10n.every_12_hours, l10n.daily, l10n.every_2_days, l10n.weekly];
return [
l10n.off,
l10n.every_6_hours,
l10n.every_12_hours,
l10n.daily,
l10n.every_2_days,
l10n.weekly
];
}

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -22,15 +23,16 @@ class MangaWebView extends ConsumerStatefulWidget {
}
class _MangaWebViewState extends ConsumerState<MangaWebView> {
late final MyInAppBrowser browser;
double _progress = 0;
bool isNotDesktop = false;
bool isNotWebviewWindow = false;
@override
void initState() {
if (Platform.isLinux) {
if (Platform.isLinux || Platform.isWindows) {
_runWebViewDesktop();
} else {
setState(() {
isNotDesktop = true;
isNotWebviewWindow = true;
});
}
super.initState();
@ -38,23 +40,59 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
Webview? _desktopWebview;
_runWebViewDesktop() async {
_desktopWebview = await WebviewWindow.create();
if (Platform.isLinux) {
_desktopWebview = await WebviewWindow.create();
final timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
try {
final cookieList = await _desktopWebview!.getAllCookies();
final ua = await _desktopWebview!.evaluateJavaScript("navigator.userAgent") ?? "";
final cookie = cookieList.map((e) => "${e.name}=${e.value}").join(";");
await MClient.setCookie(_url, ua, null, cookie: cookie);
} catch (_) {}
});
_desktopWebview!
..setBrightness(Brightness.dark)
..launch(widget.url)
..onClose.whenComplete(() {
timer.cancel();
Navigator.pop(context);
final timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
try {
final cookieList = await _desktopWebview!.getAllCookies();
final ua = await _desktopWebview!
.evaluateJavaScript("navigator.userAgent") ??
"";
final cookie =
cookieList.map((e) => "${e.name}=${e.value}").join(";");
await MClient.setCookie(_url, ua, null, cookie: cookie);
} catch (_) {}
});
_desktopWebview!
..setBrightness(Brightness.dark)
..launch(widget.url)
..onClose.whenComplete(() {
timer.cancel();
Navigator.pop(context);
});
} else {
browser = MyInAppBrowser(
context: context,
controller: (controller) {
_webViewController = controller;
},
onProgress: (progress) async {
final canGoback = await _webViewController?.canGoBack();
final canGoForward = await _webViewController?.canGoForward();
final title = await _webViewController?.getTitle();
final url = await _webViewController?.getUrl();
if (mounted) {
setState(() {
_progress = progress / 100;
_url = url.toString();
_title = title!;
_canGoback = canGoback ?? false;
_canGoForward = canGoForward ?? false;
});
}
},
);
await browser.openUrlRequest(
urlRequest: URLRequest(url: WebUri(widget.url)),
settings: InAppBrowserClassSettings(
browserSettings: InAppBrowserSettings(
presentationStyle: ModalPresentationStyle.POPOVER),
webViewSettings: InAppWebViewSettings(
isInspectable: kDebugMode, useShouldOverrideUrlLoading: true),
),
);
}
}
InAppWebViewController? _webViewController;
@ -65,16 +103,19 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
@override
Widget build(BuildContext context) {
final l10n = l10nLocalizations(context);
return !isNotDesktop
return (!isNotWebviewWindow && Platform.isLinux)
? Scaffold(
appBar: AppBar(
title: Text(
_title,
style: const TextStyle(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold),
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold),
),
leading: IconButton(
onPressed: () {
_desktopWebview!.close();
Navigator.pop(context);
},
icon: const Icon(Icons.close)),
@ -103,21 +144,32 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
dense: true,
subtitle: Text(
_url,
style: const TextStyle(fontSize: 10, overflow: TextOverflow.ellipsis),
style: const TextStyle(
fontSize: 10,
overflow: TextOverflow.ellipsis),
),
title: Text(
_title,
style: const TextStyle(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold),
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold),
),
leading: IconButton(
onPressed: () {
if (Platform.isWindows) {
if (browser.isOpened()) {
browser.close();
browser.dispose();
}
}
Navigator.pop(context);
},
icon: const Icon(Icons.close)),
),
),
IconButton(
icon: Icon(Icons.arrow_back, color: _canGoback ? null : Colors.grey),
icon: Icon(Icons.arrow_back,
color: _canGoback ? null : Colors.grey),
onPressed: _canGoback
? () {
_webViewController?.goBack();
@ -125,7 +177,8 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
: null,
),
IconButton(
icon: Icon(Icons.arrow_forward, color: _canGoForward ? null : Colors.grey),
icon: Icon(Icons.arrow_forward,
color: _canGoForward ? null : Colors.grey),
onPressed: _canGoForward
? () {
_webViewController?.goForward();
@ -136,10 +189,15 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
popUpAnimationStyle: popupAnimationStyle,
itemBuilder: (context) {
return [
PopupMenuItem<int>(value: 0, child: Text(l10n!.refresh)),
PopupMenuItem<int>(value: 1, child: Text(l10n.share)),
PopupMenuItem<int>(value: 2, child: Text(l10n.open_in_browser)),
PopupMenuItem<int>(value: 3, child: Text(l10n.clear_cookie)),
PopupMenuItem<int>(
value: 0, child: Text(l10n!.refresh)),
PopupMenuItem<int>(
value: 1, child: Text(l10n.share)),
PopupMenuItem<int>(
value: 2,
child: Text(l10n.open_in_browser)),
PopupMenuItem<int>(
value: 3, child: Text(l10n.clear_cookie)),
];
},
onSelected: (value) async {
@ -148,7 +206,8 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
} else if (value == 1) {
Share.share(_url);
} else if (value == 2) {
await InAppBrowser.openWithSystemBrowser(url: WebUri(_url));
await InAppBrowser.openWithSystemBrowser(
url: WebUri(_url));
} else if (value == 3) {
CookieManager.instance().deleteAllCookies();
MClient.deleteAllCookies(_url);
@ -157,67 +216,78 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
],
),
),
_progress < 1.0 ? LinearProgressIndicator(value: _progress) : Container(),
Expanded(
child: InAppWebView(
webViewEnvironment: webViewEnvironment,
onWebViewCreated: (controller) async {
_webViewController = controller;
},
onLoadStart: (controller, url) async {
setState(() {
_url = url.toString();
});
},
shouldOverrideUrlLoading: (controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (!["http", "https", "file", "chrome", "data", "javascript", "about"]
.contains(uri.scheme)) {
if (await canLaunchUrl(uri)) {
// Launch the App
await launchUrl(
uri,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
_progress < 1.0
? LinearProgressIndicator(value: _progress)
: Container(),
if (!Platform.isWindows)
Expanded(
child: InAppWebView(
webViewEnvironment: webViewEnvironment,
onWebViewCreated: (controller) async {
_webViewController = controller;
},
onLoadStart: (controller, url) async {
setState(() {
_url = url.toString();
});
},
shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url!;
if (![
"http",
"https",
"file",
"chrome",
"data",
"javascript",
"about"
].contains(uri.scheme)) {
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
return NavigationActionPolicy.CANCEL;
}
}
}
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, url) async {
if (mounted) {
setState(() {
_url = url.toString();
});
}
},
onProgressChanged: (controller, progress) async {
if (mounted) {
setState(() {
_progress = progress / 100;
});
}
},
onUpdateVisitedHistory: (controller, url, isReload) async {
final ua = await controller.evaluateJavascript(source: "navigator.userAgent") ?? "";
await MClient.setCookie(url.toString(), ua, controller);
final canGoback = await controller.canGoBack();
final canGoForward = await controller.canGoForward();
final title = await controller.getTitle();
if (mounted) {
setState(() {
_url = url.toString();
_title = title!;
_canGoback = canGoback;
_canGoForward = canGoForward;
});
}
},
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, url) async {
if (mounted) {
setState(() {
_url = url.toString();
});
}
},
onProgressChanged: (controller, progress) async {
if (mounted) {
setState(() {
_progress = progress / 100;
});
}
},
onUpdateVisitedHistory:
(controller, url, isReload) async {
final ua = await controller.evaluateJavascript(
source: "navigator.userAgent") ??
"";
await MClient.setCookie(
url.toString(), ua, controller);
final canGoback = await controller.canGoBack();
final canGoForward =
await controller.canGoForward();
final title = await controller.getTitle();
if (mounted) {
setState(() {
_url = url.toString();
_title = title!;
_canGoback = canGoback;
_canGoForward = canGoForward;
});
}
},
initialUrlRequest:
URLRequest(url: WebUri(widget.url)),
),
),
),
],
),
),
@ -225,3 +295,53 @@ class _MangaWebViewState extends ConsumerState<MangaWebView> {
);
}
}
class MyInAppBrowser extends InAppBrowser {
BuildContext context;
void Function(InAppWebViewController) controller;
void Function(int) onProgress;
MyInAppBrowser(
{required this.context,
required this.controller,
required this.onProgress})
: super(webViewEnvironment: webViewEnvironment);
@override
Future onBrowserCreated() async {
controller.call(webViewController!);
}
@override
void onProgressChanged(progress) {
onProgress.call(progress);
}
@override
void onExit() {
Navigator.pop(context);
}
@override
void onLoadStop(url) async {
if (webViewController != null) {
final ua = await webViewController!
.evaluateJavascript(source: "navigator.userAgent") ??
"";
await MClient.setCookie(url.toString(), ua, webViewController);
}
}
@override
Future<NavigationActionPolicy> shouldOverrideUrlLoading(
navigationAction) async {
var uri = navigationAction.request.url!;
if (!["http", "https", "file", "chrome", "data", "javascript", "about"]
.contains(uri.scheme)) {
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
}
}

View file

@ -40,6 +40,18 @@ extension StringExtensions on String {
return replaceAll(RegExp(r'[\\/:*?"<>|\0]|(^CON$|^PRN$|^AUX$|^NUL$|^COM[1-9]$|^LPT[1-9]$)'), source);
}
String get getUrlWithoutDomain {
final uri = Uri.parse(replaceAll(' ', '%20'));
String out = uri.path;
if (uri.query.isNotEmpty) {
out += '?${uri.query}';
}
if (uri.fragment.isNotEmpty) {
out += '#${uri.fragment}';
}
return out;
}
bool isMediaVideo() {
return ["3gp", "avi", "mpg", "mpeg", "webm", "ogg", "flv", "m4v", "mvp", "mp4", "wmv", "mkv", "mov"]
.any((extension) => toLowerCase().endsWith(extension));