diff --git a/lib/foundation/comic_source/models.dart b/lib/foundation/comic_source/models.dart index 9d439bb..4be9310 100644 --- a/lib/foundation/comic_source/models.dart +++ b/lib/foundation/comic_source/models.dart @@ -376,7 +376,7 @@ class ComicChapters { return _groupedChapters![group] ?? {}; } - /// Get a group of chapters by index + /// Get a group of chapters by index(0-based) Map getGroupByIndex(int index) { return _groupedChapters!.values.elementAt(index); } diff --git a/lib/foundation/history.dart b/lib/foundation/history.dart index 87edb15..0c017cf 100644 --- a/lib/foundation/history.dart +++ b/lib/foundation/history.dart @@ -50,17 +50,24 @@ class History implements Comic { @override String cover; + /// index of chapters. 1-based. int ep; + /// index of pages. 1-based. int page; + /// index of chapter groups. 1-based. + /// If [group] is not null, [ep] is the index of chapter in the group. + int? group; + @override String id; /// readEpisode is a set of episode numbers that have been read. - /// - /// The number of episodes is 1-based. - Set readEpisode; + /// For normal chapters, it is a set of chapter numbers. + /// For grouped chapters, it is a set of strings in the format of "group_number-chapter_number". + /// 1-based. + Set readEpisode; @override int? maxPage; @@ -69,29 +76,17 @@ class History implements Comic { {required HistoryMixin model, required this.ep, required this.page, - Set? readChapters, + this.group, + Set? readChapters, DateTime? time}) : type = model.historyType, title = model.title, subtitle = model.subTitle ?? '', cover = model.cover, id = model.id, - readEpisode = readChapters ?? {}, + readEpisode = readChapters ?? {}, time = time ?? DateTime.now(); - Map toMap() => { - "type": type.value, - "time": time.millisecondsSinceEpoch, - "title": title, - "subtitle": subtitle, - "cover": cover, - "ep": ep, - "page": page, - "id": id, - "readEpisode": readEpisode.toList(), - "max_page": maxPage - }; - History.fromMap(Map map) : type = HistoryType(map["type"]), time = DateTime.fromMillisecondsSinceEpoch(map["time"]), @@ -101,8 +96,9 @@ class History implements Comic { ep = map["ep"], page = map["page"], id = map["id"], - readEpisode = Set.from( - (map["readEpisode"] as List?)?.toSet() ?? const {}), + readEpisode = Set.from( + (map["readEpisode"] as List?)?.toSet() ?? + const {}), maxPage = map["max_page"]; @override @@ -119,11 +115,11 @@ class History implements Comic { ep = row["ep"], page = row["page"], id = row["id"], - readEpisode = Set.from((row["readEpisode"] as String) + readEpisode = Set.from((row["readEpisode"] as String) .split(',') - .where((element) => element != "") - .map((e) => int.parse(e))), - maxPage = row["max_page"]; + .where((element) => element != "")), + maxPage = row["max_page"], + group = row["chapter_group"]; @override bool operator ==(Object other) { @@ -212,18 +208,24 @@ class HistoryManager with ChangeNotifier { ep int, page int, readEpisode text, - max_page int + max_page int, + chapter_group int ); """); + var columns = _db.select("PRAGMA table_info(history);"); + if (!columns.any((element) => element["name"] == "chapter_group")) { + _db.execute("alter table history add column chapter_group int;"); + } + notifyListeners(); ImageFavoriteManager().init(); isInitialized = true; } - static const _insertHistorySql = """ - insert or replace into history (id, title, subtitle, cover, time, type, ep, page, readEpisode, max_page) - values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + static const _insertHistorySql = """ + insert or replace into history (id, title, subtitle, cover, time, type, ep, page, readEpisode, max_page, chapter_group) + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); """; static Future _addHistoryAsync(int dbAddr, History newItem) { @@ -239,7 +241,8 @@ class HistoryManager with ChangeNotifier { newItem.ep, newItem.page, newItem.readEpisode.join(','), - newItem.maxPage + newItem.maxPage, + newItem.group ]); }); } @@ -281,7 +284,8 @@ class HistoryManager with ChangeNotifier { newItem.ep, newItem.page, newItem.readEpisode.join(','), - newItem.maxPage + newItem.maxPage, + newItem.group ]); if (_cachedHistoryIds == null) { updateCache(); diff --git a/lib/foundation/local.dart b/lib/foundation/local.dart index 2d19c34..c2b462c 100644 --- a/lib/foundation/local.dart +++ b/lib/foundation/local.dart @@ -115,6 +115,7 @@ class LocalComic with HistoryMixin implements Comic { chapters: chapters, initialChapter: history?.ep, initialPage: history?.page, + initialChapterGroup: history?.group, history: history ?? History.fromModel( model: this, diff --git a/lib/pages/comic_details_page/chapters.dart b/lib/pages/comic_details_page/chapters.dart index 2f13089..0a9586f 100644 --- a/lib/pages/comic_details_page/chapters.dart +++ b/lib/pages/comic_details_page/chapters.dart @@ -286,8 +286,13 @@ class _GroupedComicChaptersState extends State<_GroupedComicChapters> } chapterIndex += chapters.getGroupByIndex(j).length; } - bool visited = - (history?.readEpisode ?? {}).contains(chapterIndex + 1); + String rawIndex = (chapterIndex + 1).toString(); + String groupedIndex = "${index + 1}-${i + 1}"; + bool visited = false; + if (history != null) { + visited = history!.readEpisode.contains(groupedIndex) || + history!.readEpisode.contains(rawIndex); + } return Padding( padding: const EdgeInsets.fromLTRB(6, 4, 6, 4), child: Material( diff --git a/lib/pages/comic_details_page/comic_page.dart b/lib/pages/comic_details_page/comic_page.dart index de3a662..fef8424 100644 --- a/lib/pages/comic_details_page/comic_page.dart +++ b/lib/pages/comic_details_page/comic_page.dart @@ -167,6 +167,7 @@ class _ComicPageState extends LoadingState chapters: localComic.chapters, initialPage: history?.page, initialChapter: history?.ep, + initialChapterGroup: history?.group, history: history ?? History.fromModel( model: localComic, @@ -383,11 +384,19 @@ class _ComicPageState extends LoadingState bool haveChapter = comic.chapters != null; var page = history!.page; var ep = history!.ep; + var group = history!.group; String text; if (haveChapter) { + var epName = group == null + ? comic.chapters!.titles.elementAt( + math.min(ep - 1, comic.chapters!.length - 1), + ) + : comic.chapters! + .getGroupByIndex(group - 1) + .values + .elementAt(ep - 1); text = "Last Reading: @epName Page @page".tlParams({ - 'epName': comic.chapters!.titles.elementAt( - math.min(ep - 1, comic.chapters!.length - 1)), + 'epName': epName, 'page': page, }); } else { diff --git a/lib/pages/reader/loading.dart b/lib/pages/reader/loading.dart index e8a09d4..d8fb732 100644 --- a/lib/pages/reader/loading.dart +++ b/lib/pages/reader/loading.dart @@ -33,6 +33,7 @@ class _ReaderWithLoadingState history: data.history, initialChapter: widget.initialEp ?? data.history.ep, initialPage: widget.initialPage ?? data.history.page, + initialChapterGroup: data.history.group, author: data.author, tags: data.tags, ); diff --git a/lib/pages/reader/reader.dart b/lib/pages/reader/reader.dart index 4e3d139..8e1412f 100644 --- a/lib/pages/reader/reader.dart +++ b/lib/pages/reader/reader.dart @@ -62,6 +62,7 @@ class Reader extends StatefulWidget { required this.history, this.initialPage, this.initialChapter, + this.initialChapterGroup, required this.author, required this.tags, }); @@ -84,6 +85,9 @@ class Reader extends StatefulWidget { /// Starts from 1, invalid values equal to 1 final int? initialChapter; + /// Starts from 1, invalid values equal to 1 + final int? initialChapterGroup; + final History history; @override @@ -147,13 +151,18 @@ class _ReaderState extends State with _ReaderLocation, _ReaderWindow { @override void initState() { page = widget.initialPage ?? 1; - chapter = widget.initialChapter ?? 1; if (page < 1) { page = 1; } + chapter = widget.initialChapter ?? 1; if (chapter < 1) { chapter = 1; } + if (widget.initialChapterGroup != null) { + for (int i=0; i<(widget.initialChapterGroup!-1); i++) { + chapter += widget.chapters!.getGroupByIndex(i).length; + } + } if (widget.initialPage != null) { page = (widget.initialPage! / imagesPerPage).ceil(); } @@ -238,11 +247,23 @@ class _ReaderState extends State with _ReaderLocation, _ReaderWindow { void updateHistory() { if (history != null) { history!.page = page; - history!.ep = chapter; if (maxPage > 1) { history!.maxPage = maxPage; } - history!.readEpisode.add(chapter); + if (widget.chapters?.isGrouped ?? false) { + int g = 0; + int c = chapter; + while (c > widget.chapters!.getGroupByIndex(g).length) { + c -= widget.chapters!.getGroupByIndex(g).length; + g++; + } + history!.readEpisode.add('${g + 1}-$c'); + history!.ep = c; + history!.group = g+1; + } else { + history!.readEpisode.add(chapter.toString()); + history!.ep = chapter; + } history!.time = DateTime.now(); _updateHistoryTimer?.cancel(); _updateHistoryTimer = Timer(const Duration(seconds: 1), () {