feat: add more options to manage covers

This commit is contained in:
kodjomoustapha 2024-02-16 19:31:06 +01:00
parent 94ebf47e4e
commit f71c40f0f7
26 changed files with 680 additions and 281 deletions

View file

@ -3,7 +3,7 @@ import 'package:bot_toast/bot_toast.dart';
import 'package:dart_eval/dart_eval_bridge.dart'; import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/stdlib/core.dart'; import 'package:dart_eval/stdlib/core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:html/dom.dart'; import 'package:html/dom.dart' hide Text;
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:js_packer/js_packer.dart'; import 'package:js_packer/js_packer.dart';
@ -631,13 +631,23 @@ void botToast(String title,
double? fontSize, double? fontSize,
double alignX = 0, double alignX = 0,
double alignY = 0.99}) { double alignY = 0.99}) {
BotToast.showSimpleNotification( final assets = [
titleStyle: TextStyle(fontSize: fontSize), 'assets/app_icons/icon-black.png',
onlyOne: true, 'assets/app_icons/icon-red.png'
dismissDirections: [DismissDirection.horizontal, DismissDirection.down], ];
align: Alignment(alignX, alignY), BotToast.showNotification(
duration: Duration(seconds: second), onlyOne: true,
title: title); dismissDirections: [DismissDirection.horizontal, DismissDirection.down],
align: Alignment(alignX, alignY),
duration: Duration(seconds: second),
animationDuration: const Duration(milliseconds: 200),
animationReverseDuration: const Duration(milliseconds: 200),
leading: (_) => Image.asset((assets..shuffle()).first, height: 25),
title: (_) => Text(
title,
style: TextStyle(fontSize: fontSize),
),
);
} }
(encrypt.Encrypter, encrypt.IV) _encrypt(String keyy, String ivv) { (encrypt.Encrypter, encrypt.IV) _encrypt(String keyy, String ivv) {

View file

@ -287,5 +287,11 @@
"next_chapter": "الفصل التالي", "next_chapter": "الفصل التالي",
"next_5_chapters": "الفصول الخمسة التالية", "next_5_chapters": "الفصول الخمسة التالية",
"next_10_chapters": "الفصول العشرة التالية", "next_10_chapters": "الفصول العشرة التالية",
"next_25_chapters": "الفصول الخمسة والعشرون التالية" "next_25_chapters": "الفصول الخمسة والعشرون التالية",
"cover_saved": "الغلاف المحفوظ",
"set_as_cover": "تعيين كغطاء",
"use_this_as_cover_art": "هل تريد استخدام هذا كفن الغلاف؟",
"save": "حفظ",
"picture_saved": "الصورة المحفوظة",
"cover_updated": "تم تحديث الغلاف"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Nächstes Kapitel", "next_chapter": "Nächstes Kapitel",
"next_5_chapters": "Nächsten 5 Kapitel", "next_5_chapters": "Nächsten 5 Kapitel",
"next_10_chapters": "Nächsten 10 Kapitel", "next_10_chapters": "Nächsten 10 Kapitel",
"next_25_chapters": "Nächsten 25 Kapitel" "next_25_chapters": "Nächsten 25 Kapitel",
"cover_saved": "Titelbild gespeichert",
"set_as_cover": "Als Titelbild festlegen",
"use_this_as_cover_art": "Dies als Titelbild verwenden?",
"save": "Speichern",
"picture_saved": "Bild gespeichert",
"cover_updated": "Cover aktualisiert"
} }

View file

@ -289,5 +289,11 @@
"next_chapter": "Next chapter", "next_chapter": "Next chapter",
"next_5_chapters": "Next 5 chapters", "next_5_chapters": "Next 5 chapters",
"next_10_chapters": "Next 10 chapters", "next_10_chapters": "Next 10 chapters",
"next_25_chapters": "Next 25 chapters" "next_25_chapters": "Next 25 chapters",
"cover_saved": "Cover saved",
"set_as_cover": "Set as cover",
"use_this_as_cover_art": "Use this as cover art?",
"save": "Save",
"picture_saved": "Picture saved",
"cover_updated": "Cover updated"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Próximo capítulo", "next_chapter": "Próximo capítulo",
"next_5_chapters": "Próximos 5 capítulos", "next_5_chapters": "Próximos 5 capítulos",
"next_10_chapters": "Próximos 10 capítulos", "next_10_chapters": "Próximos 10 capítulos",
"next_25_chapters": "Próximos 25 capítulos" "next_25_chapters": "Próximos 25 capítulos",
"cover_saved": "Portada guardada",
"set_as_cover": "Establecer como portada",
"use_this_as_cover_art": "¿Usar esto como portada?",
"save": "Guardar",
"picture_saved": "Imagen guardada",
"cover_updated": "Portada actualizada"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Siguiente capítulo", "next_chapter": "Siguiente capítulo",
"next_5_chapters": "Siguientes 5 capítulos", "next_5_chapters": "Siguientes 5 capítulos",
"next_10_chapters": "Siguientes 10 capítulos", "next_10_chapters": "Siguientes 10 capítulos",
"next_25_chapters": "Siguientes 25 capítulos" "next_25_chapters": "Siguientes 25 capítulos",
"cover_saved": "Portada guardada",
"set_as_cover": "Establecer como portada",
"use_this_as_cover_art": "¿Usar esto como portada?",
"save": "Guardar",
"picture_saved": "Imagen guardada",
"cover_updated": "Portada actualizada"
} }

View file

