From 4f68505a9ba5e29a3093c1d1d14ef8f2531cd9a1 Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Sat, 17 Jun 2023 02:43:55 +0100 Subject: [PATCH] feature: fix reader & add preloaded chapter --- .../archive_reader/archive_reader_screen.dart | 207 ---- .../reader/archive_reader_reader_view.dart | 938 ------------------ lib/modules/main_view/main_screen.dart | 18 +- .../reader/chapter_interval_page_view.dart | 150 +++ .../manga/reader/image_view_vertical.dart | 53 +- .../manga/reader/manga_reader_view.dart | 725 ++++++++------ .../providers/reader_controller_provider.dart | 249 +---- .../reader_controller_provider.g.dart | 96 -- lib/router/router.dart | 41 +- lib/services/get_chapter_url.dart | 26 +- lib/services/get_chapter_url.g.dart | 2 +- pubspec.lock | 8 + pubspec.yaml | 1 + 13 files changed, 666 insertions(+), 1848 deletions(-) delete mode 100644 lib/modules/archive_reader/archive_reader_screen.dart delete mode 100644 lib/modules/archive_reader/reader/archive_reader_reader_view.dart create mode 100644 lib/modules/manga/reader/chapter_interval_page_view.dart diff --git a/lib/modules/archive_reader/archive_reader_screen.dart b/lib/modules/archive_reader/archive_reader_screen.dart deleted file mode 100644 index 4472326f..00000000 --- a/lib/modules/archive_reader/archive_reader_screen.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:mangayomi/modules/archive_reader/models/models.dart'; -import 'package:mangayomi/modules/archive_reader/providers/archive_reader_providers.dart'; -import 'package:mangayomi/modules/widgets/progress_center.dart'; -import 'package:mangayomi/utils/media_query.dart'; - -class LocalReaderScreen extends ConsumerStatefulWidget { - const LocalReaderScreen({super.key}); - - @override - ConsumerState createState() => _LocalReaderScreenState(); -} - -class _LocalReaderScreenState extends ConsumerState { - List images = []; - bool isLoading = false; - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Scaffold( - appBar: AppBar( - title: const Text('Archive Reader'), - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton.icon( - onPressed: () async { - setState(() { - isLoading = true; - }); - //File - FilePickerResult? result = await FilePicker.platform - .pickFiles( - type: FileType.custom, - allowedExtensions: [ - 'cbz', - 'zip', - 'cbt', - 'tar' - ]); - if (result != null) { - //File - final ddd = await ref.watch( - getArchiveDataFromFileProvider( - result.files.first.path!) - .future); - - setState(() { - images.add(ddd); - isLoading = false; - }); - } else { - setState(() { - isLoading = false; - }); - } - }, - label: const Text("Load cbz file"), - icon: const Icon(Icons.archive_rounded)), - ElevatedButton.icon( - onPressed: () async { - setState(() { - isLoading = true; - }); - //Directory - String? result = - await FilePicker.platform.getDirectoryPath(); - - if (result != null) { - //Directory - final ddd = await ref.watch( - getArchiveDataFromDirectoryProvider(result) - .future); - setState(() { - images = ddd; - isLoading = false; - }); - } else { - setState(() { - isLoading = false; - }); - } - }, - label: const Text("Load from directory"), - icon: const Icon(Icons.create_new_folder_rounded)), - ], - ), - Expanded( - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - childAspectRatio: 0.68, crossAxisCount: 3), - itemCount: images.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Material( - borderRadius: BorderRadius.circular(10), - clipBehavior: Clip.antiAliasWithSaveLayer, - child: InkWell( - onTap: () { - context.push("/archiveReaderReaderView", - extra: images[index]); - }, - child: Ink.image( - height: 200, - fit: BoxFit.cover, - image: MemoryImage(images[index].coverImage!), - child: Container( - height: 70, - decoration: BoxDecoration( - color: - Theme.of(context).scaffoldBackgroundColor, - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black.withOpacity(0.6) - ], - stops: const [0, 1], - ), - ), - child: Stack( - children: [ - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - images[index].name!, - style: const TextStyle( - fontSize: 12, - color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0.5, 0.9), - blurRadius: 3.0) - ], - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(5), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: - BorderRadius.circular(5)), - child: Padding( - padding: const EdgeInsets.all(2), - child: Text( - getTypeExtension(images[index] - .extensionType!) - .toUpperCase(), - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 10), - ), - )), - ) - ], - ), - ), - ), - ), - ), - ); - }, - ), - ), - ], - ), - ), - if (isLoading) - Container( - width: mediaWidth(context, 1), - height: mediaHeight(context, 1), - color: Colors.black45, - child: UnconstrainedBox( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: Theme.of(context).scaffoldBackgroundColor, - ), - height: 200, - width: 200, - child: const Center(child: ProgressCenter())), - ), - ) - ], - ); - } -} diff --git a/lib/modules/archive_reader/reader/archive_reader_reader_view.dart b/lib/modules/archive_reader/reader/archive_reader_reader_view.dart deleted file mode 100644 index cf9b91c3..00000000 --- a/lib/modules/archive_reader/reader/archive_reader_reader_view.dart +++ /dev/null @@ -1,938 +0,0 @@ -import 'dart:async'; -import 'dart:math'; -import 'package:draggable_menu/draggable_menu.dart'; -import 'package:extended_image/extended_image.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter/material.dart'; -import 'package:mangayomi/main.dart'; -import 'package:mangayomi/models/settings.dart'; -import 'package:mangayomi/modules/archive_reader/models/models.dart'; -import 'package:mangayomi/utils/image_detail_info.dart'; -import 'package:mangayomi/utils/media_query.dart'; -import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart'; -import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart'; -import 'package:photo_view/photo_view.dart'; -import 'package:photo_view/photo_view_gallery.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; - -typedef DoubleClickAnimationListener = void Function(); - -class ArchiveReaderReaderView extends ConsumerWidget { - final LocalArchive localArchive; - const ArchiveReaderReaderView({ - super.key, - required this.localArchive, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, - overlays: []); - - return MangaChapterPageGallery(localArchive: localArchive); - } -} - -class MangaChapterPageGallery extends ConsumerStatefulWidget { - const MangaChapterPageGallery({super.key, required this.localArchive}); - final LocalArchive localArchive; - - @override - ConsumerState createState() { - return _MangaChapterPageGalleryState(); - } -} - -class _MangaChapterPageGalleryState - extends ConsumerState - with TickerProviderStateMixin { - late final ItemScrollController _itemScrollController = - ItemScrollController(); - late AnimationController _scaleAnimationController; - late Animation _animation; - late int _currentIndex = 0; - @override - void dispose() { - _rebuildDetail.close(); - _doubleClickAnimationController.dispose(); - clearGestureDetailsCache(); - super.dispose(); - } - - bool animatePageTransitions = - isar.settings.getSync(227)!.animatePageTransitions!; - Duration? _doubleTapAnimationDuration() { - int doubleTapAnimationValue = - isar.settings.getSync(227)!.doubleTapAnimationSpeed!; - if (doubleTapAnimationValue == 0) { - return const Duration(milliseconds: 10); - } else if (doubleTapAnimationValue == 1) { - return const Duration(milliseconds: 800); - } - return const Duration(milliseconds: 200); - } - - Future setIndex(int index) async { - setState(() { - _currentIndex = index; - }); - } - - @override - void initState() { - _doubleClickAnimationController = AnimationController( - duration: _doubleTapAnimationDuration(), vsync: this); - - _scaleAnimationController = AnimationController( - duration: _doubleTapAnimationDuration(), vsync: this); - _animation = Tween(begin: 1.0, end: 2.0).animate( - CurvedAnimation(curve: Curves.ease, parent: _scaleAnimationController)); - _animation.addListener(() => _photoViewController.scale = _animation.value); - _initCurrentIndex(); - _itemPositionsListener.itemPositions.addListener(_readProgressListener); - super.initState(); - } - - _readProgressListener() { - var posIndex = _itemPositionsListener.itemPositions.value.first.index; - if (posIndex >= 0 && posIndex < widget.localArchive.images!.length) { - if (_currentIndex != posIndex) { - setState(() { - _currentIndex = posIndex; - }); - } - } - } - - _initCurrentIndex() async { - await Future.delayed(const Duration(milliseconds: 1)); - _selectedValue = ReaderMode.vertical; - _(_selectedValue!, true); - } - - void _onPageChanged(int index) { - setState(() { - _currentIndex = index; - }); - if (_imageDetailY != 0) { - _imageDetailY = 0; - _rebuildDetail.sink.add(_imageDetailY); - } - } - - void _onBtnTapped(int index, bool isPrev, {bool isSlide = false}) { - if (isPrev) { - if (_selectedValue == ReaderMode.verticalContinuous || - _selectedValue == ReaderMode.webtoon) { - if (index != -1) { - if (isSlide) { - _itemScrollController.jumpTo( - index: index, - ); - } else { - animatePageTransitions - ? _itemScrollController.scrollTo( - curve: Curves.ease, - index: index, - duration: Duration(milliseconds: isSlide ? 2 : 150)) - : _itemScrollController.jumpTo( - index: index, - ); - } - } - } else { - if (index != -1) { - if (_extendedController.hasClients) { - setState(() { - _isZoom = false; - }); - animatePageTransitions - ? _extendedController.animateToPage(index, - duration: Duration(milliseconds: isSlide ? 2 : 150), - curve: Curves.ease) - : _extendedController.jumpToPage(index); - } - } - } - } else { - if (_selectedValue == ReaderMode.verticalContinuous || - _selectedValue == ReaderMode.webtoon) { - if (widget.localArchive.images!.length != index) { - if (isSlide) { - _itemScrollController.jumpTo( - index: index, - ); - } else { - animatePageTransitions - ? _itemScrollController.scrollTo( - curve: Curves.ease, - index: index, - duration: Duration(milliseconds: isSlide ? 2 : 150)) - : _itemScrollController.jumpTo( - index: index, - ); - } - } - } else { - if (widget.localArchive.images!.length != index) { - if (_extendedController.hasClients) { - setState(() { - _isZoom = false; - }); - animatePageTransitions - ? _extendedController.animateToPage(index, - duration: Duration(milliseconds: isSlide ? 2 : 150), - curve: Curves.ease) - : _extendedController.jumpToPage(index); - } - } - } - } - } - - ReaderMode? _selectedValue; - bool _isView = false; - Alignment _scalePosition = Alignment.center; - final PhotoViewController _photoViewController = PhotoViewController(); - final PhotoViewScaleStateController _photoViewScaleStateController = - PhotoViewScaleStateController(); - - final ItemPositionsListener _itemPositionsListener = - ItemPositionsListener.create(); - void _onScaleEnd(BuildContext context, ScaleEndDetails details, - PhotoViewControllerValue controllerValue) { - if (controllerValue.scale! < 1) { - _photoViewScaleStateController.reset(); - } - } - - late final _extendedController = ExtendedPageController( - initialPage: _currentIndex, - shouldIgnorePointerWhenScrolling: false, - ); - - double get pixelRatio => View.of(context).devicePixelRatio; - - Size get size => View.of(context).physicalSize / pixelRatio; - Alignment _computeAlignmentByTapOffset(Offset offset) { - return Alignment((offset.dx - size.width / 2) / (size.width / 2), - (offset.dy - size.height / 2) / (size.height / 2)); - } - - void _toggleScale(Offset tapPosition) { - setState(() { - if (_scaleAnimationController.isAnimating) { - return; - } - - if (_photoViewController.scale == 1.0) { - _scalePosition = _computeAlignmentByTapOffset(tapPosition); - - if (_scaleAnimationController.isCompleted) { - _scaleAnimationController.reset(); - } - - _scaleAnimationController.forward(); - return; - } - - if (_photoViewController.scale == 2.0) { - _scaleAnimationController.reverse(); - return; - } - - _photoViewScaleStateController.reset(); - }); - } - - Axis _scrollDirection = Axis.vertical; - bool _isReversHorizontal = false; - - late bool _showPagesNumber = true; - _(ReaderMode value, bool isInit) async { - if (value == ReaderMode.vertical) { - if (mounted) { - setState(() { - _selectedValue = value; - _scrollDirection = Axis.vertical; - _isReversHorizontal = false; - }); - if (isInit) { - await Future.delayed(const Duration(milliseconds: 30)); - } - _extendedController.jumpToPage(_currentIndex); - } - } else if (value == ReaderMode.ltr || value == ReaderMode.rtl) { - if (mounted) { - setState(() { - if (value == ReaderMode.rtl) { - _isReversHorizontal = true; - } else { - _isReversHorizontal = false; - } - _selectedValue = value; - _scrollDirection = Axis.horizontal; - }); - if (isInit) { - await Future.delayed(const Duration(milliseconds: 30)); - } - _extendedController.jumpToPage(_currentIndex); - } - } else { - if (mounted) { - setState(() { - _selectedValue = value; - _isReversHorizontal = false; - }); - if (isInit) { - await Future.delayed(const Duration(milliseconds: 30)); - } - _itemScrollController.scrollTo( - index: _currentIndex, duration: const Duration(milliseconds: 1)); - } - } - } - - Color _backgroundColor(BuildContext context) => - Theme.of(context).scaffoldBackgroundColor.withOpacity(0.9); - - Widget _showMore() { - return Consumer( - builder: (context, ref, child) { - final currentIndex = _currentIndex; - - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - AnimatedContainer( - height: _isView ? 80 : 0, - curve: Curves.ease, - duration: const Duration(milliseconds: 200), - child: PreferredSize( - preferredSize: Size.fromHeight(_isView ? 80 : 0), - child: AppBar( - centerTitle: false, - automaticallyImplyLeading: false, - titleSpacing: 0, - leading: BackButton( - onPressed: () { - Navigator.pop(context); - }, - ), - title: ListTile( - dense: true, - title: SizedBox( - width: mediaWidth(context, 0.8), - child: Text( - '${widget.localArchive.name} ', - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - ), - ), - subtitle: SizedBox( - width: mediaWidth(context, 0.8), - child: const Text( - "", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ), - backgroundColor: _backgroundColor(context), - ), - ), - ), - AnimatedContainer( - curve: Curves.ease, - duration: const Duration(milliseconds: 300), - width: mediaWidth(context, 1), - height: _isView ? 130 : 0, - child: Column( - children: [ - Flexible( - child: Row( - children: [ - Expanded( - child: Transform.scale( - scaleX: !_isReversHorizontal ? 1 : -1, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Container( - height: 70, - decoration: BoxDecoration( - color: _backgroundColor(context), - borderRadius: BorderRadius.circular(25)), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 12), - child: Transform.scale( - scaleX: !_isReversHorizontal ? 1 : -1, - child: SizedBox( - width: 25, - child: Text( - "${currentIndex + 1} ", - style: const TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - Flexible( - child: Slider( - onChanged: (newValue) { - _onBtnTapped(newValue.toInt(), true, - isSlide: true); - }, - divisions: max( - widget.localArchive.images!.length - - 1, - 1), - value: min( - _currentIndex.toDouble(), - widget.localArchive.images!.length - .toDouble()), - min: 0, - max: (widget.localArchive.images! - .length - - 1) - .toDouble(), - ), - ), - Padding( - padding: const EdgeInsets.only(right: 12), - child: Transform.scale( - scaleX: !_isReversHorizontal ? 1 : -1, - child: SizedBox( - width: 25, - child: Text( - "${widget.localArchive.images!.length}", - style: const TextStyle( - fontSize: 15.0, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ), - ], - ), - ), - Flexible( - child: Container( - height: 65, - color: _backgroundColor(context), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - PopupMenuButton( - color: Colors.black, - child: const Icon( - Icons.app_settings_alt_outlined, - ), - onSelected: (value) { - if (mounted) { - setState(() { - _selectedValue = value; - }); - } - _(value, true); - }, - itemBuilder: (context) => [ - for (var readerMode in ReaderMode.values) - PopupMenuItem( - value: readerMode, - child: Row( - children: [ - Icon( - Icons.check, - color: _selectedValue == readerMode - ? Colors.white - : Colors.transparent, - ), - const SizedBox( - width: 7, - ), - Text( - getReaderModeName(readerMode), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - ), - ), - ], - )), - ], - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.screen_rotation, - ), - ), - IconButton( - onPressed: () { - _showModalSettings(); - }, - icon: const Icon( - Icons.settings_rounded, - ), - ), - ], - ), - ), - ), - ], - ), - ), - ], - ); - }, - ); - } - - Widget _showPage() { - return Consumer( - builder: (context, ref, child) { - final currentIndex = _currentIndex; - return _isView - ? Container() - : _showPagesNumber - ? Align( - alignment: Alignment.bottomCenter, - child: Text( - '${currentIndex + 1} / ${widget.localArchive.images!.length}', - style: const TextStyle( - color: Colors.white, - fontSize: 12.0, - shadows: [ - Shadow(offset: Offset(0.0, 0.0), blurRadius: 10.0) - ], - ), - textAlign: TextAlign.center, - ), - ) - : Container(); - }, - ); - } - - _isViewFunction() { - if (mounted) { - setState(() { - _isView = !_isView; - }); - } - if (_isView) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: SystemUiOverlay.values); - } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, - overlays: []); - } - } - - Widget _gestureRightLeft() { - return Consumer( - builder: (context, ref, child) { - return Row( - children: [ - /// left region - Expanded( - flex: 2, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - if (_isReversHorizontal) { - _onBtnTapped(_currentIndex + 1, false); - } else { - _onBtnTapped(_currentIndex - 1, true); - } - }, - onDoubleTapDown: _isVerticalContinous() - ? (TapDownDetails details) { - _toggleScale(details.globalPosition); - } - : null, - onDoubleTap: _isVerticalContinous() ? () {} : null, - ), - ), - - /// center region - Expanded( - flex: 2, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - _isViewFunction(); - }, - onDoubleTapDown: _isVerticalContinous() - ? (TapDownDetails details) { - _toggleScale(details.globalPosition); - } - : null, - onDoubleTap: _isVerticalContinous() ? () {} : null, - ), - ), - - /// right region - Expanded( - flex: 2, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - if (_isReversHorizontal) { - _onBtnTapped(_currentIndex - 1, true); - } else { - _onBtnTapped(_currentIndex + 1, false); - } - }, - onDoubleTapDown: _isVerticalContinous() - ? (TapDownDetails details) { - _toggleScale(details.globalPosition); - } - : null, - onDoubleTap: _isVerticalContinous() ? () {} : null, - ), - ), - ], - ); - }, - ); - } - - Widget _gestureTopBottom() { - return Consumer( - builder: (context, ref, child) { - return Column( - children: [ - /// top region - Expanded( - flex: 2, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - _onBtnTapped(_currentIndex - 1, true); - }, - onDoubleTapDown: _isVerticalContinous() - ? (TapDownDetails details) { - _toggleScale(details.globalPosition); - } - : null, - onDoubleTap: _isVerticalContinous() ? () {} : null, - ), - ), - - /// center region - Expanded(flex: 5, child: Container()), - - /// bottom region - Expanded( - flex: 2, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - _onBtnTapped(_currentIndex + 1, false); - }, - onDoubleTapDown: _isVerticalContinous() - ? (TapDownDetails details) { - _toggleScale(details.globalPosition); - } - : null, - onDoubleTap: _isVerticalContinous() ? () {} : null, - ), - ), - ], - ); - }, - ); - } - - bool _isZoom = false; - bool _isVerticalContinous() { - return _selectedValue == ReaderMode.verticalContinuous || - _selectedValue == ReaderMode.webtoon; - } - - final StreamController _rebuildDetail = - StreamController.broadcast(); - final Map detailKeys = {}; - late AnimationController _doubleClickAnimationController; - - Animation? _doubleClickAnimation; - late DoubleClickAnimationListener _doubleClickAnimationListener; - List doubleTapScales = [1.0, 2.0]; - GlobalKey slidePagekey = - GlobalKey(); - double _imageDetailY = 0; - @override - Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: SystemUiOverlay.values); - Navigator.pop(context); - - return false; - }, - child: Stack( - children: [ - _isVerticalContinous() - ? PhotoViewGallery.builder( - itemCount: 1, - builder: (_, __) => PhotoViewGalleryPageOptions.customChild( - controller: _photoViewController, - scaleStateController: _photoViewScaleStateController, - basePosition: _scalePosition, - onScaleEnd: _onScaleEnd, - child: ScrollablePositionedList.separated( - physics: const ClampingScrollPhysics(), - minCacheExtent: 8 * (MediaQuery.of(context).size.height), - initialScrollIndex: _currentIndex, - itemCount: widget.localArchive.images!.length, - itemScrollController: _itemScrollController, - itemPositionsListener: _itemPositionsListener, - itemBuilder: (context, index) => GestureDetector( - behavior: HitTestBehavior.translucent, - onDoubleTapDown: (TapDownDetails details) { - _toggleScale(details.globalPosition); - }, - onDoubleTap: () {}, - child: Image.memory( - widget.localArchive.images![index].image!)), - separatorBuilder: (_, __) => Divider( - color: Colors.black, - height: _selectedValue == ReaderMode.webtoon ? 0 : 6), - ), - ), - ) - : Material( - color: Colors.black, - shadowColor: Colors.black, - child: ExtendedImageGesturePageView.builder( - controller: _extendedController, - scrollDirection: _scrollDirection, - reverse: _isReversHorizontal, - physics: const ClampingScrollPhysics(), - preloadPagesCount: - _isZoom ? 0 : widget.localArchive.images!.length, - canScrollPage: (GestureDetails? gestureDetails) { - return gestureDetails != null - ? !(gestureDetails.totalScale! > 1.0) - : true; - }, - itemBuilder: (BuildContext context, int index) { - return ExtendedImage.memory( - widget.localArchive.images![index].image!, - clearMemoryCacheWhenDispose: true, - enableMemoryCache: false, - mode: ExtendedImageMode.gesture, - loadStateChanged: (ExtendedImageState state) { - if (state.extendedImageLoadState == - LoadState.loading) { - final ImageChunkEvent? loadingProgress = - state.loadingProgress; - final double progress = - loadingProgress?.expectedTotalBytes != null - ? loadingProgress!.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : 0; - return Container( - color: Colors.black, - height: mediaHeight(context, 0.8), - child: CircularProgressIndicatorAnimateRotate( - progress: progress), - ); - } - if (state.extendedImageLoadState == - LoadState.completed) { - return StreamBuilder( - builder: (BuildContext context, - AsyncSnapshot data) { - return ExtendedImageGesture( - state, - canScaleImage: (_) => _imageDetailY == 0, - imageBuilder: (Widget image) { - return Stack( - children: [ - Positioned.fill( - top: _imageDetailY, - bottom: -_imageDetailY, - child: image, - ), - ], - ); - }, - ); - }, - initialData: _imageDetailY, - stream: _rebuildDetail.stream, - ); - } - if (state.extendedImageLoadState == - LoadState.failed) { - return Container( - color: Colors.black, - height: mediaHeight(context, 0.8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () { - state.reLoadImage(); - }, - child: const Icon( - Icons.replay_outlined, - size: 30, - )), - ], - )); - } - return Container(); - }, - initGestureConfigHandler: (ExtendedImageState state) { - double? initialScale = 1.0; - final size = MediaQuery.of(context).size; - if (state.extendedImageInfo != null) { - initialScale = initScale( - size: size, - initialScale: initialScale, - imageSize: Size( - state.extendedImageInfo!.image.width - .toDouble(), - state.extendedImageInfo!.image.height - .toDouble())); - } - return GestureConfig( - inertialSpeed: 200, - inPageView: true, - initialScale: initialScale!, - maxScale: 8, - animationMaxScale: 8, - initialAlignment: InitialAlignment.center, - cacheGesture: true, - hitTestBehavior: HitTestBehavior.translucent, - ); - }, - onDoubleTap: (ExtendedImageGestureState state) { - final Offset? pointerDownPosition = - state.pointerDownPosition; - final double? begin = - state.gestureDetails!.totalScale; - double end; - - //remove old - _doubleClickAnimation - ?.removeListener(_doubleClickAnimationListener); - - //stop pre - _doubleClickAnimationController.stop(); - - //reset to use - _doubleClickAnimationController.reset(); - - if (begin == doubleTapScales[0]) { - setState(() { - _isZoom = true; - }); - end = doubleTapScales[1]; - } else { - setState(() { - _isZoom = false; - }); - end = doubleTapScales[0]; - } - - _doubleClickAnimationListener = () { - state.handleDoubleTap( - scale: _doubleClickAnimation!.value, - doubleTapPosition: pointerDownPosition); - }; - - _doubleClickAnimation = Tween( - begin: begin, end: end) - .animate(CurvedAnimation( - curve: Curves.ease, - parent: _doubleClickAnimationController)); - - _doubleClickAnimation! - .addListener(_doubleClickAnimationListener); - - _doubleClickAnimationController.forward(); - }, - ); - }, - itemCount: widget.localArchive.images!.length, - onPageChanged: _onPageChanged)), - _gestureRightLeft(), - _gestureTopBottom(), - _showMore(), - _showPage(), - ], - ), - ); - } - - _showModalSettings() { - DraggableMenu.open( - context, - DraggableMenu( - ui: ClassicDraggableMenu(barItem: Container()), - expandable: false, - maxHeight: mediaHeight(context, 0.4), - fastDrag: false, - minimizeBeforeFastDrag: false, - child: StatefulBuilder( - builder: (context, setState) { - return Scaffold( - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 10, - ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text( - 'Settings', - style: TextStyle( - fontSize: 17, fontWeight: FontWeight.bold), - ), - ), - const Divider(), - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - SwitchListTile( - dense: true, - title: const Text('Show Page Number'), - value: _showPagesNumber, - onChanged: (value) { - setState(() { - _showPagesNumber = value; - }); - }, - ), - ], - ), - ) - ], - ), - ); - }, - ))); - } -} diff --git a/lib/modules/main_view/main_screen.dart b/lib/modules/main_view/main_screen.dart index ce2810d2..d3e84c89 100644 --- a/lib/modules/main_view/main_screen.dart +++ b/lib/modules/main_view/main_screen.dart @@ -24,7 +24,7 @@ class _MainScreenState extends State { final route = GoRouter.of(context); int currentIndex = route.location == '/library' ? 0 - : route.location == '/archiveReader' + : route.location == '/updates' ? 1 : route.location == '/history' ? 2 @@ -79,7 +79,7 @@ class _MainScreenState extends State { width: isLongPressed ? 0 : route.location != '/library' && - route.location != '/archiveReader' && + route.location != '/updates' && route.location != '/history' && route.location != '/browse' && route.location != '/more' @@ -113,7 +113,7 @@ class _MainScreenState extends State { ), label: Padding( padding: EdgeInsets.only(top: 5), - child: Text('A-Reader'))), + child: Text('Updates'))), NavigationRailDestination( selectedIcon: Icon( Icons.history, @@ -158,7 +158,7 @@ class _MainScreenState extends State { if (newIndex == 0) { route.go('/library'); } else if (newIndex == 1) { - route.go('/archiveReader'); + route.go('/updates'); } else if (newIndex == 2) { route.go('/history'); } else if (newIndex == 3) { @@ -186,7 +186,7 @@ class _MainScreenState extends State { height: isLongPressed ? 0 : route.location != '/library' && - route.location != '/archiveReader' && + route.location != '/updates' && route.location != '/history' && route.location != '/browse' && route.location != '/more' @@ -212,12 +212,12 @@ class _MainScreenState extends State { label: 'Library'), NavigationDestination( selectedIcon: Icon( - Icons.library_books, + Icons.new_releases, ), icon: Icon( - Icons.library_books_outlined, + Icons.new_releases_outlined, ), - label: 'A-Reader'), + label: 'Updates'), NavigationDestination( selectedIcon: Icon( Icons.history, @@ -252,7 +252,7 @@ class _MainScreenState extends State { if (newIndex == 0) { route.go('/library'); } else if (newIndex == 1) { - route.go('/archiveReader'); + route.go('/updates'); } else if (newIndex == 2) { route.go('/history'); } else if (newIndex == 3) { diff --git a/lib/modules/manga/reader/chapter_interval_page_view.dart b/lib/modules/manga/reader/chapter_interval_page_view.dart new file mode 100644 index 00000000..13d917bb --- /dev/null +++ b/lib/modules/manga/reader/chapter_interval_page_view.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:mangayomi/modules/manga/reader/manga_reader_view.dart'; +import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart'; +import 'package:mangayomi/utils/media_query.dart'; + +class ChapterIntervalPageView extends ConsumerWidget { + final bool hasPrevChapter; + final bool hasNextChapter; + final UChapDataPreload uChapDataPreload; + + final VoidCallback onTap; + const ChapterIntervalPageView( + {super.key, + required this.uChapDataPreload, + required this.hasPrevChapter, + required this.hasNextChapter, + required this.onTap}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final readerController = + ReaderController(chapter: uChapDataPreload.chapter!); + + String text = uChapDataPreload.isPrevPrePage && hasPrevChapter + ? "Current:" + : "Finished:"; + final noMoreChapter = uChapDataPreload.isNextPrePage && !hasNextChapter || + uChapDataPreload.isPrevPrePage && !hasPrevChapter; + String noMore = + uChapDataPreload.isNextPrePage && !hasNextChapter ? "Next" : "Previous"; + return SizedBox( + height: mediaHeight(context, 0.27), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (uChapDataPreload.isPrevPrePage && hasPrevChapter) + Column( + children: [ + const Text( + "Previous:", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 12), + ), + Text(readerController.getPrevChapter().name!, + style: const TextStyle(color: Colors.white, fontSize: 12), + textAlign: TextAlign.center), + const SizedBox( + height: 10, + ), + Column( + children: [ + Text( + text, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 12), + ), + Wrap( + children: [ + Text( + uChapDataPreload.chapter!.name!, + style: const TextStyle( + color: Colors.white, fontSize: 12), + textAlign: TextAlign.center, + ), + const SizedBox( + width: 3, + ), + const Icon( + FontAwesomeIcons.circleCheck, + color: Colors.white, + size: 17, + ), + ], + ), + // ElevatedButton(onPressed: onTap, child: const Text("Retry")), + ], + ), + ], + ), + if (noMoreChapter) + Text( + "There is no $noMore chapter", + style: const TextStyle(color: Colors.white, fontSize: 14), + textAlign: TextAlign.center, + ), + if (uChapDataPreload.isNextPrePage && hasNextChapter) + Column( + children: [ + Column( + children: [ + Text( + text, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 12), + ), + Wrap( + alignment: WrapAlignment.center, + children: [ + Text( + uChapDataPreload.chapter!.name!, + style: const TextStyle( + color: Colors.white, fontSize: 12), + textAlign: TextAlign.center, + ), + const SizedBox( + width: 3, + ), + const Icon( + FontAwesomeIcons.circleCheck, + color: Colors.white, + size: 16, + ), + ], + ), + const SizedBox( + height: 10, + ), + const Text( + "Next:", + style: TextStyle( + fontWeight: FontWeight.bold, color: Colors.white), + ), + Text( + readerController.getNextChapter().name!, + style: const TextStyle(color: Colors.white, fontSize: 12), + textAlign: TextAlign.center, + ), + const SizedBox( + height: 30, + ), + // ElevatedButton( + // onPressed: onTap, + // child: const Text("Load Next chapter")), + ], + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/modules/manga/reader/image_view_vertical.dart b/lib/modules/manga/reader/image_view_vertical.dart index d00df977..6010b216 100644 --- a/lib/modules/manga/reader/image_view_vertical.dart +++ b/lib/modules/manga/reader/image_view_vertical.dart @@ -3,7 +3,6 @@ import 'dart:typed_data'; import 'package:extended_image/extended_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:mangayomi/utils/headers.dart'; import 'package:mangayomi/utils/media_query.dart'; import 'package:mangayomi/utils/reg_exp_matcher.dart'; @@ -46,17 +45,31 @@ class ImageViewVertical extends ConsumerWidget { ), isLocale ? archiveImage != null - ? ExtendedImage.memory( - archiveImage!, + ? ExtendedImage.memory(archiveImage!, fit: BoxFit.contain, - clearMemoryCacheWhenDispose: true, enableMemoryCache: false, - ) + loadStateChanged: (ExtendedImageState state) { + if (state.extendedImageLoadState == LoadState.loading) { + return Container( + color: Colors.black, + height: mediaHeight(context, 0.8), + ); + } + return null; + }) : ExtendedImage.file( fit: BoxFit.contain, - clearMemoryCacheWhenDispose: true, enableMemoryCache: false, - File('${path.path}${padIndex(index + 1)}.jpg')) + File('${path.path}${padIndex(index + 1)}.jpg'), + loadStateChanged: (ExtendedImageState state) { + if (state.extendedImageLoadState == LoadState.loading) { + return Container( + color: Colors.black, + height: mediaHeight(context, 0.8), + ); + } + return null; + }) : ExtendedImage.network(url, headers: ref.watch(headersProvider(source: source)), handleLoadingProgress: true, @@ -99,32 +112,6 @@ class ImageViewVertical extends ConsumerWidget { } return null; }), - if (index + 1 == length) - SizedBox( - height: mediaHeight(context, 0.3), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '$chapter finished', - style: const TextStyle( - fontSize: 17.0, - fontWeight: FontWeight.bold, - color: Colors.white), - ), - const SizedBox( - height: 10, - ), - const Icon( - FontAwesomeIcons.circleCheck, - color: Colors.white, - ), - const SizedBox( - height: 20, - ), - ], - ), - ) ], ), ); diff --git a/lib/modules/manga/reader/manga_reader_view.dart b/lib/modules/manga/reader/manga_reader_view.dart index 03b60765..aa1f4312 100644 --- a/lib/modules/manga/reader/manga_reader_view.dart +++ b/lib/modules/manga/reader/manga_reader_view.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:isolate'; import 'dart:math'; import 'dart:io'; import 'package:draggable_menu/draggable_menu.dart'; @@ -8,15 +7,11 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:isar/isar.dart'; import 'package:mangayomi/main.dart'; import 'package:mangayomi/models/chapter.dart'; -import 'package:mangayomi/models/history.dart'; -import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/models/settings.dart'; -import 'package:mangayomi/modules/manga/reader/providers/auto_crop_image_provider.dart'; +import 'package:mangayomi/modules/manga/reader/chapter_interval_page_view.dart'; import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart'; -import 'package:mangayomi/providers/storage_provider.dart'; import 'package:mangayomi/sources/utils/utils.dart'; import 'package:mangayomi/modules/manga/reader/providers/push_router.dart'; import 'package:mangayomi/services/get_chapter_url.dart'; @@ -30,7 +25,7 @@ import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart'; import 'package:mangayomi/modules/widgets/progress_center.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:scrollview_observer/scrollview_observer.dart'; typedef DoubleClickAnimationListener = void Function(); @@ -48,8 +43,7 @@ class MangaReaderView extends ConsumerWidget { final chapterData = ref.watch(getChapterUrlProvider( chapter: chapter, )); - final readerController = - ref.read(readerControllerProvider(chapter: chapter).notifier); + return chapterData.when( data: (data) { if (data.pageUrls.isEmpty && @@ -80,12 +74,8 @@ class MangaReaderView extends ConsumerWidget { ); } return MangaChapterPageGallery( - path: data.path!, - url: data.pageUrls, - readerController: readerController, - isLocaleList: data.isLocaleList, chapter: chapter, - archiveImages: data.archiveImages, + chapterUrlModel: data, ); }, error: (error, stackTrace) => Scaffold( @@ -141,20 +131,14 @@ class MangaReaderView extends ConsumerWidget { } class MangaChapterPageGallery extends ConsumerStatefulWidget { - const MangaChapterPageGallery( - {super.key, - required this.path, - required this.url, - required this.readerController, - required this.isLocaleList, - required this.chapter, - required this.archiveImages}); - final ReaderController readerController; - final Directory path; - final List url; - final List isLocaleList; + const MangaChapterPageGallery({ + super.key, + required this.chapter, + required this.chapterUrlModel, + }); + final GetChapterUrlModel chapterUrlModel; + final Chapter chapter; - final List archiveImages; @override ConsumerState createState() { @@ -165,12 +149,10 @@ class MangaChapterPageGallery extends ConsumerStatefulWidget { class _MangaChapterPageGalleryState extends ConsumerState with TickerProviderStateMixin { - late final ItemScrollController _itemScrollController = - ItemScrollController(); late AnimationController _scaleAnimationController; late Animation _animation; - late int _currentIndex = widget.readerController.getPageIndex(); - late bool _isBookmarked = widget.readerController.getChapterBookmarked(); + late ReaderController _readerController = ReaderController(chapter: chapter); + @override void dispose() { _rebuildDetail.close(); @@ -179,6 +161,11 @@ class _MangaChapterPageGalleryState super.dispose(); } + late GetChapterUrlModel _chapterUrlModel = widget.chapterUrlModel; + + late Chapter chapter = widget.chapter; + + final List _uChapDataPreload = []; bool animatePageTransitions = isar.settings.getSync(227)!.animatePageTransitions!; Duration? _doubleTapAnimationDuration() { @@ -192,8 +179,23 @@ class _MangaChapterPageGalleryState return const Duration(milliseconds: 200); } + late int _currentIndex = _readerController.getPageIndex(); + + T? ambiguate(T? value) => value; + + BuildContext? _listViewContext; + late ListObserverController _observerController; + final ScrollController _scrollController = ScrollController(); @override void initState() { + _observerController = ListObserverController( + controller: _scrollController, + ); + + ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((timeStamp) { + ListViewOnceObserveNotification().dispatch(_listViewContext); + }); + _doubleClickAnimationController = AnimationController( duration: _doubleTapAnimationDuration(), vsync: this); @@ -203,63 +205,129 @@ class _MangaChapterPageGalleryState CurvedAnimation(curve: Curves.ease, parent: _scaleAnimationController)); _animation.addListener(() => _photoViewController.scale = _animation.value); _initCurrentIndex(); - _itemPositionsListener.itemPositions.addListener(_readProgressListener); + super.initState(); } - _readProgressListener() { - var posIndex = _itemPositionsListener.itemPositions.value.first.index; - if (posIndex >= 0 && posIndex < widget.url.length) { - if (_currentIndex != posIndex) { - ref.read(currentIndexProvider(widget.chapter).notifier).setCurrentIndex( - posIndex, - ); - (int, int, String) datas = (widget.chapter.id!, posIndex, _dir!.path); - Isolate.spawn(_isarIsolateService, datas); - _currentIndex = posIndex; + _preloadNextChapter( + GetChapterUrlModel chapterData, + Chapter chapter, + ) { + bool isExist = false; + List preChap = []; + for (var ee in _uChapDataPreload) { + if (chapterData.uChapDataPreload.first.chapter!.name == + ee.chapter!.name) { + isExist = true; } } + if (!isExist) { + for (var aa in chapterData.uChapDataPreload) { + preChap.add(aa); + } + } + + if (preChap.isNotEmpty) { + preChap.add(UChapDataPreload( + chapter, null, null, null, null, null, true, false, null)); + _uChapDataPreload.addAll(preChap); + } + setState(() {}); } - Directory? _dir; + _preloadPrevChapter(GetChapterUrlModel chapterData, Chapter chapter) { + // bool isExist = false; + // List preChap = []; + // for (var ee in uChapDataPreload) { + // if (chapterData.uChapDataPreload.first.chapter!.name == ee.chapter!.name) { + // isExist = true; + // } + // } + // if (!isExist) { + // for (var aa in chapterData.uChapDataPreload) { + // preChap.add(aa); + // } + // } + // if (preChap.isNotEmpty) { + // preChap.add(UChapDataPreload(chapter, null, null, null, null, null, false, true)); + // uChapDataPreload.insertAll(0, preChap); + // _currentIndex = _currentIndex + preChap.length - 1; + // } + // print({"leng${preChap.length}"}); + // _currentIndex = chapterData.pageUrls.length - 1 + _currentIndex; + // print(_currentIndex); + // _chapterUrlModel = chapterData; + // _readerController = ReaderController(chapter: chapter); + // setState(() {}); + } + + late bool _isBookmarked = _readerController.getChapterBookmarked(); _initCurrentIndex() async { - _dir = await StorageProvider().getDatabaseDirectory(); - widget.readerController.setMangaHistoryUpdate(); + _uChapDataPreload.addAll(_chapterUrlModel.uChapDataPreload); + _uChapDataPreload.add(UChapDataPreload( + chapter, null, null, null, null, null, true, false, null)); + _readerController.setMangaHistoryUpdate(); await Future.delayed(const Duration(milliseconds: 1)); - _selectedValue = widget.readerController.getReaderMode(); - _(_selectedValue!, true); + _selectedValue = _readerController.getReaderMode(); + _setReaderMode(_selectedValue!, true); } void _onPageChanged(int index) { - ref.read(currentIndexProvider(widget.chapter).notifier).setCurrentIndex( - index, - ); - (int, int, String) datas = (widget.chapter.id!, index, _dir!.path); + if (!(_uChapDataPreload[index].isNextPrePage || + _uChapDataPreload[index].isPrevPrePage)) { + _readerController = + ReaderController(chapter: _uChapDataPreload[index].chapter!); + _chapterUrlModel = _uChapDataPreload[_posIndex ?? 0].chapterUrlModel!; + _readerController.setMangaHistoryUpdate(); + _readerController.setPageIndex(_currentIndex + 1); + _readerController.setChapterPageLastRead(_currentIndex + 1); + _isBookmarked = _readerController.getChapterBookmarked(); + _posIndex = index; + _currentIndex = _uChapDataPreload[index].index!; - Isolate.spawn(_isarIsolateService, datas); - _currentIndex = index; - if (_imageDetailY != 0) { - _imageDetailY = 0; - _rebuildDetail.sink.add(_imageDetailY); + ref.read(currentIndexProvider(chapter).notifier).setCurrentIndex( + _currentIndex, + ); + + setState(() {}); } } - void _onBtnTapped(int index, bool isPrev, {bool isSlide = false}) { + final double _imageDetailY = 0; + + void _onBtnTapped(int index, bool isPrev, + {bool isSlide = false, int? slideAddValueIndex}) { + if (_uChapDataPreload[_posIndex ?? 0].chapter!.name != + _uChapDataPreload.first.chapter!.name) { + if (_uChapDataPreload[_posIndex ?? 0].index != null) { + int plu = 0; + if (isPrev && isSlide && slideAddValueIndex != null) { + plu = slideAddValueIndex; + } else if (isPrev) { + plu = -1; + } else { + plu = 1; + } + index = _uChapDataPreload.indexWhere( + (element) => element == _uChapDataPreload[_posIndex ?? 0]) + + plu; + } + } if (isPrev) { if (_selectedValue == ReaderMode.verticalContinuous || _selectedValue == ReaderMode.webtoon) { if (index != -1) { if (isSlide) { - _itemScrollController.jumpTo( + _observerController.jumpTo( index: index, ); } else { animatePageTransitions - ? _itemScrollController.scrollTo( + ? _observerController.animateTo( curve: Curves.ease, index: index, duration: Duration(milliseconds: isSlide ? 2 : 150)) - : _itemScrollController.jumpTo( + : _observerController.jumpTo( index: index, ); } @@ -285,61 +353,57 @@ class _MangaChapterPageGalleryState } else { if (_selectedValue == ReaderMode.verticalContinuous || _selectedValue == ReaderMode.webtoon) { - if (widget.readerController.getPageLength(widget.url) != index) { + if (isSlide) { + _observerController.jumpTo( + index: index, + ); + } else { + animatePageTransitions + ? _observerController.animateTo( + curve: Curves.ease, + index: index, + duration: Duration(milliseconds: isSlide ? 2 : 150)) + : _observerController.jumpTo( + index: index, + ); + } + } else { + if (_extendedController.hasClients) { + setState(() { + _isZoom = false; + }); if (isSlide) { - _itemScrollController.jumpTo( + _observerController.jumpTo( index: index, ); } else { animatePageTransitions - ? _itemScrollController.scrollTo( - curve: Curves.ease, - index: index, - duration: Duration(milliseconds: isSlide ? 2 : 150)) - : _itemScrollController.jumpTo( - index: index, - ); - } - } - } else { - if (widget.readerController.getPageLength(widget.url) != index) { - if (_extendedController.hasClients) { - setState(() { - _isZoom = false; - }); - if (isSlide) { - _itemScrollController.jumpTo( - index: index, - ); - } else { - animatePageTransitions - ? _extendedController.animateToPage(index, - duration: Duration(milliseconds: isSlide ? 2 : 150), - curve: Curves.ease) - : _extendedController.jumpToPage(index); - } + ? _extendedController.animateToPage(index, + duration: Duration(milliseconds: isSlide ? 2 : 150), + curve: Curves.ease) + : _extendedController.jumpToPage(index); } } } } } - List _cropImagesList = []; + final List _cropImagesList = []; bool isOk = false; - _cropImage() async { - if (!isOk) { - isOk = true; - _cropImagesList = await ref.watch(autoCropBorderProvider( - archiveImages: widget.archiveImages, - isLocaleList: widget.isLocaleList, - path: widget.path, - url: widget.url) - .future); - if (mounted) { - setState(() {}); - } - } - } + // _cropImage() async { + // if (!isOk) { + // isOk = true; + // _cropImagesList = await ref.watch(autoCropBorderProvider( + // archiveImages: _chapterUrlModel.archiveImages, + // isLocaleList: _chapterUrlModel.isLocaleList, + // path: _chapterUrlModel.path!, + // url: _chapterUrlModel.pageUrls) + // .future); + // if (mounted) { + // setState(() {}); + // } + // } + // } ReaderMode? _selectedValue; bool _isView = false; @@ -348,8 +412,6 @@ class _MangaChapterPageGalleryState final PhotoViewScaleStateController _photoViewScaleStateController = PhotoViewScaleStateController(); - final ItemPositionsListener _itemPositionsListener = - ItemPositionsListener.create(); void _onScaleEnd(BuildContext context, ScaleEndDetails details, PhotoViewControllerValue controllerValue) { if (controllerValue.scale! < 1) { @@ -399,10 +461,10 @@ class _MangaChapterPageGalleryState Axis _scrollDirection = Axis.vertical; bool _isReversHorizontal = false; - late bool _showPagesNumber = widget.readerController.getShowPageNumber(); - _(ReaderMode value, bool isInit) async { - widget.readerController.setReaderMode(value); - + late bool _showPagesNumber = _readerController.getShowPageNumber(); + _setReaderMode(ReaderMode value, bool isInit) async { + final indexPos = isInit ? _posIndex ?? _currentIndex : _posIndex; + _readerController.setReaderMode(value); if (value == ReaderMode.vertical) { if (mounted) { setState(() { @@ -410,10 +472,9 @@ class _MangaChapterPageGalleryState _scrollDirection = Axis.vertical; _isReversHorizontal = false; }); - if (isInit) { - await Future.delayed(const Duration(milliseconds: 30)); - } - _extendedController.jumpToPage(_currentIndex); + await Future.delayed(const Duration(milliseconds: 30)); + + _extendedController.jumpToPage(indexPos!); } } else if (value == ReaderMode.ltr || value == ReaderMode.rtl) { if (mounted) { @@ -426,10 +487,9 @@ class _MangaChapterPageGalleryState _selectedValue = value; _scrollDirection = Axis.horizontal; }); - if (isInit) { - await Future.delayed(const Duration(milliseconds: 30)); - } - _extendedController.jumpToPage(_currentIndex); + await Future.delayed(const Duration(milliseconds: 30)); + + _extendedController.jumpToPage(indexPos!); } } else { if (mounted) { @@ -437,11 +497,11 @@ class _MangaChapterPageGalleryState _selectedValue = value; _isReversHorizontal = false; }); - if (isInit) { - await Future.delayed(const Duration(milliseconds: 30)); - } - _itemScrollController.scrollTo( - index: _currentIndex, duration: const Duration(milliseconds: 1)); + await Future.delayed(const Duration(milliseconds: 30)); + _observerController.animateTo( + index: indexPos!, + duration: const Duration(milliseconds: 1), + curve: Curves.ease); } } } @@ -450,9 +510,9 @@ class _MangaChapterPageGalleryState Theme.of(context).scaffoldBackgroundColor.withOpacity(0.9); Widget _showMore() { - bool isNotFirstChapter = widget.readerController.getChapterIndex() + 1 != - widget.readerController.getChaptersLength(); - bool isNotLastChapter = widget.readerController.getChapterIndex() != 0; + bool hasPrevChapter = _readerController.getChapterIndex() + 1 != + _readerController.getChaptersLength(); + bool hasNextChapter = _readerController.getChapterIndex() != 0; return Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -476,7 +536,7 @@ class _MangaChapterPageGalleryState title: SizedBox( width: mediaWidth(context, 0.8), child: Text( - '${widget.readerController.getMangaName()} ', + '${_readerController.getMangaName()} ', style: const TextStyle(fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), @@ -484,7 +544,7 @@ class _MangaChapterPageGalleryState subtitle: SizedBox( width: mediaWidth(context, 0.8), child: Text( - widget.readerController.getChapterTitle(), + _readerController.getChapterTitle(), style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w400, @@ -496,7 +556,7 @@ class _MangaChapterPageGalleryState actions: [ IconButton( onPressed: () { - widget.readerController.setChapterBookmarked(); + _readerController.setChapterBookmarked(); setState(() { _isBookmarked = !_isBookmarked; }); @@ -504,18 +564,17 @@ class _MangaChapterPageGalleryState icon: Icon(_isBookmarked ? Icons.bookmark : Icons.bookmark_border_outlined)), - if ((widget.chapter.manga.value!.isLocalArchive ?? false) == - false) + if ((chapter.manga.value!.isLocalArchive ?? false) == false) IconButton( onPressed: () { - final manga = widget.chapter.manga.value!; + final manga = chapter.manga.value!; String url = getMangaAPIUrl(manga.source!).isEmpty ? manga.link! : "${getMangaBaseUrl(manga.source!)}${manga.link!}"; Map data = { 'url': url, 'source': manga.source!, - 'title': widget.chapter.name! + 'title': chapter.name! }; context.push("/mangawebview", extra: data); }, @@ -541,18 +600,18 @@ class _MangaChapterPageGalleryState radius: 23, backgroundColor: _backgroundColor(context), child: IconButton( - onPressed: isNotFirstChapter + onPressed: hasPrevChapter ? () { pushReplacementMangaReaderView( context: context, - chapter: widget.readerController - .getNextChapter()); + chapter: + _readerController.getPrevChapter()); } : null, icon: Transform.scale( scaleX: 1, child: Icon(Icons.skip_previous_rounded, - color: isNotFirstChapter + color: hasPrevChapter ? Theme.of(context) .textTheme .bodyLarge! @@ -586,8 +645,7 @@ class _MangaChapterPageGalleryState child: Consumer( builder: (context, ref, child) { final currentIndex = ref.watch( - currentIndexProvider( - widget.chapter)); + currentIndexProvider(chapter)); return Text( "${currentIndex + 1} ", style: const TextStyle( @@ -602,26 +660,39 @@ class _MangaChapterPageGalleryState Flexible( child: Consumer(builder: (context, ref, child) { - final currentIndex = ref.watch( - currentIndexProvider(widget.chapter)); + final currentIndex = ref + .watch(currentIndexProvider(chapter)); return Slider( onChanged: (newValue) { - _onBtnTapped(newValue.toInt(), true, - isSlide: true); + if (_readerController.getPageLength( + _chapterUrlModel.pageUrls) != + _currentIndex + 1) { + int slideAddValueIndex = 0; + if (newValue < _currentIndex) { + slideAddValueIndex = -1; + } else { + slideAddValueIndex = 1; + } + _onBtnTapped(newValue.toInt(), true, + isSlide: true, + slideAddValueIndex: + slideAddValueIndex); + } }, divisions: max( - widget.readerController - .getPageLength(widget.url) - + _readerController.getPageLength( + _chapterUrlModel.pageUrls) - 1, 1), value: min( currentIndex.toDouble(), - widget.readerController - .getPageLength(widget.url) + _readerController + .getPageLength( + _chapterUrlModel.pageUrls) .toDouble()), min: 0, - max: (widget.readerController - .getPageLength(widget.url) - + max: (_readerController.getPageLength( + _chapterUrlModel.pageUrls) - 1) .toDouble(), ); @@ -634,7 +705,7 @@ class _MangaChapterPageGalleryState child: SizedBox( width: 30, child: Text( - "${widget.readerController.getPageLength(widget.url)}", + "${_readerController.getPageLength(_chapterUrlModel.pageUrls)}", style: const TextStyle( fontSize: 15.0, fontWeight: FontWeight.bold, @@ -655,12 +726,11 @@ class _MangaChapterPageGalleryState radius: 23, backgroundColor: _backgroundColor(context), child: IconButton( - onPressed: isNotLastChapter + onPressed: hasNextChapter ? () { pushReplacementMangaReaderView( context: context, - chapter: widget.readerController - .getPrevChapter(), + chapter: _readerController.getNextChapter(), ); } : null, @@ -668,7 +738,7 @@ class _MangaChapterPageGalleryState scaleX: 1, child: Icon( Icons.skip_next_rounded, - color: isNotLastChapter + color: hasNextChapter ? Theme.of(context).textTheme.bodyLarge!.color : Theme.of(context) .textTheme @@ -702,7 +772,7 @@ class _MangaChapterPageGalleryState _selectedValue = value; }); } - _(value, true); + _setReaderMode(value, false); }, itemBuilder: (context) => [ for (var readerMode in ReaderMode.values) @@ -791,10 +861,9 @@ class _MangaChapterPageGalleryState ? Align( alignment: Alignment.bottomCenter, child: Consumer(builder: (context, ref, child) { - final currentIndex = - ref.watch(currentIndexProvider(widget.chapter)); + final currentIndex = ref.watch(currentIndexProvider(chapter)); return Text( - '${currentIndex + 1} / ${widget.readerController.getPageLength(widget.url)}', + '${currentIndex + 1} / ${_readerController.getPageLength(_chapterUrlModel.pageUrls)}', style: const TextStyle( color: Colors.white, fontSize: 12.0, @@ -946,6 +1015,7 @@ class _MangaChapterPageGalleryState _selectedValue == ReaderMode.webtoon; } + int? _posIndex; final StreamController _rebuildDetail = StreamController.broadcast(); final Map detailKeys = {}; @@ -956,7 +1026,6 @@ class _MangaChapterPageGalleryState List doubleTapScales = [1.0, 2.0]; GlobalKey slidePagekey = GlobalKey(); - double _imageDetailY = 0; @override Widget build(BuildContext context) { final cropBorders = ref.watch(cropBordersStateProvider); @@ -979,48 +1048,148 @@ class _MangaChapterPageGalleryState scaleStateController: _photoViewScaleStateController, basePosition: _scalePosition, onScaleEnd: _onScaleEnd, - child: ScrollablePositionedList.separated( - physics: const ClampingScrollPhysics(), - minCacheExtent: 8 * (MediaQuery.of(context).size.height), - initialScrollIndex: _currentIndex, - itemCount: - widget.readerController.getPageLength(widget.url), - itemScrollController: _itemScrollController, - itemPositionsListener: _itemPositionsListener, - itemBuilder: (context, index) => GestureDetector( - behavior: HitTestBehavior.translucent, - onDoubleTapDown: (TapDownDetails details) { - _toggleScale(details.globalPosition); - }, - onDoubleTap: () {}, - child: ImageViewVertical( - archiveImage: - _cropImagesList.isNotEmpty && cropBorders == true + child: ListViewObserver( + controller: _observerController, + sliverListContexts: () { + return [ + if (_listViewContext != null) _listViewContext! + ]; + }, + onObserveAll: (resultMap) { + final model = resultMap[_listViewContext]; + if (model == null) return; + _posIndex = model.firstChild?.index ?? 0; + if (!(_uChapDataPreload[_posIndex ?? 0].isNextPrePage || + _uChapDataPreload[_posIndex ?? 0].isPrevPrePage)) { + _readerController = ReaderController( + chapter: + _uChapDataPreload[_posIndex ?? 0].chapter!); + + _chapterUrlModel = _uChapDataPreload[_posIndex ?? 0] + .chapterUrlModel!; + + _currentIndex = + _uChapDataPreload[_posIndex ?? 0].index!; + + ref + .read(currentIndexProvider(chapter).notifier) + .setCurrentIndex( + _currentIndex, + ); + _isBookmarked = + _readerController.getChapterBookmarked(); + _readerController.setMangaHistoryUpdate(); + _readerController.setPageIndex(_currentIndex); + _readerController + .setChapterPageLastRead(_currentIndex); + setState(() {}); + } + }, + child: ListView.separated( + cacheExtent: 5 * mediaHeight(context, 1), + itemCount: _uChapDataPreload.length, + controller: _scrollController, + itemBuilder: (context, index) { + if (_listViewContext != context) { + _listViewContext = context; + } + _scrollController.addListener(() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + if (_uChapDataPreload[index].isNextPrePage || + _uChapDataPreload[index].isPrevPrePage) { + bool hasPrevChapter = + _readerController.getChapterIndex() + 1 != + _readerController.getChaptersLength(); + bool hasNextChapter = + _readerController.getChapterIndex() != 0; + final chapter = _uChapDataPreload[index] + .isNextPrePage && + hasNextChapter + ? _readerController.getNextChapter() + : _uChapDataPreload[index].isPrevPrePage && + hasPrevChapter + ? _readerController.getPrevChapter() + : null; + if (chapter != null) { + ref + .watch(getChapterUrlProvider( + chapter: chapter, + ).future) + .then((value) { + if (_uChapDataPreload[index] + .isNextPrePage) { + _preloadNextChapter(value, chapter); + } else { + _preloadPrevChapter(value, chapter); + } + }); + } + } + } + }); + if (_uChapDataPreload[index].isNextPrePage || + _uChapDataPreload[index].isPrevPrePage) { + bool hasPrevChapter = + _readerController.getChapterIndex() + 1 != + _readerController.getChaptersLength(); + bool hasNextChapter = + _readerController.getChapterIndex() != 0; + final chapter = + _uChapDataPreload[index].isNextPrePage && + hasNextChapter + ? _readerController.getNextChapter() + : _uChapDataPreload[index].isPrevPrePage && + hasPrevChapter + ? _readerController.getPrevChapter() + : null; + if (chapter == null) { + return ChapterIntervalPageView( + uChapDataPreload: _uChapDataPreload[index], + onTap: () async {}, + hasNextChapter: hasNextChapter, + hasPrevChapter: hasPrevChapter, + ); + } + + return ChapterIntervalPageView( + uChapDataPreload: _uChapDataPreload[index], + onTap: () async {}, + hasNextChapter: hasNextChapter, + hasPrevChapter: hasPrevChapter, + ); + } + return GestureDetector( + behavior: HitTestBehavior.translucent, + onDoubleTapDown: (TapDownDetails details) { + _toggleScale(details.globalPosition); + }, + onDoubleTap: () {}, + child: ImageViewVertical( + archiveImage: _cropImagesList.isNotEmpty && + cropBorders == true ? _cropImagesList[index] - : widget.archiveImages.isNotEmpty - ? widget.archiveImages[index] - : null, - titleManga: widget.readerController.getMangaName(), - source: widget.readerController - .getSourceName() - .replaceAll( - '${widget.readerController.getManga().lang}-', - ''), - index: index, - url: widget.url[index], - path: widget.path, - chapter: widget.readerController.getChapterTitle(), - length: - widget.readerController.getPageLength(widget.url), - isLocale: - _cropImagesList.isNotEmpty && cropBorders == true + : _uChapDataPreload[index].archiveImage, + titleManga: _readerController.getMangaName(), + source: _readerController.getSourceName(), + index: _uChapDataPreload[index].index!, + url: _uChapDataPreload[index].url!, + path: _uChapDataPreload[index].path!, + chapter: _readerController.getChapterTitle(), + length: _readerController + .getPageLength(_chapterUrlModel.pageUrls), + isLocale: _cropImagesList.isNotEmpty && + cropBorders == true ? true - : widget.isLocaleList[index], - ), + : _uChapDataPreload[index].isLocale!, + ), + ); + }, + separatorBuilder: (_, __) => Divider( + color: Colors.black, + height: + _selectedValue == ReaderMode.webtoon ? 0 : 6), ), - separatorBuilder: (_, __) => Divider( - color: Colors.black, - height: _selectedValue == ReaderMode.webtoon ? 0 : 6), ), ), ) @@ -1039,25 +1208,50 @@ class _MangaChapterPageGalleryState : true; }, itemBuilder: (BuildContext context, int index) { + if (_uChapDataPreload[index].isNextPrePage || + _uChapDataPreload[index].isPrevPrePage) { + bool hasPrevChapter = + _readerController.getChapterIndex() + 1 != + _readerController.getChaptersLength(); + bool hasNextChapter = + _readerController.getChapterIndex() != 0; + final chapter = + _uChapDataPreload[index].isNextPrePage && + hasNextChapter + ? _readerController.getNextChapter() + : _uChapDataPreload[index].isPrevPrePage && + hasPrevChapter + ? _readerController.getPrevChapter() + : null; + if (chapter == null) { + return ChapterIntervalPageView( + uChapDataPreload: _uChapDataPreload[index], + onTap: () async {}, + hasNextChapter: hasNextChapter, + hasPrevChapter: hasPrevChapter, + ); + } + + return ChapterIntervalPageView( + uChapDataPreload: _uChapDataPreload[index], + onTap: () async {}, + hasNextChapter: hasNextChapter, + hasPrevChapter: hasPrevChapter, + ); + } return ImageViewCenter( archiveImage: _cropImagesList.isNotEmpty && cropBorders == true ? _cropImagesList[index] - : widget.archiveImages.isNotEmpty - ? widget.archiveImages[index] - : null, - titleManga: widget.readerController.getMangaName(), - source: widget.readerController - .getSourceName() - .replaceAll( - '${widget.readerController.getManga().lang}-', - ''), - index: index, - url: widget.url[index], - path: widget.path, - chapter: widget.readerController.getChapterTitle(), - length: - widget.readerController.getPageLength(widget.url), + : _uChapDataPreload[index].archiveImage, + titleManga: _readerController.getMangaName(), + source: _readerController.getSourceName(), + index: _uChapDataPreload[index].index!, + url: _uChapDataPreload[index].url!, + path: _uChapDataPreload[index].path!, + chapter: _readerController.getChapterTitle(), + length: _readerController + .getPageLength(_chapterUrlModel.pageUrls), loadStateChanged: (ExtendedImageState state) { if (state.extendedImageLoadState == LoadState.loading) { @@ -1191,16 +1385,13 @@ class _MangaChapterPageGalleryState _doubleClickAnimationController.forward(); }, - isLocale: _cropImagesList.isNotEmpty && - cropBorders == true - ? true - : _isReversHorizontal - ? widget.isLocaleList.reversed.toList()[index] - : widget.isLocaleList[index], + isLocale: + _cropImagesList.isNotEmpty && cropBorders == true + ? true + : _uChapDataPreload[index].isLocale!, ); }, - itemCount: - widget.readerController.getPageLength(widget.url), + itemCount: _uChapDataPreload.length, onPageChanged: _onPageChanged)), _gestureRightLeft(), _gestureTopBottom(), @@ -1250,8 +1441,7 @@ class _MangaChapterPageGalleryState setState(() { _showPagesNumber = value; }); - widget.readerController - .setShowPageNumber(value); + _readerController.setShowPageNumber(value); }, ), ], @@ -1265,74 +1455,25 @@ class _MangaChapterPageGalleryState } } -_isarIsolateService((int, int, String) data) async { - late Isar isarIsolate; - isarIsolate = await StorageProvider().initDB(data.$3, inspector: false); - Chapter? chapter = isarIsolate.chapters.getSync(data.$1); - Manga? manga = chapter!.manga.value!; - Settings? isarIsolateSettings = isarIsolate.settings.getSync(227)!; - bool incognitoMode = isarIsolate.settings.getSync(227)!.incognitoMode!; - int pageIndex = data.$2; - - //setMangaHistoryUpdate - if (!incognitoMode) { - isarIsolate.writeTxnSync(() { - Manga? manga = chapter.manga.value; - manga!.lastRead = DateTime.now().millisecondsSinceEpoch; - isarIsolate.mangas.putSync(manga); - }); - History? history; - - final empty = - isarIsolate.historys.filter().mangaIdEqualTo(manga.id).isEmptySync(); - - if (empty) { - history = History( - mangaId: manga.id, - date: DateTime.now().millisecondsSinceEpoch.toString()) - ..chapter.value = chapter; - } else { - history = (isarIsolate.historys - .filter() - .mangaIdEqualTo(manga.id) - .findFirstSync())! - ..chapter.value = chapter - ..date = DateTime.now().millisecondsSinceEpoch.toString(); - } - isarIsolate.writeTxnSync(() { - isarIsolate.historys.putSync(history!); - history.chapter.saveSync(); - }); - } - - //setPageIndex - if (!incognitoMode) { - List? chapterPageIndexs = []; - for (var chapterPageIndex - in isarIsolateSettings.chapterPageIndexList ?? []) { - if (chapterPageIndex.chapterId != chapter.id) { - chapterPageIndexs.add(chapterPageIndex); - } - } - chapterPageIndexs.add(ChapterPageIndex() - ..chapterId = chapter.id - ..index = pageIndex); - isarIsolate.writeTxnSync(() => isarIsolate.settings.putSync( - isarIsolateSettings..chapterPageIndexList = chapterPageIndexs)); - } - //setChapterPageLastRead - if (!incognitoMode) { - final chap = chapter; - isarIsolate.writeTxnSync(() { - chap.isRead = (pageIndex + 1) == - isarIsolateSettings.chapterPageUrlsList! - .where((element) => element.chapterId == chapter.id) - .first - .urls! - .length; - - chap.lastPageRead = (pageIndex + 1).toString(); - isarIsolate.chapters.putSync(chap); - }); - } +class UChapDataPreload { + final Chapter? chapter; + final Directory? path; + final String? url; + final bool? isLocale; + final Uint8List? archiveImage; + final int? index; + final bool isNextPrePage; + final bool isPrevPrePage; + final GetChapterUrlModel? chapterUrlModel; + UChapDataPreload( + this.chapter, + this.path, + this.url, + this.isLocale, + this.archiveImage, + this.index, + this.isNextPrePage, + this.isPrevPrePage, + this.chapterUrlModel, + ); } diff --git a/lib/modules/manga/reader/providers/reader_controller_provider.dart b/lib/modules/manga/reader/providers/reader_controller_provider.dart index 9ebd1dc5..8837184d 100644 --- a/lib/modules/manga/reader/providers/reader_controller_provider.dart +++ b/lib/modules/manga/reader/providers/reader_controller_provider.dart @@ -14,9 +14,7 @@ class CurrentIndex extends _$CurrentIndex { int build(Chapter chapter) { final incognitoMode = ref.watch(incognitoModeStateProvider); if (!incognitoMode) { - return ref - .read(readerControllerProvider(chapter: chapter).notifier) - .getPageIndex(); + return ReaderController(chapter: chapter).getPageIndex(); } return 0; } @@ -28,235 +26,9 @@ class CurrentIndex extends _$CurrentIndex { } } -@riverpod -class ReaderController extends _$ReaderController { - @override - void build({required Chapter chapter}) {} - - Manga getManga() { - return chapter.manga.value!; - } - - Chapter geChapter() { - return chapter; - } - - ReaderMode getReaderMode() { - final personalReaderModeList = - getIsarSetting().personalReaderModeList ?? []; - final personalReaderMode = personalReaderModeList - .where((element) => element.mangaId == getManga().id); - if (personalReaderMode.isNotEmpty) { - return personalReaderMode.first.readerMode; - } - return isar.settings.getSync(227)!.defaultReaderMode; - } - - void setReaderMode(ReaderMode newReaderMode) { - List? personalReaderModeLists = []; - for (var personalReaderMode - in getIsarSetting().personalReaderModeList ?? []) { - if (personalReaderMode.mangaId != getManga().id) { - personalReaderModeLists.add(personalReaderMode); - } - } - personalReaderModeLists.add(PersonalReaderMode() - ..mangaId = getManga().id - ..readerMode = newReaderMode); - isar.writeTxnSync(() => isar.settings.putSync( - getIsarSetting()..personalReaderModeList = personalReaderModeLists)); - } - - void setShowPageNumber(bool value) { - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - isar.writeTxnSync(() => - isar.settings.putSync(getIsarSetting()..showPagesNumber = value)); - } - } - - Settings getIsarSetting() { - return isar.settings.getSync(227)!; - } - - bool getShowPageNumber() { - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - return getIsarSetting().showPagesNumber!; - } - return true; - } - - void setMangaHistoryUpdate() { - // log("message"); - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - isar.writeTxnSync(() { - Manga? manga = chapter.manga.value; - manga!.lastRead = DateTime.now().millisecondsSinceEpoch; - isar.mangas.putSync(manga); - }); - History? history; - - final empty = - isar.historys.filter().mangaIdEqualTo(getManga().id).isEmptySync(); - - if (empty) { - history = History( - mangaId: getManga().id, - date: DateTime.now().millisecondsSinceEpoch.toString()) - ..chapter.value = chapter; - } else { - history = (isar.historys - .filter() - .mangaIdEqualTo(getManga().id) - .findFirstSync())! - ..chapter.value = chapter - ..date = DateTime.now().millisecondsSinceEpoch.toString(); - } - isar.writeTxnSync(() { - isar.historys.putSync(history!); - history.chapter.saveSync(); - }); - } - } - - void setChapterPageLastRead(int pageIndex) { - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - final chap = chapter; - isar.writeTxnSync(() { - chap.isRead = (pageIndex + 1) == getPageLength([]); - chap.lastPageRead = (pageIndex + 1).toString(); - isar.chapters.putSync(chap); - }); - } - } - - void setChapterBookmarked() { - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - final isBookmarked = getChapterBookmarked(); - final chap = chapter; - isar.writeTxnSync(() { - chap.isBookmarked = !isBookmarked; - isar.chapters.putSync(chap); - }); - } - } - - bool getChapterBookmarked() { - return isar.chapters.getSync(chapter.id!)!.isBookmarked!; - } - - int getNextChapterIndex() { - final chapters = getManga().chapters.toList(); - int? index; - for (var i = 0; i < chapters.length; i++) { - if (chapters[i].id == chapter.id) { - index = i + 1; - } - } - return index!; - } - - int getPrevChapterIndex() { - final chapters = getManga().chapters.toList(); - int? index; - for (var i = 0; i < chapters.length; i++) { - if (chapters[i].id == chapter.id) { - index = i - 1; - } - } - return index!; - } - - int getChapterIndex() { - final chapters = getManga().chapters.toList(); - int? index; - for (var i = 0; i < chapters.length; i++) { - if (chapters[i].id == chapter.id) { - index = i; - } - } - return index!; - } - - Chapter getNextChapter() { - return getManga().chapters.toList()[getNextChapterIndex()]; - } - - Chapter getPrevChapter() { - return getManga().chapters.toList()[getPrevChapterIndex()]; - } - - int getChaptersLength() { - return getManga().chapters.length; - } - - int getPageIndex() { - final incognitoMode = ref.watch(incognitoModeStateProvider); - final chapterPageIndexList = getIsarSetting().chapterPageIndexList ?? []; - final index = chapterPageIndexList - .where((element) => element.chapterId == chapter.id); - if (!incognitoMode) { - return chapter.isRead! - ? 0 - : index.isNotEmpty - ? index.first.index! - : 0; - } - return 0; - } - - int getPageLength(List incognitoPageLength) { - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - return getIsarSetting() - .chapterPageUrlsList! - .where((element) => element.chapterId == chapter.id) - .first - .urls! - .length; - } - return incognitoPageLength.length; - } - - void setPageIndex(int newIndex) { - final incognitoMode = ref.watch(incognitoModeStateProvider); - if (!incognitoMode) { - List? chapterPageIndexs = []; - for (var chapterPageIndex - in getIsarSetting().chapterPageIndexList ?? []) { - if (chapterPageIndex.chapterId != chapter.id) { - chapterPageIndexs.add(chapterPageIndex); - } - } - chapterPageIndexs.add(ChapterPageIndex() - ..chapterId = chapter.id - ..index = newIndex); - isar.writeTxnSync(() => isar.settings - .putSync(getIsarSetting()..chapterPageIndexList = chapterPageIndexs)); - } - } - - String getMangaName() { - return getManga().name!; - } - - String getSourceName() { - return getManga().source!; - } - - String getChapterTitle() { - return chapter.name!; - } -} - -class ReaderControllerAA { +class ReaderController { final Chapter chapter; - bool incognitoMode; - ReaderControllerAA({required this.chapter, required this.incognitoMode}); + ReaderController({required this.chapter}); Manga getManga() { return chapter.manga.value!; @@ -266,6 +38,7 @@ class ReaderControllerAA { return chapter; } + final incognitoMode = isar.settings.getSync(227)!.incognitoMode!; ReaderMode getReaderMode() { final personalReaderModeList = getIsarSetting().personalReaderModeList ?? []; @@ -311,8 +84,6 @@ class ReaderControllerAA { } void setMangaHistoryUpdate() { - // log("message"); - if (!incognitoMode) { isar.writeTxnSync(() { Manga? manga = chapter.manga.value; @@ -370,7 +141,7 @@ class ReaderControllerAA { return isar.chapters.getSync(chapter.id!)!.isBookmarked!; } - int getNextChapterIndex() { + int getPrevChapterIndex() { final chapters = getManga().chapters.toList(); int? index; for (var i = 0; i < chapters.length; i++) { @@ -381,7 +152,7 @@ class ReaderControllerAA { return index!; } - int getPrevChapterIndex() { + int getNextChapterIndex() { final chapters = getManga().chapters.toList(); int? index; for (var i = 0; i < chapters.length; i++) { @@ -403,14 +174,14 @@ class ReaderControllerAA { return index!; } - Chapter getNextChapter() { - return getManga().chapters.toList()[getNextChapterIndex()]; - } - Chapter getPrevChapter() { return getManga().chapters.toList()[getPrevChapterIndex()]; } + Chapter getNextChapter() { + return getManga().chapters.toList()[getNextChapterIndex()]; + } + int getChaptersLength() { return getManga().chapters.length; } diff --git a/lib/modules/manga/reader/providers/reader_controller_provider.g.dart b/lib/modules/manga/reader/providers/reader_controller_provider.g.dart index 8e845921..9df50dc9 100644 --- a/lib/modules/manga/reader/providers/reader_controller_provider.g.dart +++ b/lib/modules/manga/reader/providers/reader_controller_provider.g.dart @@ -122,100 +122,4 @@ class CurrentIndexProvider ); } } - -String _$readerControllerHash() => r'65ce80e436f72efffea6ba9b1b6f87be702b79cd'; - -abstract class _$ReaderController extends BuildlessAutoDisposeNotifier { - late final Chapter chapter; - - void build({ - required Chapter chapter, - }); -} - -/// See also [ReaderController]. -@ProviderFor(ReaderController) -const readerControllerProvider = ReaderControllerFamily(); - -/// See also [ReaderController]. -class ReaderControllerFamily extends Family { - /// See also [ReaderController]. - const ReaderControllerFamily(); - - /// See also [ReaderController]. - ReaderControllerProvider call({ - required Chapter chapter, - }) { - return ReaderControllerProvider( - chapter: chapter, - ); - } - - @override - ReaderControllerProvider getProviderOverride( - covariant ReaderControllerProvider provider, - ) { - return call( - chapter: provider.chapter, - ); - } - - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'readerControllerProvider'; -} - -/// See also [ReaderController]. -class ReaderControllerProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [ReaderController]. - ReaderControllerProvider({ - required this.chapter, - }) : super.internal( - () => ReaderController()..chapter = chapter, - from: readerControllerProvider, - name: r'readerControllerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$readerControllerHash, - dependencies: ReaderControllerFamily._dependencies, - allTransitiveDependencies: - ReaderControllerFamily._allTransitiveDependencies, - ); - - final Chapter chapter; - - @override - bool operator ==(Object other) { - return other is ReaderControllerProvider && other.chapter == chapter; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, chapter.hashCode); - - return _SystemHash.finish(hash); - } - - @override - void runNotifierBuild( - covariant ReaderController notifier, - ) { - return notifier.build( - chapter: chapter, - ); - } -} // ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions diff --git a/lib/router/router.dart b/lib/router/router.dart index 43afbf30..9e5dff70 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -3,10 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga_type.dart'; -import 'package:mangayomi/modules/archive_reader/archive_reader_screen.dart'; -import 'package:mangayomi/modules/archive_reader/models/models.dart'; -import 'package:mangayomi/modules/archive_reader/reader/archive_reader_reader_view.dart'; import 'package:mangayomi/modules/more/settings/downloads/downloads_screen.dart'; +import 'package:mangayomi/modules/updates/updates_screen.dart'; import 'package:mangayomi/modules/webview/webview.dart'; import 'package:mangayomi/modules/browse/browse_screen.dart'; import 'package:mangayomi/modules/browse/extension/extension_lang.dart'; @@ -53,15 +51,6 @@ class AsyncRouterNotifier extends ChangeNotifier { child: const LibraryScreen(), ), ), - GoRoute( - name: "archiveReader", - path: '/archiveReader', - builder: (context, state) => const LocalReaderScreen(), - pageBuilder: (context, state) => CustomTransition( - key: state.pageKey, - child: const LocalReaderScreen(), - ), - ), GoRoute( name: "history", path: '/history', @@ -80,6 +69,15 @@ class AsyncRouterNotifier extends ChangeNotifier { child: const BrowseScreen(), ), ), + GoRoute( + name: "updates", + path: '/updates', + builder: (context, state) => const UpdatesScreen(), + pageBuilder: (context, state) => CustomTransition( + key: state.pageKey, + child: const UpdatesScreen(), + ), + ), GoRoute( name: "more", path: '/more', @@ -335,25 +333,6 @@ class AsyncRouterNotifier extends ChangeNotifier { ); }, ), - GoRoute( - path: "/archiveReaderReaderView", - name: "archiveReaderReaderView", - builder: (context, state) { - final localArchive = state.extra as LocalArchive; - return ArchiveReaderReaderView( - localArchive: localArchive, - ); - }, - pageBuilder: (context, state) { - final localArchive = state.extra as LocalArchive; - return CustomTransition( - key: state.pageKey, - child: ArchiveReaderReaderView( - localArchive: localArchive, - ), - ); - }, - ), ]; } diff --git a/lib/services/get_chapter_url.dart b/lib/services/get_chapter_url.dart index cebe872a..782a3baa 100644 --- a/lib/services/get_chapter_url.dart +++ b/lib/services/get_chapter_url.dart @@ -7,6 +7,7 @@ import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/settings.dart'; import 'package:mangayomi/models/source.dart'; import 'package:mangayomi/modules/archive_reader/providers/archive_reader_providers.dart'; +import 'package:mangayomi/modules/manga/reader/manga_reader_view.dart'; import 'package:mangayomi/providers/storage_provider.dart'; import 'package:mangayomi/sources/multisrc/heancms/heancms.dart'; import 'package:mangayomi/sources/multisrc/madara/src/madara.dart'; @@ -28,11 +29,13 @@ class GetChapterUrlModel { List pageUrls = []; List isLocaleList = []; List archiveImages = []; + List uChapDataPreload; GetChapterUrlModel( {required this.path, required this.pageUrls, required this.isLocaleList, - required this.archiveImages}); + required this.archiveImages, + required this.uChapDataPreload}); } @riverpod @@ -40,6 +43,7 @@ Future getChapterUrl( GetChapterUrlRef ref, { required Chapter chapter, }) async { + List uChapDataPreloadp = []; Directory? path; List pageUrls = []; final manga = chapter.manga.value!; @@ -177,11 +181,29 @@ Future getChapterUrl( isar.writeTxnSync(() => isar.settings .putSync(settings..chapterPageUrlsList = chapterPageUrls)); } + for (var i = 0; i < pageUrls.length; i++) { + uChapDataPreloadp.add(UChapDataPreload( + chapter, + path, + pageUrls[i], + isLocaleList[i], + archiveImages[i], + i, + false, + false, + GetChapterUrlModel( + path: path, + pageUrls: pageUrls, + isLocaleList: isLocaleList, + archiveImages: archiveImages, + uChapDataPreload: uChapDataPreloadp))); + } } return GetChapterUrlModel( path: path, pageUrls: pageUrls, isLocaleList: isLocaleList, - archiveImages: archiveImages); + archiveImages: archiveImages, + uChapDataPreload: uChapDataPreloadp); } diff --git a/lib/services/get_chapter_url.g.dart b/lib/services/get_chapter_url.g.dart index 52c8628a..a2b234ac 100644 --- a/lib/services/get_chapter_url.g.dart +++ b/lib/services/get_chapter_url.g.dart @@ -6,7 +6,7 @@ part of 'get_chapter_url.dart'; // RiverpodGenerator // ************************************************************************** -String _$getChapterUrlHash() => r'd34b2bbc9062d7675347ff44d57943ff135b0135'; +String _$getChapterUrlHash() => r'6e9e28c2d3775791f9c8544cf0f41bb07b5eaf81'; /// Copied from Dart SDK class _SystemHash { diff --git a/pubspec.lock b/pubspec.lock index a160bab5..c16db2f3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -957,6 +957,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.5" + scrollview_observer: + dependency: "direct main" + description: + name: scrollview_observer + sha256: "459ab67e3b1680e660d3bff24b64c9de4fc6f22a49bd3ecbbafece8e83f0b973" + url: "https://pub.dev" + source: hosted + version: "1.14.0" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 93aeac86..2fe2c2de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: file_picker: ^5.3.0 path_provider: ^2.0.15 image: ^3.3.0 + scrollview_observer: ^1.14.0 # The following adds the Cupertino Icons font to your application.