mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-01-11 22:40:36 +00:00
569 lines
17 KiB
Dart
569 lines
17 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_riverpod/misc.dart';
|
|
import 'package:isar_community/isar.dart';
|
|
import 'package:mangayomi/main.dart';
|
|
import 'package:mangayomi/models/chapter.dart';
|
|
import 'package:mangayomi/models/download.dart';
|
|
import 'package:mangayomi/models/history.dart';
|
|
import 'package:mangayomi/models/manga.dart';
|
|
import 'package:mangayomi/models/settings.dart';
|
|
import 'package:mangayomi/models/track.dart';
|
|
import 'package:mangayomi/models/track_preference.dart';
|
|
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
|
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
|
|
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
|
import 'package:mangayomi/utils/chapter_recognition.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 {
|
|
@override
|
|
KeepAliveLink build({required Chapter chapter}) {
|
|
_keepAliveLink = ref.keepAlive();
|
|
return _keepAliveLink!;
|
|
}
|
|
|
|
KeepAliveLink? _keepAliveLink;
|
|
|
|
KeepAliveLink? get keepAliveLink => _keepAliveLink;
|
|
|
|
Manga getManga() {
|
|
return chapter.manga.value!;
|
|
}
|
|
|
|
Chapter geChapter() {
|
|
return chapter;
|
|
}
|
|
|
|
final incognitoMode = isar.settings.getSync(227)!.incognitoMode!;
|
|
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;
|
|
}
|
|
|
|
(bool, double) autoScrollValues() {
|
|
final autoScrollPagesList = getIsarSetting().autoScrollPages ?? [];
|
|
final autoScrollPages = autoScrollPagesList.where(
|
|
(element) => element.mangaId == getManga().id,
|
|
);
|
|
if (autoScrollPages.isNotEmpty) {
|
|
return (
|
|
autoScrollPages.first.autoScroll ?? false,
|
|
autoScrollPages.first.pageOffset ?? 10,
|
|
);
|
|
}
|
|
return (false, 10);
|
|
}
|
|
|
|
void setAutoScroll(bool value, double offset) {
|
|
List<AutoScrollPages>? autoScrollPagesList = [];
|
|
for (var autoScrollPages in getIsarSetting().autoScrollPages ?? []) {
|
|
if (autoScrollPages.mangaId != getManga().id) {
|
|
autoScrollPagesList.add(autoScrollPages);
|
|
}
|
|
}
|
|
autoScrollPagesList.add(
|
|
AutoScrollPages()
|
|
..mangaId = getManga().id
|
|
..pageOffset = offset
|
|
..autoScroll = value,
|
|
);
|
|
isar.writeTxnSync(
|
|
() => isar.settings.putSync(
|
|
getIsarSetting()
|
|
..autoScrollPages = autoScrollPagesList
|
|
..updatedAt = DateTime.now().millisecondsSinceEpoch,
|
|
),
|
|
);
|
|
}
|
|
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
|
|
void setShowPageNumber(bool value) {
|
|
if (!incognitoMode) {
|
|
isar.writeTxnSync(
|
|
() => isar.settings.putSync(
|
|
getIsarSetting()
|
|
..showPagesNumber = value
|
|
..updatedAt = DateTime.now().millisecondsSinceEpoch,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Settings getIsarSetting() {
|
|
return isar.settings.getSync(227)!;
|
|
}
|
|
|
|
bool getShowPageNumber() {
|
|
if (!incognitoMode) {
|
|
return getIsarSetting().showPagesNumber!;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void setMangaHistoryUpdate() {
|
|
if (incognitoMode) return;
|
|
isar.writeTxnSync(() {
|
|
Manga? manga = chapter.manga.value;
|
|
manga!.lastRead = DateTime.now().millisecondsSinceEpoch;
|
|
manga.updatedAt = 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(),
|
|
itemType: getManga().itemType,
|
|
chapterId: chapter.id,
|
|
)..chapter.value = chapter;
|
|
} else {
|
|
history =
|
|
(isar.historys
|
|
.filter()
|
|
.mangaIdEqualTo(getManga().id)
|
|
.findFirstSync())!
|
|
..chapterId = chapter.id
|
|
..chapter.value = chapter
|
|
..date = DateTime.now().millisecondsSinceEpoch.toString();
|
|
}
|
|
isar.writeTxnSync(() {
|
|
history!.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
|
isar.historys.putSync(history);
|
|
history.chapter.saveSync();
|
|
});
|
|
}
|
|
|
|
void setChapterBookmarked() {
|
|
if (incognitoMode) return;
|
|
final isBookmarked = getChapterBookmarked();
|
|
final chap = chapter;
|
|
isar.writeTxnSync(() {
|
|
chap.isBookmarked = !isBookmarked;
|
|
chap.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
|
isar.chapters.putSync(chap);
|
|
});
|
|
}
|
|
|
|
bool getChapterBookmarked() {
|
|
return isar.chapters.getSync(chapter.id!)!.isBookmarked!;
|
|
}
|
|
|
|
(int, bool) getPrevChapterIndex() {
|
|
final chapters = getManga().getFilteredChapterList();
|
|
int? index;
|
|
for (var i = 0; i < chapters.length; i++) {
|
|
if (chapters[i].id == chapter.id) {
|
|
index = i + 1;
|
|
}
|
|
}
|
|
if (index == null) {
|
|
final chapters = getManga().chapters.toList().reversed.toList();
|
|
for (var i = 0; i < chapters.length; i++) {
|
|
if (chapters[i].id == chapter.id) {
|
|
index = i + 1;
|
|
}
|
|
}
|
|
return (index!, false);
|
|
}
|
|
return (index, true);
|
|
}
|
|
|
|
(int, bool) getNextChapterIndex() {
|
|
final chapters = getManga().getFilteredChapterList();
|
|
int? index;
|
|
for (var i = 0; i < chapters.length; i++) {
|
|
if (chapters[i].id == chapter.id) {
|
|
index = i - 1;
|
|
}
|
|
}
|
|
if (index == null) {
|
|
final chapters = getManga().chapters.toList().reversed.toList();
|
|
for (var i = 0; i < chapters.length; i++) {
|
|
if (chapters[i].id == chapter.id) {
|
|
index = i - 1;
|
|
}
|
|
}
|
|
return (index!, false);
|
|
}
|
|
return (index, true);
|
|
}
|
|
|
|
(int, bool) getChapterIndex() {
|
|
final chapters = getManga().getFilteredChapterList();
|
|
int? index;
|
|
for (var i = 0; i < chapters.length; i++) {
|
|
if (chapters[i].id == chapter.id) {
|
|
index = i;
|
|
}
|
|
}
|
|
if (index == null) {
|
|
final chapters = getManga().chapters.toList().reversed.toList();
|
|
for (var i = 0; i < chapters.length; i++) {
|
|
if (chapters[i].id == chapter.id) {
|
|
index = i;
|
|
}
|
|
}
|
|
return (index!, false);
|
|
}
|
|
return (index, true);
|
|
}
|
|
|
|
Chapter getPrevChapter() {
|
|
final prevChapIdx = getPrevChapterIndex();
|
|
return prevChapIdx.$2
|
|
? getManga().getFilteredChapterList()[prevChapIdx.$1]
|
|
: getManga().chapters.toList().reversed.toList()[prevChapIdx.$1];
|
|
}
|
|
|
|
Chapter getNextChapter() {
|
|
final nextChapIdx = getNextChapterIndex();
|
|
return nextChapIdx.$2
|
|
? getManga().getFilteredChapterList()[nextChapIdx.$1]
|
|
: getManga().chapters.toList().reversed.toList()[nextChapIdx.$1];
|
|
}
|
|
|
|
int getChaptersLength(bool isInFilterList) {
|
|
return isInFilterList
|
|
? getManga().getFilteredChapterList().length
|
|
: getManga().chapters.length;
|
|
}
|
|
|
|
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;
|
|
return getIsarSetting().chapterPageUrlsList!
|
|
.where((element) => element.chapterId == chapter.id)
|
|
.first
|
|
.urls!
|
|
.length;
|
|
}
|
|
|
|
void setPageIndex(int newIndex, bool save) {
|
|
if (chapter.isRead!) return;
|
|
if (incognitoMode) return;
|
|
final isRead =
|
|
(getReaderMode() == ReaderMode.verticalContinuous ||
|
|
getReaderMode() == ReaderMode.webtoon)
|
|
? ((newIndex + 2) == getPageLength([]) - 1)
|
|
? ((newIndex + 2) == getPageLength([]) - 1)
|
|
: (newIndex + 2) == getPageLength([])
|
|
: (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);
|
|
});
|
|
if (isRead) {
|
|
chapter.updateTrackChapterRead(ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
String getMangaName() {
|
|
return getManga().name!;
|
|
}
|
|
|
|
String getSourceName() {
|
|
return getManga().source!;
|
|
}
|
|
|
|
String getChapterTitle() {
|
|
return chapter.name!;
|
|
}
|
|
}
|
|
|
|
extension ChapterExtensions on Chapter {
|
|
void updateTrackChapterRead(dynamic ref) {
|
|
if (!(ref is WidgetRef || ref is Ref)) return;
|
|
final updateProgressAfterReading = ref.watch(
|
|
updateProgressAfterReadingStateProvider,
|
|
);
|
|
if (!updateProgressAfterReading) return;
|
|
final manga = this.manga.value!;
|
|
final chapterNumber = ChapterRecognition().parseChapterNumber(
|
|
manga.name!,
|
|
name!,
|
|
);
|
|
|
|
final tracks = isar.tracks
|
|
.filter()
|
|
.idIsNotNull()
|
|
.itemTypeEqualTo(manga.itemType)
|
|
.mangaIdEqualTo(manga.id!)
|
|
.findAllSync();
|
|
|
|
if (tracks.isEmpty) return;
|
|
for (var track in tracks) {
|
|
final service = isar.trackPreferences
|
|
.filter()
|
|
.syncIdIsNotNull()
|
|
.syncIdEqualTo(track.syncId)
|
|
.findFirstSync();
|
|
if (!(service == null || chapterNumber <= (track.lastChapterRead ?? 0))) {
|
|
if (track.status != TrackStatus.completed) {
|
|
track.lastChapterRead = chapterNumber;
|
|
if (track.lastChapterRead == track.totalChapter &&
|
|
(track.totalChapter ?? 0) > 0) {
|
|
track.status = TrackStatus.completed;
|
|
track.finishedReadingDate = DateTime.now().millisecondsSinceEpoch;
|
|
} else {
|
|
track.status = manga.itemType == ItemType.manga
|
|
? TrackStatus.reading
|
|
: TrackStatus.watching;
|
|
if (track.lastChapterRead == 1) {
|
|
track.startedReadingDate = DateTime.now().millisecondsSinceEpoch;
|
|
}
|
|
}
|
|
}
|
|
ref
|
|
.read(
|
|
trackStateProvider(
|
|
track: track,
|
|
itemType: manga.itemType,
|
|
widgetRef: ref,
|
|
).notifier,
|
|
)
|
|
.updateManga();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension MangaExtensions on Manga {
|
|
List<Chapter> getFilteredChapterList() {
|
|
final data = this.chapters.toList().reversed.toList();
|
|
final filterUnread =
|
|
(isar.settings
|
|
.getSync(227)!
|
|
.chapterFilterUnreadList!
|
|
.where((element) => element.mangaId == id)
|
|
.toList()
|
|
.firstOrNull ??
|
|
ChapterFilterUnread(mangaId: id, type: 0))
|
|
.type!;
|
|
|
|
final filterBookmarked =
|
|
(isar.settings
|
|
.getSync(227)!
|
|
.chapterFilterBookmarkedList!
|
|
.where((element) => element.mangaId == id)
|
|
.toList()
|
|
.firstOrNull ??
|
|
ChapterFilterBookmarked(mangaId: id, type: 0))
|
|
.type!;
|
|
final filterDownloaded =
|
|
(isar.settings
|
|
.getSync(227)!
|
|
.chapterFilterDownloadedList!
|
|
.where((element) => element.mangaId == id)
|
|
.toList()
|
|
.firstOrNull ??
|
|
ChapterFilterDownloaded(mangaId: id, type: 0))
|
|
.type!;
|
|
|
|
final sortChapter =
|
|
(isar.settings
|
|
.getSync(227)!
|
|
.sortChapterList!
|
|
.where((element) => element.mangaId == id)
|
|
.toList()
|
|
.firstOrNull ??
|
|
SortChapter(mangaId: id, index: 1, reverse: false))
|
|
.index;
|
|
final filterScanlator = _getFilterScanlator(this) ?? [];
|
|
List<Chapter>? chapterList;
|
|
chapterList = data
|
|
.where(
|
|
(element) => filterUnread == 1
|
|
? element.isRead == false
|
|
: filterUnread == 2
|
|
? element.isRead == true
|
|
: true,
|
|
)
|
|
.where(
|
|
(element) => filterBookmarked == 1
|
|
? element.isBookmarked == true
|
|
: filterBookmarked == 2
|
|
? element.isBookmarked == false
|
|
: true,
|
|
)
|
|
.where((element) {
|
|
final modelChapDownload = isar.downloads
|
|
.filter()
|
|
.idEqualTo(element.id)
|
|
.findAllSync();
|
|
return filterDownloaded == 1
|
|
? modelChapDownload.isNotEmpty &&
|
|
modelChapDownload.first.isDownload == true
|
|
: filterDownloaded == 2
|
|
? !(modelChapDownload.isNotEmpty &&
|
|
modelChapDownload.first.isDownload == true)
|
|
: true;
|
|
})
|
|
.where((element) => !filterScanlator.contains(element.scanlator))
|
|
.toList();
|
|
List<Chapter> chapters = sortChapter == 1
|
|
? chapterList.reversed.toList()
|
|
: chapterList;
|
|
if (sortChapter == 0) {
|
|
chapters.sort((a, b) {
|
|
return (a.scanlator == null ||
|
|
b.scanlator == null ||
|
|
a.dateUpload == null ||
|
|
b.dateUpload == null)
|
|
? 0
|
|
: a.scanlator!.compareTo(b.scanlator!) |
|
|
a.dateUpload!.compareTo(b.dateUpload!);
|
|
});
|
|
} else if (sortChapter == 2) {
|
|
chapters.sort((a, b) {
|
|
return (a.dateUpload == null || b.dateUpload == null)
|
|
? 0
|
|
: int.parse(a.dateUpload!).compareTo(int.parse(b.dateUpload!));
|
|
});
|
|
} else if (sortChapter == 3) {
|
|
chapters.sort((a, b) {
|
|
return (a.name == null || b.name == null)
|
|
? 0
|
|
: a.name!.compareTo(b.name!);
|
|
});
|
|
}
|
|
return chapterList;
|
|
}
|
|
}
|
|
|
|
List<String>? _getFilterScanlator(Manga manga) {
|
|
final scanlators = isar.settings.getSync(227)!.filterScanlatorList ?? [];
|
|
final filter = scanlators
|
|
.where((element) => element.mangaId == manga.id)
|
|
.toList();
|
|
return filter.firstOrNull?.scanlators;
|
|
}
|