@ -288,5 +288,11 @@
"next_chapter": "Chapitre suivant", "next_chapter": "Chapitre suivant",
"next_5_chapters": "5 chapitres suivants", "next_5_chapters": "5 chapitres suivants",
"next_10_chapters": "10 chapitres suivants", "next_10_chapters": "10 chapitres suivants",
"next_25_chapters": "25 chapitres suivants" "next_25_chapters": "25 chapitres suivants",
"cover_saved": "Couverture enregistrée",
"set_as_cover": "Définir comme couverture",
"use_this_as_cover_art": "Utiliser ceci comme illustration de couverture ?",
"save": "Enregistrer",
"picture_saved": "Image enregistrée",
"cover_updated": "Couverture mise à jour"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Berikutnya bab", "next_chapter": "Berikutnya bab",
"next_5_chapters": "5 bab berikutnya", "next_5_chapters": "5 bab berikutnya",
"next_10_chapters": "10 bab berikutnya", "next_10_chapters": "10 bab berikutnya",
"next_25_chapters": "25 bab berikutnya" "next_25_chapters": "25 bab berikutnya",
"cover_saved": "Sampul disimpan",
"set_as_cover": "Atur sebagai sampul",
"use_this_as_cover_art": "Gunakan ini sebagai seni sampul?",
"save": "Simpan",
"picture_saved": "Gambar disimpan",
"cover_updated": "Penutup diperbarui"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Capitolo successivo", "next_chapter": "Capitolo successivo",
"next_5_chapters": "Prossimi 5 capitoli", "next_5_chapters": "Prossimi 5 capitoli",
"next_10_chapters": "Prossimi 10 capitoli", "next_10_chapters": "Prossimi 10 capitoli",
"next_25_chapters": "Prossimi 25 capitoli" "next_25_chapters": "Prossimi 25 capitoli",
"cover_saved": "Copertina salvata",
"set_as_cover": "Imposta come copertina",
"use_this_as_cover_art": "Usare questo come copertina?",
"save": "Salva",
"picture_saved": "Immagine salvata",
"cover_updated": "Copertina aggiornata"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Próximo capítulo", "next_chapter": "Próximo capítulo",
"next_5_chapters": "Próximos 5 capítulos", "next_5_chapters": "Próximos 5 capítulos",
"next_10_chapters": "Próximos 10 capítulos", "next_10_chapters": "Próximos 10 capítulos",
"next_25_chapters": "Próximos 25 capítulos" "next_25_chapters": "Próximos 25 capítulos",
"cover_saved": "Capa salva",
"set_as_cover": "Definir como capa",
"use_this_as_cover_art": "Usar isso como arte de capa?",
"save": "Salvar",
"picture_saved": "Imagem salva",
"cover_updated": "Capa atualizada"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Próximo capítulo", "next_chapter": "Próximo capítulo",
"next_5_chapters": "Próximos 5 capítulos", "next_5_chapters": "Próximos 5 capítulos",
"next_10_chapters": "Próximos 10 capítulos", "next_10_chapters": "Próximos 10 capítulos",
"next_25_chapters": "Próximos 25 capítulos" "next_25_chapters": "Próximos 25 capítulos",
"cover_saved": "Capa salva",
"set_as_cover": "Definir como capa",
"use_this_as_cover_art": "Usar isso como arte de capa?",
"save": "Salvar",
"picture_saved": "Foto salva",
"cover_updated": "Capa atualizada"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Следующая глава", "next_chapter": "Следующая глава",
"next_5_chapters": "Следующие 5 глав", "next_5_chapters": "Следующие 5 глав",
"next_10_chapters": "Следующие 10 глав", "next_10_chapters": "Следующие 10 глав",
"next_25_chapters": "Следующие 25 глав" "next_25_chapters": "Следующие 25 глав",
"cover_saved": "Обложка сохранена",
"set_as_cover": "Установить как обложку",
"use_this_as_cover_art": "Использовать это как обложку?",
"save": "Сохранить",
"picture_saved": "Изображение сохранено",
"cover_updated": "Обложка обновлена"
} }

View file

@ -287,5 +287,11 @@
"next_chapter": "Sonraki bölüm", "next_chapter": "Sonraki bölüm",
"next_5_chapters": "Sonraki 5 bölüm", "next_5_chapters": "Sonraki 5 bölüm",
"next_10_chapters": "Sonraki 10 bölüm", "next_10_chapters": "Sonraki 10 bölüm",
"next_25_chapters": "Sonraki 25 bölüm" "next_25_chapters": "Sonraki 25 bölüm",
"cover_saved": "Kapak kaydedildi",
"set_as_cover": "Kapak olarak ayarla",
"use_this_as_cover_art": "Bu resmi kapak sanatı olarak kullan?",
"save": "Kaydet",
"picture_saved": "Resim kaydedildi",
"cover_updated": "Kapak güncellendi"
} }

View file

@ -289,5 +289,11 @@
"next_chapter": "下一章", "next_chapter": "下一章",
"next_5_chapters": "下5章", "next_5_chapters": "下5章",
"next_10_chapters": "下10章", "next_10_chapters": "下10章",
"next_25_chapters": "下25章" "next_25_chapters": "下25章",
"cover_saved": "封面已保存",
"set_as_cover": "设置为封面",
"use_this_as_cover_art": "使用此作为封面?",
"save": "保存",
"picture_saved": "图片已保存",
"cover_updated": "封面已更新"
} }

View file

@ -29,7 +29,8 @@ class DesktopControllerWidget extends StatefulWidget {
required this.tempDuration}); required this.tempDuration});
@override @override
State<DesktopControllerWidget> createState() => _DesktopControllerWidgetState(); State<DesktopControllerWidget> createState() =>
_DesktopControllerWidgetState();
} }
class _DesktopControllerWidgetState extends State<DesktopControllerWidget> { class _DesktopControllerWidgetState extends State<DesktopControllerWidget> {
@ -797,13 +798,16 @@ class _CustomMaterialDesktopFullscreenButtonState
Future<bool> setFullScreen({bool? value}) async { Future<bool> setFullScreen({bool? value}) async {
if (value != null) { if (value != null) {
await windowManager.setTitleBarStyle( final isFullScreen = await windowManager.isFullScreen();
value == false ? TitleBarStyle.normal : TitleBarStyle.hidden); if (value != isFullScreen) {
await windowManager.setFullScreen(value); await windowManager.setTitleBarStyle(
if (value == false) { value == false ? TitleBarStyle.normal : TitleBarStyle.hidden);
await windowManager.center(); await windowManager.setFullScreen(value);
if (value == false) {
await windowManager.center();
}
await windowManager.show();
} }
await windowManager.show();
return value; return value;
} }
final isFullScreen = await windowManager.isFullScreen(); final isFullScreen = await windowManager.isFullScreen();

View file

@ -9,6 +9,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/main.dart'; import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/download.dart'; import 'package:mangayomi/models/download.dart';
@ -24,10 +25,12 @@ import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provi
import 'package:mangayomi/modules/more/settings/appearance/providers/pure_black_dark_mode_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/more/settings/track/widgets/track_listile.dart';
import 'package:mangayomi/providers/l10n_providers.dart'; import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/services/get_source_baseurl.dart'; import 'package:mangayomi/services/get_source_baseurl.dart';
import 'package:mangayomi/sources/utils/utils.dart'; import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/utils/cached_network.dart'; import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/extensions/others.dart';
import 'package:mangayomi/utils/headers.dart'; import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/modules/manga/detail/providers/isar_providers.dart'; import 'package:mangayomi/modules/manga/detail/providers/isar_providers.dart';
import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart'; import 'package:mangayomi/modules/manga/detail/providers/state_providers.dart';
@ -1581,7 +1584,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
); );
} }
_openImage(ImageProvider imageProvider) { void _openImage(ImageProvider imageProvider) {
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@ -1610,141 +1613,221 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
Positioned( Positioned(
bottom: 0, bottom: 0,
right: 0, right: 0,
child: Row( child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Column( Padding(
children: [ padding: const EdgeInsets.symmetric(horizontal: 8.0),
StreamBuilder( child: StreamBuilder(
stream: isar.trackPreferences stream: isar.trackPreferences
.filter() .filter()
.syncIdIsNotNull() .syncIdIsNotNull()
.watch(fireImmediately: true), .watch(fireImmediately: true),
builder: (context, snapshot) { builder: (context, snapshot) {
List<TrackPreference>? entries = List<TrackPreference>? entries =
snapshot.hasData ? snapshot.data! : []; snapshot.hasData ? snapshot.data! : [];
if (entries.isEmpty) { if (entries.isEmpty) {
return Container(); return Container();
} }
return Column( return Column(
children: entries children: entries
.map((e) => Padding( .map((e) => Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: MaterialButton( child: MaterialButton(
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: borderRadius:
BorderRadius.circular(10)), BorderRadius.circular(10)),
onPressed: () async { onPressed: () async {
final trackSearch = final trackSearch =
await trackersSearchraggableMenu( await trackersSearchraggableMenu(
context, context,
isManga: widget.manga!.isManga!, isManga: widget.manga!.isManga!,
track: Track( track: Track(
status: status:
TrackStatus.planToRead, TrackStatus.planToRead,
syncId: e.syncId!, syncId: e.syncId!,
title: widget.manga!.name!), title: widget.manga!.name!),
) as TrackSearch?; ) as TrackSearch?;
if (trackSearch != null) { if (trackSearch != null) {
isar.writeTxnSync(() { isar.writeTxnSync(() {
isar.mangas.putSync(widget isar.mangas.putSync(
.manga! widget.manga!
..customCoverFromTracker = ..customCoverImage = null
trackSearch.coverUrl); ..customCoverFromTracker =
}); trackSearch.coverUrl);
if (context.mounted) { });
Navigator.pop(context); if (context.mounted) {
} Navigator.pop(context);
botToast(
context.l10n.cover_updated,
second: 3);
} }
}, }
child: Container( },
decoration: BoxDecoration( child: Container(
borderRadius: decoration: BoxDecoration(
BorderRadius.circular(10), borderRadius:
color: BorderRadius.circular(10),
trackInfos(e.syncId!).$3), color:
width: 45, trackInfos(e.syncId!).$3),
height: 50, width: 45,
child: Image.asset( height: 50,
trackInfos(e.syncId!).$1, child: Image.asset(
height: 30, trackInfos(e.syncId!).$1,
), height: 30,
), ),
), ),
)) ),
.toList(), ))
); .toList(),
}, );
), },
),
PopupMenuButton( ),
itemBuilder: (context) { SizedBox(
return [ width: context.mediaWidth(1),
if (widget.manga!.customCoverImage != null || child: Row(
widget.manga!.customCoverFromTracker != mainAxisAlignment: MainAxisAlignment.spaceBetween,
null) children: [
PopupMenuItem<int>( Padding(
value: 0, padding: const EdgeInsets.all(8.0),
child: Text(context.l10n.delete)), child: Container(
PopupMenuItem<int>( decoration: BoxDecoration(
value: 1, child: Text(context.l10n.edit)), borderRadius: BorderRadius.circular(20),
]; color: context.isLight
}, ? Colors.white
onSelected: (value) async { : Colors.black),
final manga = widget.manga!; child: GestureDetector(
if (value == 0) { onTap: () {
isar.writeTxnSync(() { Navigator.pop(context);
isar.mangas.putSync(manga },
..customCoverImage = null child: const Padding(
..customCoverFromTracker = null); padding: EdgeInsets.all(8.0),
}); child: Icon(Icons.close),
Navigator.pop(context); )),
} else if (value == 1) { ),
FilePickerResult? result =
await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: [
'png',
'jpg',
'jpeg'
]);
if (result != null) {
if (result.files.first.size < 5000000) {
final customCoverImage =
File(result.files.first.path!)
.readAsBytesSync();
isar.writeTxnSync(() {
isar.mangas.putSync(manga
..customCoverImage = customCoverImage);
});
}
}
if (context.mounted) {
Navigator.pop(context);
}
}
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: CircleAvatar(
child: Icon(Icons.edit_outlined)),
), ),
), Padding(
// IconButton( padding: const EdgeInsets.all(8.0),
// onPressed: () async { child: Container(
// Uint8List? bytes; decoration: BoxDecoration(
// if (isLocalArchive) { borderRadius: BorderRadius.circular(20),
// bytes = color: context.isLight
// widget.manga!.customCoverImage as Uint8List?; ? Colors.white
// } : Colors.black),
// await Share.shareXFiles([ child: Row(
// XFile.fromData(bytes!, children: [
// name: widget.manga!.name, GestureDetector(
// mimeType: 'image/jpeg') onTap: () async {
// ]); final bytes = await imageProvider
// }, .getBytes(context);
// icon: const CircleAvatar(child: Icon(Icons.share))), if (bytes != null) {
], await Share.shareXFiles([
XFile.fromData(bytes,
name: widget.manga!.name,
mimeType: 'image/png')
]);
}
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.share),
)),
GestureDetector(
onTap: () async {
final dir = await StorageProvider()
.getGalleryDirectory();
if (context.mounted) {
final bytes = await imageProvider
.getBytes(context);
if (bytes != null &&
context.mounted) {
final file = File(
'${dir!.path}/${widget.manga!.name}.png');
file.writeAsBytesSync(bytes);
botToast(context.l10n.cover_saved,
second: 3);
}
}
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.save_outlined),
)),
PopupMenuButton(
itemBuilder: (context) {
return [
if (widget.manga!.customCoverImage !=
null ||
widget.manga!
.customCoverFromTracker !=
null)
PopupMenuItem<int>(
value: 0,
child:
Text(context.l10n.delete)),
PopupMenuItem<int>(
value: 1,
child: Text(context.l10n.edit)),
];
},
onSelected: (value) async {
final manga = widget.manga!;
if (value == 0) {
isar.writeTxnSync(() {
isar.mangas.putSync(manga
..customCoverImage = null
..customCoverFromTracker = null);
});
Navigator.pop(context);
} else if (value == 1) {
FilePickerResult? result =
await FilePicker.platform
.pickFiles(
type: FileType.custom,
allowedExtensions: [
'png',
'jpg',
'jpeg'
]);
if (result != null &&
context.mounted) {
if (result.files.first.size <
5000000) {
final customCoverImage =
File(result.files.first.path!)
.readAsBytesSync();
isar.writeTxnSync(() {
isar.mangas.putSync(manga
..customCoverImage =
customCoverImage);
});
botToast(
context.l10n.cover_updated,
second: 3);
}
}
if (context.mounted) {
Navigator.pop(context);
}
}
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.edit_outlined,
color: !context.isLight
? Colors.white
: Colors.black,
)),
),
],
),
),
),
],
),
), ),
], ],
), ),

