mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 23:22:07 +00:00
Manage trackers
This commit is contained in:
parent
8c846edbf5
commit
eaca422612
17 changed files with 492 additions and 122 deletions
|
|
@ -261,6 +261,7 @@
|
|||
"restore_backup_warning_title":"Restoring a backup will overwrite all existing data.\n\nContinue restoring?",
|
||||
"services":"Services",
|
||||
"tracking_warning_info":"One-way sync yo update the chapter progress in tracking services. Set up tracking for individual entries from their tracking button.",
|
||||
"use_page_tap_zones":"Use page tap zones"
|
||||
"use_page_tap_zones":"Use page tap zones" ,
|
||||
"manage_trackers":"Manage trackers"
|
||||
|
||||
}
|
||||
|
|
@ -261,5 +261,6 @@
|
|||
"restore_backup_warning_title":"La restauration d'une sauvegarde écrasera toutes les données existantes.\n\nContinuer la restauration ?",
|
||||
"services":"Services",
|
||||
"tracking_warning_info":"Synchronisation à sens unique pour mettre à jour la progression du chapitre dans les services de suivi. Configurez le suivi des entrées individuelles à partir de leur boutton de suivi.",
|
||||
"use_page_tap_zones":"Utiliser les zones tactiles"
|
||||
"use_page_tap_zones":"Utiliser les zones tactiles",
|
||||
"manage_trackers":"Gérer les suivis"
|
||||
}
|
||||
|
|
@ -31,6 +31,8 @@ class Track {
|
|||
|
||||
String? trackingUrl;
|
||||
|
||||
bool? isManga;
|
||||
|
||||
Track(
|
||||
{this.id = Isar.autoIncrement,
|
||||
this.libraryId,
|
||||
|
|
@ -44,7 +46,8 @@ class Track {
|
|||
required this.status,
|
||||
this.startedReadingDate,
|
||||
this.finishedReadingDate,
|
||||
this.trackingUrl});
|
||||
this.trackingUrl,
|
||||
this.isManga});
|
||||
Track.fromJson(Map<String, dynamic> json) {
|
||||
finishedReadingDate = json['finishedReadingDate'];
|
||||
id = json['id'];
|
||||
|
|
@ -59,6 +62,7 @@ class Track {
|
|||
title = json['title'];
|
||||
totalChapter = json['totalChapter'];
|
||||
trackingUrl = json['trackingUrl'];
|
||||
isManga = json['isManga'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -76,6 +80,7 @@ class Track {
|
|||
data['title'] = title;
|
||||
data['totalChapter'] = totalChapter;
|
||||
data['trackingUrl'] = trackingUrl;
|
||||
data['isManga'] = isManga;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,59 +22,64 @@ const TrackSchema = CollectionSchema(
|
|||
name: r'finishedReadingDate',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'lastChapterRead': PropertySchema(
|
||||
r'isManga': PropertySchema(
|
||||
id: 1,
|
||||
name: r'isManga',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'lastChapterRead': PropertySchema(
|
||||
id: 2,
|
||||
name: r'lastChapterRead',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'libraryId': PropertySchema(
|
||||
id: 2,
|
||||
id: 3,
|
||||
name: r'libraryId',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'mangaId': PropertySchema(
|
||||
id: 3,
|
||||
id: 4,
|
||||
name: r'mangaId',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'mediaId': PropertySchema(
|
||||
id: 4,
|
||||
id: 5,
|
||||
name: r'mediaId',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'score': PropertySchema(
|
||||
id: 5,
|
||||
id: 6,
|
||||
name: r'score',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'startedReadingDate': PropertySchema(
|
||||
id: 6,
|
||||
id: 7,
|
||||
name: r'startedReadingDate',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'status': PropertySchema(
|
||||
id: 7,
|
||||
id: 8,
|
||||
name: r'status',
|
||||
type: IsarType.byte,
|
||||
enumMap: _TrackstatusEnumValueMap,
|
||||
),
|
||||
r'syncId': PropertySchema(
|
||||
id: 8,
|
||||
id: 9,
|
||||
name: r'syncId',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'title': PropertySchema(
|
||||
id: 9,
|
||||
id: 10,
|
||||
name: r'title',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'totalChapter': PropertySchema(
|
||||
id: 10,
|
||||
id: 11,
|
||||
name: r'totalChapter',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'trackingUrl': PropertySchema(
|
||||
id: 11,
|
||||
id: 12,
|
||||
name: r'trackingUrl',
|
||||
type: IsarType.string,
|
||||
)
|
||||
|
|
@ -121,17 +126,18 @@ void _trackSerialize(
|
|||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeLong(offsets[0], object.finishedReadingDate);
|
||||
writer.writeLong(offsets[1], object.lastChapterRead);
|
||||
writer.writeLong(offsets[2], object.libraryId);
|
||||
writer.writeLong(offsets[3], object.mangaId);
|
||||
writer.writeLong(offsets[4], object.mediaId);
|
||||
writer.writeLong(offsets[5], object.score);
|
||||
writer.writeLong(offsets[6], object.startedReadingDate);
|
||||
writer.writeByte(offsets[7], object.status.index);
|
||||
writer.writeLong(offsets[8], object.syncId);
|
||||
writer.writeString(offsets[9], object.title);
|
||||
writer.writeLong(offsets[10], object.totalChapter);
|
||||
writer.writeString(offsets[11], object.trackingUrl);
|
||||
writer.writeBool(offsets[1], object.isManga);
|
||||
writer.writeLong(offsets[2], object.lastChapterRead);
|
||||
writer.writeLong(offsets[3], object.libraryId);
|
||||
writer.writeLong(offsets[4], object.mangaId);
|
||||
writer.writeLong(offsets[5], object.mediaId);
|
||||
writer.writeLong(offsets[6], object.score);
|
||||
writer.writeLong(offsets[7], object.startedReadingDate);
|
||||
writer.writeByte(offsets[8], object.status.index);
|
||||
writer.writeLong(offsets[9], object.syncId);
|
||||
writer.writeString(offsets[10], object.title);
|
||||
writer.writeLong(offsets[11], object.totalChapter);
|
||||
writer.writeString(offsets[12], object.trackingUrl);
|
||||
}
|
||||
|
||||
Track _trackDeserialize(
|
||||
|
|
@ -143,18 +149,19 @@ Track _trackDeserialize(
|
|||
final object = Track(
|
||||
finishedReadingDate: reader.readLongOrNull(offsets[0]),
|
||||
id: id,
|
||||
lastChapterRead: reader.readLongOrNull(offsets[1]),
|
||||
libraryId: reader.readLongOrNull(offsets[2]),
|
||||
mangaId: reader.readLongOrNull(offsets[3]),
|
||||
mediaId: reader.readLongOrNull(offsets[4]),
|
||||
score: reader.readLongOrNull(offsets[5]),
|
||||
startedReadingDate: reader.readLongOrNull(offsets[6]),
|
||||
status: _TrackstatusValueEnumMap[reader.readByteOrNull(offsets[7])] ??
|
||||
isManga: reader.readBoolOrNull(offsets[1]),
|
||||
lastChapterRead: reader.readLongOrNull(offsets[2]),
|
||||
libraryId: reader.readLongOrNull(offsets[3]),
|
||||
mangaId: reader.readLongOrNull(offsets[4]),
|
||||
mediaId: reader.readLongOrNull(offsets[5]),
|
||||
score: reader.readLongOrNull(offsets[6]),
|
||||
startedReadingDate: reader.readLongOrNull(offsets[7]),
|
||||
status: _TrackstatusValueEnumMap[reader.readByteOrNull(offsets[8])] ??
|
||||
TrackStatus.reading,
|
||||
syncId: reader.readLongOrNull(offsets[8]),
|
||||
title: reader.readStringOrNull(offsets[9]),
|
||||
totalChapter: reader.readLongOrNull(offsets[10]),
|
||||
trackingUrl: reader.readStringOrNull(offsets[11]),
|
||||
syncId: reader.readLongOrNull(offsets[9]),
|
||||
title: reader.readStringOrNull(offsets[10]),
|
||||
totalChapter: reader.readLongOrNull(offsets[11]),
|
||||
trackingUrl: reader.readStringOrNull(offsets[12]),
|
||||
);
|
||||
return object;
|
||||
}
|
||||
|
|
@ -169,7 +176,7 @@ P _trackDeserializeProp<P>(
|
|||
case 0:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
return (reader.readBoolOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 3:
|
||||
|
|
@ -181,15 +188,17 @@ P _trackDeserializeProp<P>(
|
|||
case 6:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 8:
|
||||
return (_TrackstatusValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||
TrackStatus.reading) as P;
|
||||
case 8:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 9:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 10:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 10:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 11:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 12:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
|
|
@ -447,6 +456,32 @@ extension TrackQueryFilter on QueryBuilder<Track, Track, QFilterCondition> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterFilterCondition> isMangaIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'isManga',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterFilterCondition> isMangaIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'isManga',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterFilterCondition> isMangaEqualTo(
|
||||
bool? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'isManga',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterFilterCondition> lastChapterReadIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
|
|
@ -1358,6 +1393,18 @@ extension TrackQuerySortBy on QueryBuilder<Track, Track, QSortBy> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterSortBy> sortByIsManga() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isManga', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterSortBy> sortByIsMangaDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isManga', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterSortBy> sortByLastChapterRead() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'lastChapterRead', Sort.asc);
|
||||
|
|
@ -1516,6 +1563,18 @@ extension TrackQuerySortThenBy on QueryBuilder<Track, Track, QSortThenBy> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterSortBy> thenByIsManga() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isManga', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterSortBy> thenByIsMangaDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'isManga', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QAfterSortBy> thenByLastChapterRead() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'lastChapterRead', Sort.asc);
|
||||
|
|
@ -1656,6 +1715,12 @@ extension TrackQueryWhereDistinct on QueryBuilder<Track, Track, QDistinct> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QDistinct> distinctByIsManga() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'isManga');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, Track, QDistinct> distinctByLastChapterRead() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'lastChapterRead');
|
||||
|
|
@ -1738,6 +1803,12 @@ extension TrackQueryProperty on QueryBuilder<Track, Track, QQueryProperty> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, bool?, QQueryOperations> isMangaProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'isManga');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Track, int?, QQueryOperations> lastChapterReadProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'lastChapterRead');
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ 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/track.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'migration.g.dart';
|
||||
|
||||
|
|
@ -20,25 +22,32 @@ Future<void> migration(MigrationRef ref) async {
|
|||
.idIsNotNull()
|
||||
.isMangaIsNull()
|
||||
.findAllSync();
|
||||
final tracks =
|
||||
isar.tracks.filter().idIsNotNull().isMangaIsNull().findAllSync();
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
//mangaId in chapter
|
||||
for (var chapter in chapters) {
|
||||
final mangaId = chapter.manga.value!.id;
|
||||
final mangaId = chapter.manga.value?.id;
|
||||
isar.chapters.putSync(chapter..mangaId = mangaId);
|
||||
}
|
||||
//mangaId in Download
|
||||
for (var download in downloads) {
|
||||
final mangaId = download.chapter.value!.manga.value!.id;
|
||||
final mangaId = download.chapter.value?.manga.value?.id;
|
||||
isar.downloads.putSync(download..mangaId = mangaId);
|
||||
}
|
||||
//chapterId and isManga in History
|
||||
for (var history in histories) {
|
||||
final chapterId = history.chapter.value!.id;
|
||||
final isManga = history.chapter.value!.manga.value!.isManga;
|
||||
final chapterId = history.chapter.value?.id;
|
||||
final isManga = history.chapter.value?.manga.value?.isManga;
|
||||
isar.historys.putSync(history
|
||||
..chapterId = chapterId
|
||||
..isManga = isManga);
|
||||
}
|
||||
// isManga in Track
|
||||
for (var track in tracks) {
|
||||
final isManga = isar.mangas.getSync(track.mangaId!)?.isManga;
|
||||
isar.tracks.putSync(track..isManga = isManga);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'migration.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$migrationHash() => r'c37210dc71317d8be30be363f78793ff5c9c6fd1';
|
||||
String _$migrationHash() => r'6e66a54603736b638f834ffa9e2dbe03ec3f494e';
|
||||
|
||||
/// See also [migration].
|
||||
@ProviderFor(migration)
|
||||
|
|
|
|||
|
|
@ -1366,7 +1366,9 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
return Column(
|
||||
children: [
|
||||
Icon(
|
||||
isNotEmpty ? Icons.done_rounded : Icons.sync_outlined,
|
||||
isNotEmpty
|
||||
? Icons.done_rounded
|
||||
: Icons.sync_outlined,
|
||||
size: 20,
|
||||
color: color,
|
||||
),
|
||||
|
|
@ -1688,7 +1690,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
return trackRes!.isNotEmpty
|
||||
? TrackerWidget(
|
||||
mangaId: widget.manga!.id!,
|
||||
trackPreference: entries[index],
|
||||
syncId: entries[index].syncId!,
|
||||
trackRes: trackRes.first,
|
||||
isManga: widget.manga!.isManga!)
|
||||
: TrackListile(
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class TrackState extends _$TrackState {
|
|||
|
||||
ref
|
||||
.read(tracksProvider(syncId: track!.syncId!).notifier)
|
||||
.updateTrackManga(updateTrack!);
|
||||
.updateTrackManga(updateTrack!, isManga);
|
||||
}
|
||||
|
||||
int getScoreMaxValue() {
|
||||
|
|
@ -133,14 +133,12 @@ class TrackState extends _$TrackState {
|
|||
.read(anilistProvider(syncId: syncId, isManga: isManga).notifier)
|
||||
.findLibAnime(track);
|
||||
findManga ??= isManga!
|
||||
? await ref
|
||||
.read(
|
||||
anilistProvider(syncId: syncId, isManga: isManga).notifier)
|
||||
.addLibManga(track)
|
||||
: await ref
|
||||
.read(
|
||||
anilistProvider(syncId: syncId, isManga: isManga).notifier)
|
||||
.addLibAnime(track);
|
||||
? await ref
|
||||
.read(anilistProvider(syncId: syncId, isManga: isManga).notifier)
|
||||
.addLibManga(track)
|
||||
: await ref
|
||||
.read(anilistProvider(syncId: syncId, isManga: isManga).notifier)
|
||||
.addLibAnime(track);
|
||||
} else if (syncId == 3) {
|
||||
findManga = isManga!
|
||||
? await ref
|
||||
|
|
@ -152,7 +150,7 @@ class TrackState extends _$TrackState {
|
|||
}
|
||||
ref
|
||||
.read(tracksProvider(syncId: syncId).notifier)
|
||||
.updateTrackManga(findManga!);
|
||||
.updateTrackManga(findManga!, isManga);
|
||||
}
|
||||
|
||||
List<TrackStatus> getStatusList() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_search_widget.dart';
|
||||
|
|
@ -18,13 +17,15 @@ class TrackerWidget extends ConsumerStatefulWidget {
|
|||
final bool isManga;
|
||||
final Track trackRes;
|
||||
final int mangaId;
|
||||
final TrackPreference trackPreference;
|
||||
final int syncId;
|
||||
final bool hide;
|
||||
const TrackerWidget(
|
||||
{super.key,
|
||||
required this.isManga,
|
||||
required this.trackPreference,
|
||||
required this.syncId,
|
||||
required this.trackRes,
|
||||
required this.mangaId});
|
||||
required this.mangaId,
|
||||
this.hide = false});
|
||||
|
||||
@override
|
||||
ConsumerState<TrackerWidget> createState() => _TrackerWidgetState();
|
||||
|
|
@ -46,8 +47,8 @@ class _TrackerWidgetState extends ConsumerState<TrackerWidget> {
|
|||
.findManga();
|
||||
if (mounted) {
|
||||
ref
|
||||
.read(tracksProvider(syncId: widget.trackPreference.syncId!).notifier)
|
||||
.updateTrackManga(findManga!);
|
||||
.read(tracksProvider(syncId: widget.syncId).notifier)
|
||||
.updateTrackManga(findManga!, widget.isManga);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,39 +66,43 @@ class _TrackerWidgetState extends ConsumerState<TrackerWidget> {
|
|||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
if (!widget.hide)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 5),
|
||||
child: Container(
|
||||
color: const Color.fromRGBO(18, 25, 35, 1),
|
||||
width: 70,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: trackInfos(widget.syncId).$3),
|
||||
width: 50,
|
||||
height: 45,
|
||||
child: Image.asset(
|
||||
trackInfos(widget.trackPreference.syncId!).$1,
|
||||
trackInfos(widget.syncId).$1,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _elevatedButton(
|
||||
context,
|
||||
borderRadius:
|
||||
const BorderRadius.only(topRight: Radius.circular(20)),
|
||||
onPressed: () async {
|
||||
final trackSearch = await trackersSearchraggableMenu(
|
||||
context,
|
||||
isManga: widget.isManga,
|
||||
track: widget.trackRes) as TrackSearch?;
|
||||
if (trackSearch != null) {
|
||||
await ref
|
||||
.read(trackStateProvider(
|
||||
track: null, isManga: widget.isManga)
|
||||
.notifier)
|
||||
.setTrackSearch(trackSearch, widget.mangaId,
|
||||
widget.trackPreference.syncId!);
|
||||
}
|
||||
},
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(20),
|
||||
topLeft: Radius.circular(20)),
|
||||
onPressed: !widget.hide
|
||||
? () async {
|
||||
final trackSearch = await trackersSearchraggableMenu(
|
||||
context,
|
||||
isManga: widget.isManga,
|
||||
track: widget.trackRes) as TrackSearch?;
|
||||
if (trackSearch != null) {
|
||||
await ref
|
||||
.read(trackStateProvider(
|
||||
track: null, isManga: widget.isManga)
|
||||
.notifier)
|
||||
.setTrackSearch(
|
||||
trackSearch, widget.mangaId, widget.syncId);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
|
@ -120,8 +125,7 @@ class _TrackerWidgetState extends ConsumerState<TrackerWidget> {
|
|||
IconButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(tracksProvider(
|
||||
syncId: widget.trackPreference.syncId!)
|
||||
.read(tracksProvider(syncId: widget.syncId)
|
||||
.notifier)
|
||||
.deleteTrackManga(widget.trackRes);
|
||||
},
|
||||
|
|
@ -445,7 +449,7 @@ class _TrackerWidgetState extends ConsumerState<TrackerWidget> {
|
|||
}
|
||||
|
||||
Widget _elevatedButton(BuildContext context,
|
||||
{required VoidCallback onPressed,
|
||||
{required Function()? onPressed,
|
||||
String text = "",
|
||||
Widget? child,
|
||||
BorderRadiusGeometry? borderRadius}) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
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/track_preference.dart';
|
||||
import 'package:mangayomi/modules/widgets/gridview_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/media_query.dart';
|
||||
|
||||
class ManageTrackersScreen extends StatefulWidget {
|
||||
const ManageTrackersScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ManageTrackersScreen> createState() => _ManageTrackersScreenState();
|
||||
}
|
||||
|
||||
class _ManageTrackersScreenState extends State<ManageTrackersScreen> {
|
||||
late List<TrackPreference> trackPreferences = [];
|
||||
@override
|
||||
void initState() {
|
||||
trackPreferences =
|
||||
isar.trackPreferences.filter().syncIdIsNotNull().findAllSync();
|
||||
// trackPreferences.insert(0, TrackPreference(syncId: -1));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(context.l10n.manage_trackers)),
|
||||
body: GridViewWidget(
|
||||
childAspectRatio: 0.69,
|
||||
itemCount: trackPreferences.length,
|
||||
itemBuilder: (context, index) {
|
||||
final trackerPref = trackPreferences[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
onPressed: () {
|
||||
context.push('/trackingDetail', extra: trackerPref);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: trackerPref.syncId == -1
|
||||
? Colors.grey
|
||||
: trackInfos(trackerPref.syncId!).$3,
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
child: trackerPref.syncId == -1
|
||||
? SizedBox(
|
||||
width: mediaWidth(context, 1),
|
||||
child: const Icon(Icons.local_library_rounded,
|
||||
size: 60))
|
||||
: Image.asset(
|
||||
trackInfos(trackerPref.syncId!).$1)),
|
||||
)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
trackerPref.syncId == -1
|
||||
? 'Local'
|
||||
: trackInfos(trackerPref.syncId!).$2,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 19)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/tracker_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
|
||||
class TrackingDetail extends StatefulWidget {
|
||||
final TrackPreference trackerPref;
|
||||
const TrackingDetail({super.key, required this.trackerPref});
|
||||
|
||||
@override
|
||||
State<TrackingDetail> createState() => _TrackingDetailState();
|
||||
}
|
||||
|
||||
class _TrackingDetailState extends State<TrackingDetail>
|
||||
with TickerProviderStateMixin {
|
||||
late TabController _tabBarController;
|
||||
@override
|
||||
void initState() {
|
||||
_tabBarController = TabController(length: 2, vsync: this);
|
||||
_tabBarController.animateTo(0);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return DefaultTabController(
|
||||
animationDuration: Duration.zero,
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
title: Text(widget.trackerPref.syncId == -1
|
||||
? 'Local'
|
||||
: trackInfos(widget.trackerPref.syncId!).$2),
|
||||
bottom: TabBar(
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
controller: _tabBarController,
|
||||
tabs: [
|
||||
Tab(text: l10n.manga),
|
||||
Tab(text: l10n.anime),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: TabBarView(controller: _tabBarController, children: [
|
||||
TrackingTab(isManga: true, syncId: widget.trackerPref.syncId!),
|
||||
TrackingTab(isManga: false, syncId: widget.trackerPref.syncId!)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrackingTab extends StatelessWidget {
|
||||
final bool isManga;
|
||||
final int syncId;
|
||||
const TrackingTab({super.key, required this.isManga, required this.syncId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: isar.tracks
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.isMangaEqualTo(isManga)
|
||||
.syncIdEqualTo(syncId)
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
List<Track>? trackRes = snapshot.hasData ? snapshot.data : [];
|
||||
final mediaIds = trackRes!.map((e) => e.mediaId).toSet().toList();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemCount: mediaIds.length,
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final mediaId = mediaIds[index];
|
||||
final track = trackRes
|
||||
.firstWhere((element) => element.mediaId == mediaId);
|
||||
return ExpansionTile(
|
||||
title: Text(track.title!),
|
||||
children: [
|
||||
TrackingWidget(
|
||||
isManga: isManga, syncId: syncId, mediaId: mediaId!)
|
||||
],
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) {
|
||||
return const Divider();
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class TrackingWidget extends StatelessWidget {
|
||||
final int syncId;
|
||||
final bool isManga;
|
||||
final int mediaId;
|
||||
const TrackingWidget(
|
||||
{super.key,
|
||||
required this.mediaId,
|
||||
required this.isManga,
|
||||
required this.syncId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: isar.tracks
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.mediaIdEqualTo(mediaId)
|
||||
.isMangaEqualTo(isManga)
|
||||
.watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
List<Track>? trackRes = [];
|
||||
List<Track> res = snapshot.data ?? [];
|
||||
for (var track in res) {
|
||||
if (!trackRes
|
||||
.map((e) => e.mediaId)
|
||||
.toList()
|
||||
.contains(track.mediaId)) {
|
||||
trackRes.add(track);
|
||||
}
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemCount: trackRes.length,
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final track = trackRes[index];
|
||||
return TrackerWidget(
|
||||
mangaId: track.mangaId!,
|
||||
syncId: track.syncId!,
|
||||
trackRes: track,
|
||||
isManga: isManga,
|
||||
hide: true,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) {
|
||||
return const Divider();
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ class Tracks extends _$Tracks {
|
|||
});
|
||||
}
|
||||
|
||||
void updateTrackManga(Track track) {
|
||||
void updateTrackManga(Track track, bool? isManga) {
|
||||
final tra = isar.tracks
|
||||
.filter()
|
||||
.syncIdEqualTo(syncId)
|
||||
|
|
@ -36,7 +36,9 @@ class Tracks extends _$Tracks {
|
|||
}
|
||||
}
|
||||
|
||||
isar.writeTxnSync(() => isar.tracks.putSync(track..syncId = syncId));
|
||||
isar.writeTxnSync(() => isar.tracks.putSync(track
|
||||
..syncId = syncId
|
||||
..isManga = isManga));
|
||||
}
|
||||
|
||||
void deleteTrackManga(Track track) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart';
|
||||
import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/trackers/anilist.dart';
|
||||
import 'package:mangayomi/services/trackers/kitsu.dart';
|
||||
|
|
@ -44,7 +46,6 @@ class TrackScreen extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
TrackListile(
|
||||
color: const Color.fromRGBO(18, 25, 35, 1),
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(anilistProvider(syncId: 2).notifier)
|
||||
|
|
@ -53,14 +54,12 @@ class TrackScreen extends ConsumerWidget {
|
|||
id: 2,
|
||||
entries: entries!),
|
||||
TrackListile(
|
||||
color: const Color.fromRGBO(51, 37, 50, 1),
|
||||
onTap: () async {
|
||||
_showDialogLogin(context, ref);
|
||||
},
|
||||
id: 3,
|
||||
entries: entries),
|
||||
TrackListile(
|
||||
color: const Color.fromRGBO(46, 81, 162, 1),
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(myAnimeListProvider(syncId: 1, isManga: null)
|
||||
|
|
@ -84,7 +83,11 @@ class TrackScreen extends ConsumerWidget {
|
|||
subtitle: Text(l10n.tracking_warning_info,
|
||||
style: TextStyle(
|
||||
fontSize: 11, color: secondaryColor(context))),
|
||||
)
|
||||
),
|
||||
ListTileWidget(
|
||||
title: l10n.manage_trackers,
|
||||
icon: Icons.settings,
|
||||
onTap: () => context.push('/manageTrackers')),
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -11,13 +11,11 @@ class TrackListile extends ConsumerWidget {
|
|||
final int id;
|
||||
final List<TrackPreference> entries;
|
||||
final String? text;
|
||||
final Color? color;
|
||||
const TrackListile(
|
||||
{super.key,
|
||||
required this.onTap,
|
||||
required this.id,
|
||||
required this.entries,
|
||||
this.color,
|
||||
this.text});
|
||||
|
||||
@override
|
||||
|
|
@ -31,9 +29,8 @@ class TrackListile extends ConsumerWidget {
|
|||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
leading: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: color,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: trackInfos(id).$3),
|
||||
width: 60,
|
||||
height: 70,
|
||||
child: Image.asset(
|
||||
|
|
@ -72,9 +69,11 @@ class TrackListile extends ConsumerWidget {
|
|||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel,
|
||||
style:
|
||||
TextStyle(color: secondaryColor(context)),)),
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: TextStyle(
|
||||
color: secondaryColor(context)),
|
||||
)),
|
||||
const SizedBox(width: 15),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
|
|
@ -82,14 +81,15 @@ class TrackListile extends ConsumerWidget {
|
|||
Colors.red.withOpacity(0.7)),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(tracksProvider(syncId: id).notifier)
|
||||
.read(
|
||||
tracksProvider(syncId: id).notifier)
|
||||
.logout();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.log_out,
|
||||
style:
|
||||
TextStyle(color: secondaryColor(context)),
|
||||
style: TextStyle(
|
||||
color: secondaryColor(context)),
|
||||
)),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/modules/anime/anime_player_view.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/extension_detail.dart';
|
||||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
|
|
@ -10,6 +11,8 @@ import 'package:mangayomi/modules/more/backup_and_restore/backup_and_restore.dar
|
|||
import 'package:mangayomi/modules/more/categories/categories_screen.dart';
|
||||
import 'package:mangayomi/modules/more/settings/downloads/downloads_screen.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/track.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/manage_trackers/manage_trackers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/manage_trackers/tracking_detail.dart';
|
||||
import 'package:mangayomi/modules/updates/updates_screen.dart';
|
||||
import 'package:mangayomi/modules/webview/webview.dart';
|
||||
import 'package:mangayomi/modules/browse/browse_screen.dart';
|
||||
|
|
@ -39,7 +42,7 @@ GoRouter router(RouterRef ref) {
|
|||
return GoRouter(
|
||||
observers: [BotToastNavigatorObserver()],
|
||||
initialLocation: '/MangaLibrary',
|
||||
debugLogDiagnostics: false,
|
||||
debugLogDiagnostics: true,
|
||||
refreshListenable: router,
|
||||
routes: router._routes,
|
||||
);
|
||||
|
|
@ -453,6 +456,34 @@ class RouterNotifier extends ChangeNotifier {
|
|||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/manageTrackers",
|
||||
name: "manageTrackers",
|
||||
builder: (context, state) {
|
||||
return const ManageTrackersScreen();
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
return CustomTransition(
|
||||
key: state.pageKey,
|
||||
child: const ManageTrackersScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/trackingDetail",
|
||||
name: "trackingDetail",
|
||||
builder: (context, state) {
|
||||
final trackerPref = state.extra as TrackPreference;
|
||||
return TrackingDetail(trackerPref: trackerPref);
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
final trackerPref = state.extra as TrackPreference;
|
||||
return CustomTransition(
|
||||
key: state.pageKey,
|
||||
child: TrackingDetail(trackerPref: trackerPref),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -473,11 +504,3 @@ Route createRoute({required Widget page}) {
|
|||
},
|
||||
);
|
||||
}
|
||||
// I/vrf ( 6408): 4<EFBFBD>~
|
||||
// I/URL_SAFE( 6408): NJV-
|
||||
// I/DEFAULT ( 6408): TkpWLQ==
|
||||
// I/shift ( 6408): QnlYJVAB
|
||||
// I/shift_default( 6408): UW5sWUpWQUI=
|
||||
// I/rot13 ( 6408): HJ5fJHcJDHV=
|
||||
// I/vrfEncrypt( 6408): HJ5fJHcJDHV=
|
||||
// I/flutter ( 6408): vrf=HJ5fJHcJDHV%3D%0A
|
||||
|
|
@ -6,7 +6,7 @@ part of 'anilist.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$anilistHash() => r'0fd8945c8705e69d1609d9d5c6ccb5bacd84149e';
|
||||
String _$anilistHash() => r'ee83d9fc6e85366bb7cbce876b0878207829a979';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -55,11 +55,23 @@ TrackStatus toTrackStatus(TrackStatus status, bool isManga, int syncId) {
|
|||
: status;
|
||||
}
|
||||
|
||||
(String, String) trackInfos(int id) {
|
||||
(String, String, Color) trackInfos(int id) {
|
||||
return switch (id) {
|
||||
1 => ("assets/trackers_icons/tracker_mal.webp", "MyAnimeList"),
|
||||
2 => ("assets/trackers_icons/tracker_anilist.webp", "Anilist"),
|
||||
_ => ("assets/trackers_icons/tracker_kitsu.webp", "Kitsu"),
|
||||
1 => (
|
||||
"assets/trackers_icons/tracker_mal.webp",
|
||||
"MyAnimeList",
|
||||
const Color.fromRGBO(46, 81, 162, 1)
|
||||
),
|
||||
2 => (
|
||||
"assets/trackers_icons/tracker_anilist.webp",
|
||||
"Anilist",
|
||||
const Color.fromRGBO(51, 37, 50, 1)
|
||||
),
|
||||
_ => (
|
||||
"assets/trackers_icons/tracker_kitsu.webp",
|
||||
"Kitsu",
|
||||
const Color.fromRGBO(18, 25, 35, 1)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue