This commit is contained in:
Moustapha Kodjo Amadou 2025-11-05 12:21:36 +01:00
parent 0d0f488ef3
commit 1f2b143585
3 changed files with 49 additions and 170 deletions

View file

@ -247,7 +247,7 @@ class _MangaChapterPageGalleryState
late int pagePreloadAmount = ref.read(pagePreloadAmountStateProvider);
late bool _isBookmarked = _readerController.getChapterBookmarked();
bool _isLastPageTransition = false;
final _currentReaderMode = StateProvider<ReaderMode?>(() => null);
PageMode? _pageMode;
bool _isView = false;
@ -471,7 +471,7 @@ class _MangaChapterPageGalleryState
if (cropBorders) {
_processCropBorders();
}
final usePageTapZones = ref.watch(usePageTapZonesStateProvider);
final l10n = l10nLocalizations(context)!;
return KeyboardListener(
autofocus: true,
@ -635,22 +635,24 @@ class _MangaChapterPageGalleryState
}
},
onReachedLastPage: (lastPageIndex) {
try {
ref
.watch(
getChapterPagesProvider(
chapter: _readerController
.getNextChapter(),
).future,
)
.then(
(value) => _preloadNextChapter(
value,
chapter,
),
);
} on RangeError {
_addLastPageTransition(chapter);
if (!_isLastPageTransition) {
try {
ref
.watch(
getChapterPagesProvider(
chapter: _readerController
.getNextChapter(),
).future,
)
.then(
(value) => _preloadNextChapter(
value,
chapter,
),
);
} on RangeError {
_addLastPageTransition(chapter);
}
}
},
),
@ -941,8 +943,28 @@ class _MangaChapterPageGalleryState
onPageChanged: _onPageChanged,
),
),
_gestureRightLeft(failedToLoadImage, usePageTapZones),
_gestureTopBottom(failedToLoadImage, usePageTapZones),
Consumer(
builder: (context, ref, child) {
final usePageTapZones = ref.watch(
usePageTapZonesStateProvider,
);
return _gestureRightLeft(
failedToLoadImage,
usePageTapZones,
);
},
),
Consumer(
builder: (context, ref, child) {
final usePageTapZones = ref.watch(
usePageTapZonesStateProvider,
);
return _gestureTopBottom(
failedToLoadImage,
usePageTapZones,
);
},
),
_appBar(),
_bottomBar(),
_showPage(),
@ -1020,7 +1042,8 @@ class _MangaChapterPageGalleryState
});
}
}
if (itemPositions.last.index == pagesLength - 1) {
if ((itemPositions.last.index == pagesLength - 1) &&
!_isLastPageTransition) {
try {
ref
.watch(
@ -1042,6 +1065,7 @@ class _MangaChapterPageGalleryState
}
void _addLastPageTransition(Chapter chap) {
if (_isLastPageTransition) return;
try {
if (!mounted || (_uChapDataPreload.last.isLastChapter ?? false)) return;
final currentLength = _uChapDataPreload.length;
@ -1056,6 +1080,7 @@ class _MangaChapterPageGalleryState
if (mounted) {
setState(() {
_uChapDataPreload.add(transitionPage);
_isLastPageTransition = true;
});
}
} catch (_) {}
@ -1195,7 +1220,8 @@ class _MangaChapterPageGalleryState
.setCurrentIndex(_uChapDataPreload[index].index!);
}
if (_uChapDataPreload[index].pageIndex! == _uChapDataPreload.length - 1) {
if ((_uChapDataPreload[index].pageIndex! == _uChapDataPreload.length - 1) &&
!_isLastPageTransition) {
try {
ref
.watch(

View file

@ -1,11 +1,10 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
/// Page loading states for virtual scrolling
enum PageLoadState { notLoaded, loading, loaded, error, cached }
enum PageLoadState { notLoaded, loaded, error, cached }
/// Virtual page information for tracking state
class VirtualPageInfo {
@ -26,7 +25,6 @@ class VirtualPageInfo {
bool get isVisible =>
loadState == PageLoadState.loaded || loadState == PageLoadState.cached;
bool get needsLoading => loadState == PageLoadState.notLoaded;
bool get isLoading => loadState == PageLoadState.loading;
bool get hasError => loadState == PageLoadState.error;
void markAccessed() {
@ -98,9 +96,6 @@ class VirtualPageManager extends ChangeNotifier {
/// Get page count
int get pageCount => _originalPages.length;
/// Get current visible index
int get currentVisibleIndex => _currentVisibleIndex;
/// Get page info for a specific index
VirtualPageInfo? getPageInfo(int index) {
if (index < 0 || index >= _originalPages.length) return null;
@ -130,13 +125,6 @@ class VirtualPageManager extends ChangeNotifier {
return distance <= config.preloadDistance;
}
/// Get priority for a page (higher = more important)
int getPagePriority(int index) {
final distance = (index - _currentVisibleIndex).abs();
if (distance == 0) return 1000; // Current page has highest priority
return max(0, 100 - distance * 10);
}
/// Schedule preloading for nearby pages
void _schedulePreloading() {
_preloadQueue.clear();
@ -150,43 +138,6 @@ class VirtualPageManager extends ChangeNotifier {
}
}
}
// Process preload queue
_processPreloadQueue();
}
/// Process the preload queue
void _processPreloadQueue() {
final sortedQueue = _preloadQueue.toList()
..sort((a, b) => getPagePriority(b).compareTo(getPagePriority(a)));
for (final index in sortedQueue.take(3)) {
// Limit concurrent loading
_loadPage(index);
}
}
/// Load a specific page
Future<void> _loadPage(int index) async {
final pageInfo = _pageInfoMap[index];
if (pageInfo == null || pageInfo.isLoading) return;
pageInfo.loadState = PageLoadState.loading;
notifyListeners();
try {
// For now, we just mark as loaded since the actual image loading
// is handled by the ImageView widgets
await Future.delayed(const Duration(milliseconds: 10));
pageInfo.loadState = PageLoadState.loaded;
pageInfo.markAccessed();
} catch (error) {
pageInfo.loadState = PageLoadState.error;
pageInfo.error = error;
}
notifyListeners();
}
/// Perform memory cleanup
@ -238,11 +189,6 @@ class VirtualPageManager extends ChangeNotifier {
}
}
/// Force load a page immediately
Future<void> forceLoadPage(int index) async {
await _loadPage(index);
}
/// Get memory usage statistics
Map<String, dynamic> getMemoryStats() {
final loadedCount = _pageInfoMap.values
@ -262,24 +208,4 @@ class VirtualPageManager extends ChangeNotifier {
'preloadQueueSize': _preloadQueue.length,
};
}
/// Preload a range of pages
Future<void> preloadRange(int startIndex, int endIndex) async {
for (int i = startIndex; i <= endIndex && i < _originalPages.length; i++) {
if (i >= 0) {
await _loadPage(i);
}
}
}
/// Clear all cached pages
void clearCache() {
for (final pageInfo in _pageInfoMap.values) {
if (pageInfo.loadState != PageLoadState.loading) {
pageInfo.loadState = PageLoadState.notLoaded;
pageInfo.error = null;
}
}
notifyListeners();
}
}

View file

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
import 'package:mangayomi/utils/riverpod.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';
@ -10,12 +9,6 @@ import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_page_manager.dart';
import 'package:mangayomi/modules/manga/reader/virtual_scrolling/virtual_manga_list.dart';
/// Provides virtual page manager instances
final virtualPageManagerProvider =
Provider.family<VirtualPageManager, List<UChapDataPreload>>((ref, pages) {
return VirtualPageManager(pages: pages);
});
/// Main widget for virtual reading that replaces ScrollablePositionedList
class VirtualReaderView extends ConsumerStatefulWidget {
final List<UChapDataPreload> pages;
@ -155,69 +148,3 @@ class _VirtualReaderViewState extends ConsumerState<VirtualReaderView> {
);
}
}
/// Mixin to add virtual page manager capabilities to existing widgets
mixin VirtualPageManagerMixin<T extends ConsumerStatefulWidget>
on ConsumerState<T> {
VirtualPageManager? _virtualPageManager;
VirtualPageManager get virtualPageManager {
_virtualPageManager ??= VirtualPageManager(pages: getPages());
return _virtualPageManager!;
}
/// Override this method to provide the pages list
List<UChapDataPreload> getPages();
/// Call this when pages change
void updateVirtualPages(List<UChapDataPreload> newPages) {
_virtualPageManager?.dispose();
_virtualPageManager = VirtualPageManager(pages: newPages);
}
/// Call this when the visible page changes
void updateVisiblePage(int index) {
virtualPageManager.updateVisibleIndex(index);
}
@override
void dispose() {
_virtualPageManager?.dispose();
super.dispose();
}
}
/// Configuration provider for virtual page manager
final virtualPageConfigProvider = Provider<VirtualPageConfig>((ref) {
// Get user preferences for virtual scrolling configuration
final preloadAmount = ref.watch(readerPagePreloadAmountStateProvider);
return VirtualPageConfig(
preloadDistance: preloadAmount,
maxCachedPages: preloadAmount * 3,
cacheTimeout: const Duration(minutes: 5),
enableMemoryOptimization: true,
);
});
/// Provider for page preload amount (renamed to avoid conflicts)
final readerPagePreloadAmountStateProvider = StateProvider<int>(() => 3);
/// Extension to convert ReaderMode to virtual scrolling parameters
extension ReaderModeExtension on ReaderMode {
bool get isContinuous {
return this == ReaderMode.verticalContinuous ||
this == ReaderMode.webtoon ||
this == ReaderMode.horizontalContinuous;
}
Axis get scrollDirection {
return this == ReaderMode.horizontalContinuous
? Axis.horizontal
: Axis.vertical;
}
bool get isHorizontalContinuous {
return this == ReaderMode.horizontalContinuous;
}
}