View file

@ -14,6 +14,7 @@ import 'package:photo_view/photo_view_gallery.dart';
class DoubleColummView extends StatefulWidget { class DoubleColummView extends StatefulWidget {
final bool cropBorders; final bool cropBorders;
final List<UChapDataPreload?> datas; final List<UChapDataPreload?> datas;
final Function(UChapDataPreload datas) onLongPressData;
final Function(double) scale; final Function(double) scale;
final BackgroundColor backgroundColor; final BackgroundColor backgroundColor;
final Function(bool) isFailedToLoadImage; final Function(bool) isFailedToLoadImage;
@ -21,6 +22,7 @@ class DoubleColummView extends StatefulWidget {
{super.key, {super.key,
required this.datas, required this.datas,
required this.scale, required this.scale,
required this.onLongPressData,
required this.backgroundColor, required this.backgroundColor,
required this.isFailedToLoadImage, required this.isFailedToLoadImage,
required this.cropBorders}); required this.cropBorders});
@ -199,6 +201,8 @@ class _DoubleColummViewState extends State<DoubleColummView>
return null; return null;
}, },
cropBorders: widget.cropBorders, cropBorders: widget.cropBorders,
onLongPressData: (datas) =>
widget.onLongPressData.call(datas),
), ),
), ),
// if (widget.datas[1] != null) const SizedBox(width: 10), // if (widget.datas[1] != null) const SizedBox(width: 10),
@ -276,6 +280,8 @@ class _DoubleColummViewState extends State<DoubleColummView>
return null; return null;
}, },
cropBorders: widget.cropBorders, cropBorders: widget.cropBorders,
onLongPressData: (datas) =>
widget.onLongPressData.call(datas),
), ),
), ),
], ],

View file

