mangayomi-mirror/lib/modules/manga/reader/providers/reader_controller_provider.dart
2026-04-15 23:41:19 +02:00

228 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/misc.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/modules/manga/reader/mixins/chapter_reader_settings_mixin.dart';
import 'package:mangayomi/modules/manga/reader/mixins/chapter_controller_mixin.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
import 'package:mangayomi/modules/more/settings/downloads/providers/downloads_state_provider.dart';
import 'package:mangayomi/utils/extensions/chapter.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'reader_controller_provider.g.dart';
@riverpod
class CurrentIndex extends _$CurrentIndex {
@override
int build(Chapter chapter) {
final incognitoMode = ref.watch(incognitoModeStateProvider);
if (incognitoMode) return 0;
return ref
.read(readerControllerProvider(chapter: chapter).notifier)
.getPageIndex();
}
void setCurrentIndex(int currentIndex) {
state = currentIndex;
}
}
BoxFit getBoxFit(ScaleType scaleType) {
return switch (scaleType) {
ScaleType.fitHeight => BoxFit.fitHeight,
ScaleType.fitWidth => BoxFit.fitWidth,
ScaleType.fitScreen => BoxFit.contain,
ScaleType.originalSize => BoxFit.cover,
ScaleType.smartFit => BoxFit.contain,
_ => BoxFit.cover,
};
}
@riverpod
class ReaderController extends _$ReaderController
with ChapterControllerMixin, ChapterReaderSettingsMixin {
@override
KeepAliveLink build({required Chapter chapter}) {
_keepAliveLink = ref.keepAlive();
return _keepAliveLink!;
}
KeepAliveLink? _keepAliveLink;
KeepAliveLink? get keepAliveLink => _keepAliveLink;
// Keep incognitoMode as a final field so it is read from Isar only once.
@override
final bool incognitoMode = isar.settings.getSync(227)!.incognitoMode!;
// Override getIsarSetting to add per-instance caching; callers that mutate
// settings must call _invalidateSettingsCache() afterwards.
Settings? _cachedSettings;
@override
void onSettingsMutated() => _cachedSettings = null;
@override
Settings getIsarSetting() => _cachedSettings ??= isar.settings.getSync(227)!;
// ---------------------------------------------------------------------------
// Reader-specific settings
// ---------------------------------------------------------------------------
ReaderMode getReaderMode() {
final personalReaderModeList =
getIsarSetting().personalReaderModeList ?? [];
final personalReaderMode = personalReaderModeList.where(
(element) => element.mangaId == getManga().id,
);
if (personalReaderMode.isNotEmpty) {
return personalReaderMode.first.readerMode;
}
return getIsarSetting().defaultReaderMode;
}
PageMode getPageMode() {
final personalPageModeList = getIsarSetting().personalPageModeList ?? [];
final personalPageMode = personalPageModeList.where(
(element) => element.mangaId == getManga().id,
);
if (personalPageMode.isNotEmpty) {
return personalPageMode.first.pageMode;
}
return PageMode.onePage;
}
void setReaderMode(ReaderMode newReaderMode) {
List<PersonalReaderMode>? 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
..updatedAt = DateTime.now().millisecondsSinceEpoch,
),
);
onSettingsMutated();
}
void setPageMode(PageMode newPageMode) {
List<PersonalPageMode>? personalPageModeLists = [];
for (var personalPageMode in getIsarSetting().personalPageModeList ?? []) {
if (personalPageMode.mangaId != getManga().id) {
personalPageModeLists.add(personalPageMode);
}
}
personalPageModeLists.add(
PersonalPageMode()
..mangaId = getManga().id
..pageMode = newPageMode,
);
isar.writeTxnSync(
() => isar.settings.putSync(
getIsarSetting()
..personalPageModeList = personalPageModeLists
..updatedAt = DateTime.now().millisecondsSinceEpoch,
),
);
onSettingsMutated();
}
void setShowPageNumber(bool value) {
if (!incognitoMode) {
isar.writeTxnSync(
() => isar.settings.putSync(
getIsarSetting()
..showPagesNumber = value
..updatedAt = DateTime.now().millisecondsSinceEpoch,
),
);
onSettingsMutated();
}
}
bool getShowPageNumber() {
if (!incognitoMode) return getIsarSetting().showPagesNumber!;
return true;
}
// ---------------------------------------------------------------------------
// Page tracking
// ---------------------------------------------------------------------------
int getPageIndex() {
if (incognitoMode) return 0;
final chapterPageIndexList = getIsarSetting().chapterPageIndexList ?? [];
final index = chapterPageIndexList.where(
(element) => element.chapterId == chapter.id,
);
return chapter.isRead!
? 0
: index.isNotEmpty
? index.first.index!
: 0;
}
int getPageLength(List incognitoPageLength) {
if (incognitoMode) return incognitoPageLength.length;
final urls = getIsarSetting().chapterPageUrlsList!
.where((element) => element.chapterId == chapter.id)
.firstOrNull
?.urls;
if (urls == null || urls.isEmpty) return incognitoPageLength.length;
return urls.length;
}
int? _lastSavedIndex;
void setPageIndex(int newIndex, bool save) {
if (chapter.isRead! || incognitoMode) return;
if (!save && newIndex == _lastSavedIndex) return;
_lastSavedIndex = newIndex;
final isContinuousLike =
getReaderMode() == ReaderMode.verticalContinuous ||
getReaderMode() == ReaderMode.webtoon;
final isRead = isContinuousLike
? (newIndex + 2) >= getPageLength([]) - 1
: (newIndex + 2) >= getPageLength([]);
if (isRead || save) {
List<ChapterPageIndex>? chapterPageIndexs = [];
for (var chapterPageIndex
in getIsarSetting().chapterPageIndexList ?? []) {
if (chapterPageIndex.chapterId != chapter.id) {
chapterPageIndexs.add(chapterPageIndex);
}
}
chapterPageIndexs.add(
ChapterPageIndex()
..chapterId = chapter.id
..index = isRead ? 0 : newIndex,
);
final chap = chapter;
isar.writeTxnSync(() {
isar.settings.putSync(
getIsarSetting()
..chapterPageIndexList = chapterPageIndexs
..updatedAt = DateTime.now().millisecondsSinceEpoch,
);
chap.isRead = isRead;
chap.lastPageRead = isRead ? '1' : (newIndex + 1).toString();
chap.updatedAt = DateTime.now().millisecondsSinceEpoch;
isar.chapters.putSync(chap);
});
onSettingsMutated();
if (isRead) {
chapter.updateTrackChapterRead(ref);
if (ref.read(deleteDownloadAfterReadingStateProvider)) {
chapter.deleteDownloadedFiles();
}
}
}
}
}