@ -11,6 +11,7 @@ import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
class DoubleColummVerticalView extends StatelessWidget { class DoubleColummVerticalView extends StatelessWidget {
final bool cropBorders; final bool cropBorders;
final List<UChapDataPreload?> datas; final List<UChapDataPreload?> datas;
final Function(UChapDataPreload datas) onLongPressData;
final Function(double) scale; final Function(double) scale;
final BackgroundColor backgroundColor; final BackgroundColor backgroundColor;
final Function(bool) isFailedToLoadImage; final Function(bool) isFailedToLoadImage;
@ -18,6 +19,7 @@ class DoubleColummVerticalView extends StatelessWidget {
{super.key, {super.key,
required this.datas, required this.datas,
required this.scale, required this.scale,
required this.onLongPressData,
required this.backgroundColor, required this.backgroundColor,
required this.isFailedToLoadImage, required this.isFailedToLoadImage,
required this.cropBorders}); required this.cropBorders});
@ -103,6 +105,7 @@ class DoubleColummVerticalView extends StatelessWidget {
return null; return null;
}, },
cropBorders: cropBorders, cropBorders: cropBorders,
onLongPressData: (datas) => onLongPressData.call(datas),
), ),
), ),
// if (datas[1] != null) const SizedBox(width: 10), // if (datas[1] != null) const SizedBox(width: 10),
@ -174,6 +177,7 @@ class DoubleColummVerticalView extends StatelessWidget {
return null; return null;
}, },
cropBorders: cropBorders, cropBorders: cropBorders,
onLongPressData: (datas) => onLongPressData.call(datas),
), ),
), ),
], ],

View file

@ -12,6 +12,7 @@ import 'package:mangayomi/utils/reg_exp_matcher.dart';
class ImageViewCenter extends ConsumerWidget { class ImageViewCenter extends ConsumerWidget {
final UChapDataPreload datas; final UChapDataPreload datas;
final bool cropBorders; final bool cropBorders;
final Function(UChapDataPreload datas) onLongPressData;
final Widget? Function(ExtendedImageState state) loadStateChanged; final Widget? Function(ExtendedImageState state) loadStateChanged;
final Function(ExtendedImageGestureState state)? onDoubleTap; final Function(ExtendedImageGestureState state)? onDoubleTap;
final GestureConfig Function(ExtendedImageState state)? final GestureConfig Function(ExtendedImageState state)?
@ -20,6 +21,7 @@ class ImageViewCenter extends ConsumerWidget {
super.key, super.key,
required this.datas, required this.datas,
required this.cropBorders, required this.cropBorders,
required this.onLongPressData,
required this.loadStateChanged, required this.loadStateChanged,
this.onDoubleTap, this.onDoubleTap,
this.initGestureConfigHandler, this.initGestureConfigHandler,
@ -47,15 +49,18 @@ class ImageViewCenter extends ConsumerWidget {
source: datas.chapter!.manga.value!.source!, source: datas.chapter!.manga.value!.source!,
lang: datas.chapter!.manga.value!.lang!))); lang: datas.chapter!.manga.value!.lang!)));
return ExtendedImage( return GestureDetector(
image: image as ImageProvider<Object>, onLongPress: () => onLongPressData.call(datas),
fit: getBoxFit(scaleType), child: ExtendedImage(
filterQuality: FilterQuality.medium, image: image as ImageProvider<Object>,
enableMemoryCache: true, fit: getBoxFit(scaleType),
mode: ExtendedImageMode.gesture, filterQuality: FilterQuality.medium,
handleLoadingProgress: true, enableMemoryCache: true,
loadStateChanged: loadStateChanged, mode: ExtendedImageMode.gesture,
initGestureConfigHandler: initGestureConfigHandler, handleLoadingProgress: true,
onDoubleTap: onDoubleTap); loadStateChanged: loadStateChanged,
initGestureConfigHandler: initGestureConfigHandler,
onDoubleTap: onDoubleTap),
);
} }
} }

View file

@ -14,6 +14,7 @@ import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicat
class ImageViewVertical extends ConsumerWidget { class ImageViewVertical extends ConsumerWidget {
final UChapDataPreload datas; final UChapDataPreload datas;
final Function(UChapDataPreload datas) onLongPressData;
final bool cropBorders; final bool cropBorders;
final Function(bool) failedToLoadImage; final Function(bool) failedToLoadImage;
@ -21,6 +22,7 @@ class ImageViewVertical extends ConsumerWidget {
const ImageViewVertical( const ImageViewVertical(
{super.key, {super.key,
required this.datas, required this.datas,
required this.onLongPressData,
required this.cropBorders, required this.cropBorders,
required this.failedToLoadImage}); required this.failedToLoadImage});
@ -47,79 +49,83 @@ class ImageViewVertical extends ConsumerWidget {
lang: datas.chapter!.manga.value!.lang!))); lang: datas.chapter!.manga.value!.lang!)));
final scaleType = ref.watch(scaleTypeStateProvider); final scaleType = ref.watch(scaleTypeStateProvider);
final l10n = l10nLocalizations(context)!; final l10n = l10nLocalizations(context)!;
return Column( return GestureDetector(
mainAxisAlignment: MainAxisAlignment.center, onLongPress: () => onLongPressData.call(datas),
children: [ child: Column(
if (datas.index == 0) mainAxisAlignment: MainAxisAlignment.center,
SizedBox( children: [
height: MediaQuery.of(context).padding.top, if (datas.index == 0)
), SizedBox(
ExtendedImage( height: MediaQuery.of(context).padding.top,
image: image as ImageProvider<Object>, ),
filterQuality: FilterQuality.medium, ExtendedImage(
handleLoadingProgress: true, image: image as ImageProvider<Object>,
fit: getBoxFit(scaleType), filterQuality: FilterQuality.medium,
enableMemoryCache: true, handleLoadingProgress: true,
enableLoadState: true, fit: getBoxFit(scaleType),
loadStateChanged: (state) { enableMemoryCache: true,
if (state.extendedImageLoadState == LoadState.completed) { enableLoadState: true,
failedToLoadImage(false); loadStateChanged: (state) {
} if (state.extendedImageLoadState == LoadState.completed) {
if (state.extendedImageLoadState == LoadState.loading) { failedToLoadImage(false);
final ImageChunkEvent? loadingProgress = state.loadingProgress; }
final double progress = if (state.extendedImageLoadState == LoadState.loading) {
loadingProgress?.expectedTotalBytes != null final ImageChunkEvent? loadingProgress =
? loadingProgress!.cumulativeBytesLoaded / state.loadingProgress;
loadingProgress.expectedTotalBytes! final double progress =
: 0; loadingProgress?.expectedTotalBytes != null
return Container( ? loadingProgress!.cumulativeBytesLoaded /
color: Colors.black, loadingProgress.expectedTotalBytes!
height: context.mediaHeight(0.8), : 0;
child: CircularProgressIndicatorAnimateRotate( return Container(
progress: progress),
);
}
if (state.extendedImageLoadState == LoadState.failed) {
failedToLoadImage(true);
return Container(
color: Colors.black, color: Colors.black,
height: context.mediaHeight(0.8), height: context.mediaHeight(0.8),
child: Column( child: CircularProgressIndicatorAnimateRotate(
mainAxisAlignment: MainAxisAlignment.center, progress: progress),
children: [ );
Text(l10n.image_loading_error, }
style: TextStyle( if (state.extendedImageLoadState == LoadState.failed) {
color: Colors.white.withOpacity(0.7))), failedToLoadImage(true);
Padding( return Container(
padding: const EdgeInsets.all(8.0), color: Colors.black,
child: GestureDetector( height: context.mediaHeight(0.8),
onLongPress: () { child: Column(
state.reLoadImage(); mainAxisAlignment: MainAxisAlignment.center,
failedToLoadImage(false); children: [
}, Text(l10n.image_loading_error,
onTap: () { style: TextStyle(
state.reLoadImage(); color: Colors.white.withOpacity(0.7))),
failedToLoadImage(false); Padding(
}, padding: const EdgeInsets.all(8.0),
child: Container( child: GestureDetector(
decoration: BoxDecoration( onLongPress: () {
color: context.primaryColor, state.reLoadImage();
borderRadius: BorderRadius.circular(30)), failedToLoadImage(false);
child: Padding( },
padding: const EdgeInsets.symmetric( onTap: () {
vertical: 8, horizontal: 16), state.reLoadImage();
child: Text( failedToLoadImage(false);
l10n.retry, },
child: Container(
decoration: BoxDecoration(
color: context.primaryColor,
borderRadius: BorderRadius.circular(30)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 16),
child: Text(
l10n.retry,
),
), ),
), )),
)), ),
), ],
], ));
)); }
} return null;
return null; }),
}), ],
], ),
); );
} }
} }

View file

@ -1,10 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:mangayomi/messages/crop_borders.pb.dart'; import 'package:mangayomi/messages/crop_borders.pb.dart';
import 'package:mangayomi/modules/manga/reader/reader_view.dart'; import 'package:mangayomi/modules/manga/reader/reader_view.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart'; import 'package:mangayomi/utils/extensions/others.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'crop_borders_provider.g.dart'; part 'crop_borders_provider.g.dart';
@ -16,18 +14,8 @@ Future<Uint8List?> cropBorders(CropBordersRef ref,
Uint8List? imageBytes; Uint8List? imageBytes;
if (cropBorder) { if (cropBorder) {
if (datas.archiveImage != null) { imageBytes = await datas.getImageBytes;
imageBytes = datas.archiveImage;
} else if (datas.isLocale!) {
imageBytes = File('${datas.path!.path}${padIndex(datas.index! + 1)}.jpg')
.readAsBytesSync();
} else {
File? cachedImage;
if (datas.url != null) {
cachedImage = await getCachedImageFile(datas.url!);
}
imageBytes = cachedImage?.readAsBytesSync();
}
if (imageBytes == null) { if (imageBytes == null) {
return null; return null;
} }

View file

@ -9,9 +9,11 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:mangayomi/eval/model/m_bridge.dart';
import 'package:mangayomi/main.dart'; import 'package:mangayomi/main.dart';
import 'package:mangayomi/messages/generated.dart'; import 'package:mangayomi/messages/generated.dart';
import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart'; import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/modules/anime/widgets/desktop.dart'; import 'package:mangayomi/modules/anime/widgets/desktop.dart';
import 'package:mangayomi/modules/manga/reader/double_columm_view_vertical.dart'; import 'package:mangayomi/modules/manga/reader/double_columm_view_vertical.dart';
@ -19,10 +21,12 @@ import 'package:mangayomi/modules/manga/reader/double_columm_view_center.dart';
import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart'; import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart'; import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart'; import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/sources/utils/utils.dart'; import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart'; import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
import 'package:mangayomi/services/get_chapter_pages.dart'; import 'package:mangayomi/services/get_chapter_pages.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart'; import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/extensions/others.dart';
import 'package:mangayomi/utils/headers.dart'; import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/modules/manga/reader/image_view_center.dart'; import 'package:mangayomi/modules/manga/reader/image_view_center.dart';
import 'package:mangayomi/modules/manga/reader/image_view_vertical.dart'; import 'package:mangayomi/modules/manga/reader/image_view_vertical.dart';
@ -34,6 +38,7 @@ import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart'; import 'package:photo_view/photo_view_gallery.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:share_plus/share_plus.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
typedef DoubleClickAnimationListener = void Function(); typedef DoubleClickAnimationListener = void Function();
@ -241,6 +246,134 @@ class _MangaChapterPageGalleryState
ref.read(fullScreenReaderStateProvider.notifier).set(!value!); ref.read(fullScreenReaderStateProvider.notifier).set(!value!);
} }
void _onLongPressImageDialog(
UChapDataPreload datas, BuildContext context) async {
Widget button(String label, IconData icon, Function() onPressed) =>
Expanded(
child: Padding(
padding: const EdgeInsets.all(15),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
elevation: 0,
shadowColor: Colors.transparent),
onPressed: onPressed,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(4),
child: Icon(icon),
),
Text(label)
],
)),
),
);
final imageBytes = await datas.getImageBytes;
if (imageBytes != null && context.mounted) {
final name =
"${widget.chapter.manga.value!.name} ${widget.chapter.name} - ${datas.pageIndex}"
.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_');
showModalBottomSheet(
context: context,
constraints: BoxConstraints(
maxWidth: context.mediaWidth(1),
),
builder: (context) {
return SizedBox(
height: 120,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20)),
color: context.themeData.scaffoldBackgroundColor),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 7,
width: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: context.secondaryColor.withOpacity(0.4)),
),
),
Row(
children: [
button(context.l10n.set_as_cover, Icons.image_outlined,
() async {
final res = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content:
Text(context.l10n.use_this_as_cover_art),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(context.l10n.cancel)),
const SizedBox(
width: 15,
),
TextButton(
onPressed: () {
final manga =
widget.chapter.manga.value!;
isar.writeTxnSync(() {
isar.mangas.putSync(manga
..customCoverImage =
imageBytes);
});
if (mounted) {
Navigator.pop(context, "ok");
}
},
child: Text(context.l10n.ok)),
],
)
],
);
});
if (res != null && res == "ok" && context.mounted) {
Navigator.pop(context);
botToast(context.l10n.cover_updated, second: 3);
}
}),
button(context.l10n.share, Icons.share_outlined,
() async {
await Share.shareXFiles([
XFile.fromData(imageBytes,
name: name, mimeType: 'image/png')
]);
}),
button(context.l10n.save, Icons.save_outlined, () async {
final dir =
await StorageProvider().getGalleryDirectory();
final file = File("${dir!.path}/$name.png");
file.writeAsBytesSync(imageBytes);
if (context.mounted) {
botToast(context.l10n.picture_saved, second: 3);
}
}),
],
),
],
),
),
);
},
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final backgroundColor = ref.watch(backgroundColorStateProvider); final backgroundColor = ref.watch(backgroundColorStateProvider);
@ -404,6 +537,10 @@ class _MangaChapterPageGalleryState
backgroundColor, backgroundColor,
isFailedToLoadImage: (val) {}, isFailedToLoadImage: (val) {},
cropBorders: cropBorders, cropBorders: cropBorders,
onLongPressData: (datas) {
_onLongPressImageDialog(
datas, context);
},
) )
: ImageViewVertical( : ImageViewVertical(
datas: datas:
@ -412,6 +549,10 @@ class _MangaChapterPageGalleryState
// _failedToLoadImage.value = value; // _failedToLoadImage.value = value;
}, },
cropBorders: cropBorders, cropBorders: cropBorders,
onLongPressData: (datas) {
_onLongPressImageDialog(
datas, context);
},
), ),
); );
}, },
@ -462,6 +603,10 @@ class _MangaChapterPageGalleryState
} }
}, },
cropBorders: cropBorders, cropBorders: cropBorders,
onLongPressData: (datas) {
_onLongPressImageDialog(
datas, context);
},
); );
}, },
itemCount: itemCount:
@ -658,6 +803,10 @@ class _MangaChapterPageGalleryState
.forward(); .forward();
}, },
cropBorders: cropBorders, cropBorders: cropBorders,
onLongPressData: (datas) {
_onLongPressImageDialog(
datas, context);
},
); );
}, },
itemCount: _uChapDataPreload.length, itemCount: _uChapDataPreload.length,
@ -1666,15 +1815,17 @@ class _MangaChapterPageGalleryState
flex: 2, flex: 2,
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: usePageTapZones onTap: () {
? () { if (usePageTapZones) {
if (_isReverseHorizontal) { if (_isReverseHorizontal) {
_onBtnTapped(_currentIndex! + 1, false); _onBtnTapped(_currentIndex! + 1, false);
} else { } else {
_onBtnTapped(_currentIndex! - 1, true); _onBtnTapped(_currentIndex! - 1, true);
} }
} } else {
: null, _isViewFunction();
}
},
onDoubleTapDown: _isVerticalContinous() onDoubleTapDown: _isVerticalContinous()
? (TapDownDetails details) { ? (TapDownDetails details) {
_toggleScale(details.globalPosition); _toggleScale(details.globalPosition);
@ -1711,15 +1862,17 @@ class _MangaChapterPageGalleryState
flex: 2, flex: 2,
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: usePageTapZones onTap: () {
? () { if (usePageTapZones) {
if (_isReverseHorizontal) { if (_isReverseHorizontal) {
_onBtnTapped(_currentIndex! - 1, true); _onBtnTapped(_currentIndex! - 1, true);
} else { } else {
_onBtnTapped(_currentIndex! + 1, false); _onBtnTapped(_currentIndex! + 1, false);
} }
} } else {
: null, _isViewFunction();
}
},
onDoubleTapDown: _isVerticalContinous() onDoubleTapDown: _isVerticalContinous()
? (TapDownDetails details) { ? (TapDownDetails details) {
_toggleScale(details.globalPosition); _toggleScale(details.globalPosition);
@ -1749,7 +1902,7 @@ class _MangaChapterPageGalleryState
? _isViewFunction() ? _isViewFunction()
: usePageTapZones : usePageTapZones
? _onBtnTapped(_currentIndex! - 1, true) ? _onBtnTapped(_currentIndex! - 1, true)
: null; : _isViewFunction();
}, },
onDoubleTapDown: _isVerticalContinous() onDoubleTapDown: _isVerticalContinous()
? (TapDownDetails details) { ? (TapDownDetails details) {
@ -1773,7 +1926,7 @@ class _MangaChapterPageGalleryState
? _isViewFunction() ? _isViewFunction()
: usePageTapZones : usePageTapZones
? _onBtnTapped(_currentIndex! + 1, false) ? _onBtnTapped(_currentIndex! + 1, false)
: null; : _isViewFunction();
}, },
onDoubleTapDown: _isVerticalContinous() onDoubleTapDown: _isVerticalContinous()
? (TapDownDetails details) { ? (TapDownDetails details) {

View file

@ -126,6 +126,10 @@ void doBackUp(DoBackUpRef ref,
encoder.addFile(File(backupFilePath)); encoder.addFile(File(backupFilePath));
encoder.close(); encoder.close();
Directory(backupFilePath).deleteSync(recursive: true); Directory(backupFilePath).deleteSync(recursive: true);
final assets = [
'assets/app_icons/icon-black.png',
'assets/app_icons/icon-red.png'
];
if (context != null) { if (context != null) {
Navigator.pop(context); Navigator.pop(context);
BotToast.showNotification( BotToast.showNotification(
@ -133,8 +137,7 @@ void doBackUp(DoBackUpRef ref,
animationReverseDuration: const Duration(milliseconds: 200), animationReverseDuration: const Duration(milliseconds: 200),
duration: const Duration(seconds: 5), duration: const Duration(seconds: 5),
backButtonBehavior: BackButtonBehavior.none, backButtonBehavior: BackButtonBehavior.none,
leading: (cancel) => leading: (_) => Image.asset((assets..shuffle()).first, height: 25),
Image.asset('assets/app_icons/icon-red.png', height: 40),
title: (_) => const Text( title: (_) => const Text(
"Backup created!", "Backup created!",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),

View file

@ -20,7 +20,7 @@ class StorageProvider {
final RegExp _regExpChar = RegExp(r'[^a-zA-Z0-9 .()\-\s]'); final RegExp _regExpChar = RegExp(r'[^a-zA-Z0-9 .()\-\s]');
Future<bool> requestPermission() async { Future<bool> requestPermission() async {
Permission permission = Permission.manageExternalStorage; Permission permission = Permission.manageExternalStorage;
if (Platform.isAndroid || Platform.isIOS) { if (Platform.isAndroid) {
if (await permission.isGranted) { if (await permission.isGranted) {
return true; return true;
} else { } else {
@ -105,6 +105,17 @@ class StorageProvider {
} }
} }
Future<Directory?> getGalleryDirectory() async {
String gPath = (await getDirectory())!.path;
if (Platform.isAndroid) {
gPath = "/storage/emulated/0/Pictures/Mangayomi/";
} else {
gPath = path.join(gPath, 'Pictures');
}
await Directory(gPath).create(recursive: true);
return Directory(gPath);
}
Future<Isar> initDB(String? path, {bool? inspector = false}) async { Future<Isar> initDB(String? path, {bool? inspector = false}) async {
Directory? dir; Directory? dir;
if (path == null) { if (path == null) {

View file

@ -1,5 +1,53 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/modules/manga/reader/reader_view.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
extension LetExtension<T> on T { extension LetExtension<T> on T {
R let<R>(R Function(T) block) { R let<R>(R Function(T) block) {
return block(this); return block(this);
} }
} }
extension ImageProviderExtension on ImageProvider {
Future<Uint8List?> getBytes(BuildContext context,
{ImageByteFormat format = ImageByteFormat.png}) async {
final imageStream = resolve(createLocalImageConfiguration(context));
final Completer<Uint8List?> completer = Completer<Uint8List?>();
final ImageStreamListener listener = ImageStreamListener(
(imageInfo, synchronousCall) async {
final bytes = await imageInfo.image.toByteData(format: format);
if (!completer.isCompleted) {
completer.complete(bytes?.buffer.asUint8List());
}
},
);
imageStream.addListener(listener);
final imageBytes = await completer.future;
imageStream.removeListener(listener);
return imageBytes;
}
}
extension UChapDataPreloadExtensions on UChapDataPreload {
Future<Uint8List?> get getImageBytes async {
Uint8List? imageBytes;
if (archiveImage != null) {
imageBytes = archiveImage;
} else if (isLocale!) {
imageBytes =
File('${path!.path}${padIndex(index! + 1)}.jpg').readAsBytesSync();
} else {
File? cachedImage;
if (url != null) {
cachedImage = await getCachedImageFile(url!);
}
imageBytes = cachedImage?.readAsBytesSync();
}
return imageBytes;
}
}

View file

@ -8,14 +8,14 @@ part 'headers.g.dart';
Map<String, String> headers(HeadersRef ref, Map<String, String> headers(HeadersRef ref,
{required String source, required String lang}) { {required String source, required String lang}) {
final mSource = getSource(lang, source); final mSource = getSource(lang, source);
if (mSource == null) return {};
Map<String, String> headers = {}; Map<String, String> headers = {};
if (mSource?.headers?.isNotEmpty ?? false) { if (mSource.headers?.isNotEmpty ?? false) {
headers = (jsonDecode(mSource!.headers!) as Map) headers = (jsonDecode(mSource.headers!) as Map)
.map((key, value) => MapEntry(key.toString(), value.toString())); .map((key, value) => MapEntry(key.toString(), value.toString()));
} }
final cookies = MInterceptor.getCookiesPref(mSource!.baseUrl!); final cookies = MInterceptor.getCookiesPref(mSource.baseUrl!);
headers.addAll(cookies); headers.addAll(cookies);
return headers; return headers;