added feed feature
This commit is contained in:
parent
45a0d5c9db
commit
85aa687606
15 changed files with 893 additions and 22 deletions
|
|
@ -3,6 +3,7 @@
|
|||
"library": "Library",
|
||||
"updates": "Updates",
|
||||
"history": "History",
|
||||
"feed": "Feed",
|
||||
"browse": "Browse",
|
||||
"more": "More",
|
||||
"open_random_entry": "Open random entry",
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
"no_recent_updates": "No recent updates",
|
||||
"remove_everything": "Remove everything",
|
||||
"remove_everything_msg": "Are you sure? All history will be lost",
|
||||
"remove_all_feed_msg": "Are you sure? The whole feed will be cleared",
|
||||
"ok": "OK",
|
||||
"cancel": "Cancel",
|
||||
"remove": "Remove",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ class Feed {
|
|||
|
||||
int? mangaId;
|
||||
|
||||
String? chapterName;
|
||||
|
||||
final chapter = IsarLink<Chapter>();
|
||||
|
||||
String? date;
|
||||
|
|
@ -16,18 +18,21 @@ class Feed {
|
|||
Feed({
|
||||
this.id = Isar.autoIncrement,
|
||||
required this.mangaId,
|
||||
required this.chapterName,
|
||||
required this.date,
|
||||
});
|
||||
|
||||
Feed.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
mangaId = json['mangaId'];
|
||||
mangaId = json['chapterName'];
|
||||
date = json['date'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'mangaId': mangaId,
|
||||
'chapterName': chapterName,
|
||||
'date': date,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,18 @@ const FeedSchema = CollectionSchema(
|
|||
name: r'Feed',
|
||||
id: 8879644747771893978,
|
||||
properties: {
|
||||
r'date': PropertySchema(
|
||||
r'chapterName': PropertySchema(
|
||||
id: 0,
|
||||
name: r'chapterName',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'date': PropertySchema(
|
||||
id: 1,
|
||||
name: r'date',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'mangaId': PropertySchema(
|
||||
id: 1,
|
||||
id: 2,
|
||||
name: r'mangaId',
|
||||
type: IsarType.long,
|
||||
)
|
||||
|
|
@ -55,6 +60,12 @@ int _feedEstimateSize(
|
|||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.chapterName;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
{
|
||||
final value = object.date;
|
||||
if (value != null) {
|
||||
|
|
@ -70,8 +81,9 @@ void _feedSerialize(
|
|||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeString(offsets[0], object.date);
|
||||
writer.writeLong(offsets[1], object.mangaId);
|
||||
writer.writeString(offsets[0], object.chapterName);
|
||||
writer.writeString(offsets[1], object.date);
|
||||
writer.writeLong(offsets[2], object.mangaId);
|
||||
}
|
||||
|
||||
Feed _feedDeserialize(
|
||||
|
|
@ -81,9 +93,10 @@ Feed _feedDeserialize(
|
|||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = Feed(
|
||||
date: reader.readStringOrNull(offsets[0]),
|
||||
chapterName: reader.readStringOrNull(offsets[0]),
|
||||
date: reader.readStringOrNull(offsets[1]),
|
||||
id: id,
|
||||
mangaId: reader.readLongOrNull(offsets[1]),
|
||||
mangaId: reader.readLongOrNull(offsets[2]),
|
||||
);
|
||||
return object;
|
||||
}
|
||||
|
|
@ -98,6 +111,8 @@ P _feedDeserializeProp<P>(
|
|||
case 0:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
|
|
@ -193,6 +208,152 @@ extension FeedQueryWhere on QueryBuilder<Feed, Feed, QWhereClause> {
|
|||
}
|
||||
|
||||
extension FeedQueryFilter on QueryBuilder<Feed, Feed, QFilterCondition> {
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'chapterName',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'chapterName',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameEqualTo(
|
||||
String? value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'chapterName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'chapterName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'chapterName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'chapterName',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'chapterName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'chapterName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameContains(
|
||||
String value,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'chapterName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameMatches(
|
||||
String pattern,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'chapterName',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'chapterName',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> chapterNameIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'chapterName',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterFilterCondition> dateIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
|
|
@ -492,6 +653,18 @@ extension FeedQueryLinks on QueryBuilder<Feed, Feed, QFilterCondition> {
|
|||
}
|
||||
|
||||
extension FeedQuerySortBy on QueryBuilder<Feed, Feed, QSortBy> {
|
||||
QueryBuilder<Feed, Feed, QAfterSortBy> sortByChapterName() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'chapterName', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterSortBy> sortByChapterNameDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'chapterName', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterSortBy> sortByDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'date', Sort.asc);
|
||||
|
|
@ -518,6 +691,18 @@ extension FeedQuerySortBy on QueryBuilder<Feed, Feed, QSortBy> {
|
|||
}
|
||||
|
||||
extension FeedQuerySortThenBy on QueryBuilder<Feed, Feed, QSortThenBy> {
|
||||
QueryBuilder<Feed, Feed, QAfterSortBy> thenByChapterName() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'chapterName', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterSortBy> thenByChapterNameDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'chapterName', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QAfterSortBy> thenByDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'date', Sort.asc);
|
||||
|
|
@ -556,6 +741,13 @@ extension FeedQuerySortThenBy on QueryBuilder<Feed, Feed, QSortThenBy> {
|
|||
}
|
||||
|
||||
extension FeedQueryWhereDistinct on QueryBuilder<Feed, Feed, QDistinct> {
|
||||
QueryBuilder<Feed, Feed, QDistinct> distinctByChapterName(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'chapterName', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, Feed, QDistinct> distinctByDate(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
|
|
@ -577,6 +769,12 @@ extension FeedQueryProperty on QueryBuilder<Feed, Feed, QQueryProperty> {
|
|||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, String?, QQueryOperations> chapterNameProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'chapterName');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Feed, String?, QQueryOperations> dateProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'date');
|
||||
|
|
|
|||
361
lib/modules/feed/feed_screen.dart
Normal file
361
lib/modules/feed/feed_screen.dart
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:grouped_list/sliver_grouped_list.dart';
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/feed.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/history/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/cached_network.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
import 'package:mangayomi/utils/extensions/chapter.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
import 'package:mangayomi/modules/library/widgets/search_text_form_field.dart';
|
||||
import 'package:mangayomi/modules/widgets/error_text.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
|
||||
class FeedScreen extends ConsumerStatefulWidget {
|
||||
const FeedScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<FeedScreen> createState() => _FeedScreenState();
|
||||
}
|
||||
|
||||
class _FeedScreenState extends ConsumerState<FeedScreen>
|
||||
with TickerProviderStateMixin {
|
||||
late TabController _tabBarController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_tabBarController = TabController(length: 2, vsync: this);
|
||||
_tabBarController.animateTo(0);
|
||||
_tabBarController.addListener(() {
|
||||
setState(() {
|
||||
_textEditingController.clear();
|
||||
_isSearch = false;
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
final _textEditingController = TextEditingController();
|
||||
bool _isSearch = false;
|
||||
List<History> entriesData = [];
|
||||
@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: _isSearch
|
||||
? null
|
||||
: Text(
|
||||
l10n.feed,
|
||||
style: TextStyle(color: Theme.of(context).hintColor),
|
||||
),
|
||||
actions: [
|
||||
_isSearch
|
||||
? SeachFormTextField(
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
onSuffixPressed: () {
|
||||
_textEditingController.clear();
|
||||
setState(() {});
|
||||
},
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isSearch = false;
|
||||
});
|
||||
_textEditingController.clear();
|
||||
},
|
||||
controller: _textEditingController,
|
||||
)
|
||||
: IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isSearch = true;
|
||||
});
|
||||
},
|
||||
icon:
|
||||
Icon(Icons.search, color: Theme.of(context).hintColor)),
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.remove_everything,
|
||||
),
|
||||
content: Text(l10n.remove_all_feed_msg),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(l10n.cancel)),
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
List<Feed> feeds = isar.feeds
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.chapter((q) => q.manga((q) =>
|
||||
q.isMangaEqualTo(
|
||||
_tabBarController.index ==
|
||||
0)))
|
||||
.findAllSync()
|
||||
.toList();
|
||||
isar.writeTxnSync(() {
|
||||
for (var feed in feeds) {
|
||||
isar.feeds.deleteSync(feed.id!);
|
||||
}
|
||||
});
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
icon: Icon(Icons.delete_sweep_outlined,
|
||||
color: Theme.of(context).hintColor)),
|
||||
],
|
||||
bottom: TabBar(
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
controller: _tabBarController,
|
||||
tabs: [
|
||||
Tab(text: l10n.manga),
|
||||
Tab(text: l10n.anime),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: TabBarView(controller: _tabBarController, children: [
|
||||
FeedTab(
|
||||
isManga: true,
|
||||
query: _textEditingController.text,
|
||||
),
|
||||
FeedTab(
|
||||
isManga: false,
|
||||
query: _textEditingController.text,
|
||||
)
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FeedTab extends ConsumerStatefulWidget {
|
||||
final String query;
|
||||
final bool isManga;
|
||||
const FeedTab({required this.isManga, required this.query, super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<FeedTab> createState() => _FeedTabState();
|
||||
}
|
||||
|
||||
class _FeedTabState extends ConsumerState<FeedTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final feed =
|
||||
ref.watch(getAllFeedStreamProvider(isManga: widget.isManga));
|
||||
return Scaffold(
|
||||
body: feed.when(
|
||||
data: (data) {
|
||||
final entries = data
|
||||
.where((element) => widget.query.isNotEmpty
|
||||
? element.chapter.value!.manga.value!.name!
|
||||
.toLowerCase()
|
||||
.contains(widget.query.toLowerCase())
|
||||
: true)
|
||||
.toList();
|
||||
|
||||
if (entries.isNotEmpty) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverGroupedListView<Feed, String>(
|
||||
elements: entries,
|
||||
groupBy: (element) => dateFormat(element.date!,
|
||||
context: context,
|
||||
ref: ref,
|
||||
forHistoryValue: true,
|
||||
useRelativeTimesTamps: false),
|
||||
groupSeparatorBuilder: (String groupByValue) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8, left: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(dateFormat(
|
||||
null,
|
||||
context: context,
|
||||
stringDate: groupByValue,
|
||||
ref: ref,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, Feed element) {
|
||||
final manga = element.chapter.value!.manga.value!;
|
||||
final chapter = element.chapter.value!;
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
backgroundColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0)),
|
||||
elevation: 0,
|
||||
shadowColor: Colors.transparent),
|
||||
onPressed: () {
|
||||
chapter.pushToReaderView(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: SizedBox(
|
||||
height: 105,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 60,
|
||||
height: 90,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(7)),
|
||||
),
|
||||
onPressed: () {
|
||||
context.push('/manga-reader/detail',
|
||||
extra: manga.id);
|
||||
},
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
child: manga.customCoverImage != null
|
||||
? Image.memory(
|
||||
manga.customCoverImage as Uint8List)
|
||||
: cachedNetworkImage(
|
||||
headers: ref.watch(headersProvider(
|
||||
source: manga.source!,
|
||||
lang: manga.lang!)),
|
||||
imageUrl: toImgUrl(
|
||||
manga.customCoverFromTracker ??
|
||||
manga.imageUrl ??
|
||||
""),
|
||||
width: 60,
|
||||
height: 90,
|
||||
fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
manga.name!,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.color,
|
||||
fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
Wrap(
|
||||
crossAxisAlignment:
|
||||
WrapCrossAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
chapter.name!,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.color,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
" - ${dateFormatHour(element.date!, context)}",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.color,
|
||||
fontWeight:
|
||||
FontWeight.w400),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemComparator: (item1, item2) =>
|
||||
item1.date!.compareTo(item2.date!),
|
||||
order: GroupedListOrder.DESC,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return Center(
|
||||
child: Text(l10n.nothing_read_recently),
|
||||
);
|
||||
},
|
||||
error: (Object error, StackTrace stackTrace) {
|
||||
return ErrorText(error);
|
||||
},
|
||||
loading: () {
|
||||
return const ProgressCenter();
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/feed.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
|
@ -16,3 +17,14 @@ Stream<List<History>> getAllHistoryStream(GetAllHistoryStreamRef ref,
|
|||
.chapter((q) => q.manga((q) => q.isMangaEqualTo(isManga)))
|
||||
.watch(fireImmediately: true);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Stream<List<Feed>> getAllFeedStream(GetAllFeedStreamRef ref,
|
||||
{required bool isManga}) async* {
|
||||
yield* isar.feeds
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.and()
|
||||
.chapter((q) => q.manga((q) => q.isMangaEqualTo(isManga)))
|
||||
.watch(fireImmediately: true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,5 +157,134 @@ class _GetAllHistoryStreamProviderElement
|
|||
@override
|
||||
bool get isManga => (origin as GetAllHistoryStreamProvider).isManga;
|
||||
}
|
||||
|
||||
String _$getAllFeedStreamHash() => r'3d60bca5377bf6fc2aee36e7bec5b319b2377add';
|
||||
|
||||
/// See also [getAllFeedStream].
|
||||
@ProviderFor(getAllFeedStream)
|
||||
const getAllFeedStreamProvider = GetAllFeedStreamFamily();
|
||||
|
||||
/// See also [getAllFeedStream].
|
||||
class GetAllFeedStreamFamily extends Family<AsyncValue<List<Feed>>> {
|
||||
/// See also [getAllFeedStream].
|
||||
const GetAllFeedStreamFamily();
|
||||
|
||||
/// See also [getAllFeedStream].
|
||||
GetAllFeedStreamProvider call({
|
||||
required bool isManga,
|
||||
}) {
|
||||
return GetAllFeedStreamProvider(
|
||||
isManga: isManga,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
GetAllFeedStreamProvider getProviderOverride(
|
||||
covariant GetAllFeedStreamProvider provider,
|
||||
) {
|
||||
return call(
|
||||
isManga: provider.isManga,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'getAllFeedStreamProvider';
|
||||
}
|
||||
|
||||
/// See also [getAllFeedStream].
|
||||
class GetAllFeedStreamProvider extends AutoDisposeStreamProvider<List<Feed>> {
|
||||
/// See also [getAllFeedStream].
|
||||
GetAllFeedStreamProvider({
|
||||
required bool isManga,
|
||||
}) : this._internal(
|
||||
(ref) => getAllFeedStream(
|
||||
ref as GetAllFeedStreamRef,
|
||||
isManga: isManga,
|
||||
),
|
||||
from: getAllFeedStreamProvider,
|
||||
name: r'getAllFeedStreamProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$getAllFeedStreamHash,
|
||||
dependencies: GetAllFeedStreamFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
GetAllFeedStreamFamily._allTransitiveDependencies,
|
||||
isManga: isManga,
|
||||
);
|
||||
|
||||
GetAllFeedStreamProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.isManga,
|
||||
}) : super.internal();
|
||||
|
||||
final bool isManga;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
Stream<List<Feed>> Function(GetAllFeedStreamRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: GetAllFeedStreamProvider._internal(
|
||||
(ref) => create(ref as GetAllFeedStreamRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
isManga: isManga,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeStreamProviderElement<List<Feed>> createElement() {
|
||||
return _GetAllFeedStreamProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is GetAllFeedStreamProvider && other.isManga == isManga;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, isManga.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
mixin GetAllFeedStreamRef on AutoDisposeStreamProviderRef<List<Feed>> {
|
||||
/// The parameter `isManga` of this provider.
|
||||
bool get isManga;
|
||||
}
|
||||
|
||||
class _GetAllFeedStreamProviderElement
|
||||
extends AutoDisposeStreamProviderElement<List<Feed>>
|
||||
with GetAllFeedStreamRef {
|
||||
_GetAllFeedStreamProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
bool get isManga => (origin as GetAllFeedStreamProvider).isManga;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ 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/feed.dart';
|
||||
import 'package:mangayomi/modules/library/providers/add_torrent.dart';
|
||||
import 'package:mangayomi/modules/library/providers/local_archive.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart';
|
||||
|
|
@ -1164,6 +1165,11 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
|
|||
.notifier)
|
||||
.addUpdatedChapter(
|
||||
chapter, true, false);
|
||||
isar.feeds
|
||||
.filter()
|
||||
.mangaIdEqualTo(chapter.mangaId)
|
||||
.chapterNameEqualTo(chapter.name)
|
||||
.deleteAllSync();
|
||||
isar.chapters.deleteSync(chapter.id!);
|
||||
}
|
||||
ref
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/feed.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/widgets/loading_icon.dart';
|
||||
import 'package:mangayomi/services/fetch_anime_sources.dart';
|
||||
|
|
@ -45,8 +46,9 @@ class MainScreen extends ConsumerWidget {
|
|||
'/MangaLibrary' => 0,
|
||||
'/AnimeLibrary' => 1,
|
||||
'/history' => 2,
|
||||
'/browse' => 3,
|
||||
_ => 4,
|
||||
'/feed' => 3,
|
||||
'/browse' => 4,
|
||||
_ => 5,
|
||||
};
|
||||
|
||||
final incognitoMode = ref.watch(incognitoModeStateProvider);
|
||||
|
|
@ -96,6 +98,7 @@ class MainScreen extends ConsumerWidget {
|
|||
!= '/MangaLibrary' &&
|
||||
!= '/AnimeLibrary' &&
|
||||
!= '/history' &&
|
||||
!= '/feed' &&
|
||||
!= '/browse' &&
|
||||
!= '/more' =>
|
||||
0,
|
||||
|
|
@ -141,6 +144,36 @@ class MainScreen extends ConsumerWidget {
|
|||
padding:
|
||||
const EdgeInsets.only(top: 5),
|
||||
child: Text(l10n.history))),
|
||||
NavigationRailDestination(
|
||||
selectedIcon: Stack(
|
||||
children: [
|
||||
const Icon(Icons.rss_feed),
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: _feedTotalNumbers(
|
||||
ref, false))
|
||||
],
|
||||
),
|
||||
icon: Stack(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.rss_feed_outlined),
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: _feedTotalNumbers(
|
||||
ref, false))
|
||||
],
|
||||
),
|
||||
label: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 5),
|
||||
child: Stack(
|
||||
children: [
|
||||
Text(l10n.feed),
|
||||
],
|
||||
))),
|
||||
NavigationRailDestination(
|
||||
selectedIcon:
|
||||
const Icon(Icons.explore),
|
||||
|
|
@ -169,8 +202,10 @@ class MainScreen extends ConsumerWidget {
|
|||
} else if (newIndex == 2) {
|
||||
route.go('/history');
|
||||
} else if (newIndex == 3) {
|
||||
route.go('/browse');
|
||||
route.go('/feed');
|
||||
} else if (newIndex == 4) {
|
||||
route.go('/browse');
|
||||
} else if (newIndex == 5) {
|
||||
route.go('/more');
|
||||
}
|
||||
},
|
||||
|
|
@ -199,6 +234,7 @@ class MainScreen extends ConsumerWidget {
|
|||
!= '/MangaLibrary' &&
|
||||
!= '/AnimeLibrary' &&
|
||||
!= '/history' &&
|
||||
!= '/feed' &&
|
||||
!= '/browse' &&
|
||||
!= '/more' =>
|
||||
0,
|
||||
|
|
@ -231,6 +267,18 @@ class MainScreen extends ConsumerWidget {
|
|||
selectedIcon: const Icon(Icons.history),
|
||||
icon: const Icon(Icons.history_outlined),
|
||||
label: l10n.history),
|
||||
Stack(
|
||||
children: [
|
||||
NavigationDestination(
|
||||
selectedIcon: const Icon(Icons.rss_feed),
|
||||
icon: const Icon(Icons.rss_feed_outlined),
|
||||
label: l10n.feed),
|
||||
Positioned(
|
||||
right: 14,
|
||||
top: 3,
|
||||
child: _feedTotalNumbers(ref, true)),
|
||||
],
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
NavigationDestination(
|
||||
|
|
@ -315,3 +363,38 @@ Widget _extensionUpdateTotalNumbers(WidgetRef ref) {
|
|||
return Container();
|
||||
});
|
||||
}
|
||||
|
||||
Widget _feedTotalNumbers(WidgetRef ref, bool mobile) {
|
||||
return StreamBuilder(
|
||||
stream: isar.feeds.filter().idIsNotNull().watch(fireImmediately: true),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||
final entries = snapshot.data!.where((element) {
|
||||
if (!element.chapter.isLoaded) {
|
||||
element.chapter.loadSync();
|
||||
}
|
||||
return !(element.chapter.value?.isRead ?? false);
|
||||
}).toList();
|
||||
return entries.isEmpty
|
||||
? Container()
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: const Color.fromARGB(255, 176, 46, 37)),
|
||||
child: Padding(
|
||||
padding: mobile
|
||||
? const EdgeInsets.symmetric(horizontal: 5, vertical: 3)
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 3, vertical: 1),
|
||||
child: Text(
|
||||
entries.length.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Theme.of(context).textTheme.bodySmall!.color),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,10 @@ Future<dynamic> updateMangaDetail(UpdateMangaDetailRef ref,
|
|||
chap.manga.saveSync();
|
||||
final savedChapter = isar.chapters.getSync(chap.id!);
|
||||
if (savedChapter != null) {
|
||||
final feed = Feed(mangaId: mangaId, date: savedChapter.dateUpload)
|
||||
final feed = Feed(
|
||||
mangaId: mangaId,
|
||||
chapterName: savedChapter.name,
|
||||
date: savedChapter.dateUpload)
|
||||
..chapter.value = savedChapter;
|
||||
isar.feeds.putSync(feed);
|
||||
feed.chapter.saveSync();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$updateMangaDetailHash() => r'36381242c5d911c4c656e57f72135f5f1e377198';
|
||||
String _$updateMangaDetailHash() => r'c21ac4f7725b5ac4403902bac07a3b5462488bbd';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import 'dart:convert';
|
|||
import 'package:archive/archive_io.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/eval/dart/model/m_bridge.dart';
|
||||
import 'package:mangayomi/eval/dart/model/source_preference.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/category.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/download.dart';
|
||||
import 'package:mangayomi/models/feed.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
|
|
@ -59,6 +61,8 @@ void doRestore(DoRestoreRef ref,
|
|||
final extensionsPref = (backup["extensions_preferences"] as List?)
|
||||
?.map((e) => SourcePreference.fromJson(e))
|
||||
.toList();
|
||||
final feeds =
|
||||
(backup["feeds"] as List?)?.map((e) => Feed.fromJson(e)).toList();
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.clearSync();
|
||||
|
|
@ -95,6 +99,23 @@ void doRestore(DoRestoreRef ref,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar.feeds.clearSync();
|
||||
if (feeds != null) {
|
||||
final tempChapters =
|
||||
isar.chapters.filter().idIsNotNull().findAllSync().toList();
|
||||
for (var feed in feeds) {
|
||||
final matchingChapter = tempChapters
|
||||
.where((chapter) =>
|
||||
chapter.mangaId == feed.mangaId &&
|
||||
chapter.name == feed.chapterName)
|
||||
.firstOrNull;
|
||||
if (matchingChapter != null) {
|
||||
isar.feeds.putSync(feed..chapter.value = matchingChapter);
|
||||
feed.chapter.saveSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar.categorys.clearSync();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'restore.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$doRestoreHash() => r'3c88ad8ba80c245a4b511961111f7ab79c0d330f';
|
||||
String _$doRestoreHash() => r'823b26bade20d89ae7b7b56a7eb7c25020795b45';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:mangayomi/modules/browse/extension/edit_code.dart';
|
|||
import 'package:mangayomi/modules/browse/extension/extension_detail.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/widgets/create_extension.dart';
|
||||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
import 'package:mangayomi/modules/feed/feed_screen.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/backup_and_restore.dart';
|
||||
import 'package:mangayomi/modules/more/categories/categories_screen.dart';
|
||||
import 'package:mangayomi/modules/more/settings/downloads/downloads_screen.dart';
|
||||
|
|
@ -115,6 +116,15 @@ class RouterNotifier extends ChangeNotifier {
|
|||
child: const HistoryScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: "feed",
|
||||
path: '/feed',
|
||||
builder: (context, state) => const FeedScreen(),
|
||||
pageBuilder: (context, state) => transitionPage(
|
||||
key: state.pageKey,
|
||||
child: const FeedScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: "browse",
|
||||
path: '/browse',
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/eval/dart/model/m_bridge.dart';
|
||||
import 'package:mangayomi/eval/dart/model/source_preference.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed_items.dart';
|
||||
import 'package:mangayomi/models/feed.dart';
|
||||
import 'package:mangayomi/models/sync_preference.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
|
|
@ -184,10 +183,9 @@ class SyncServer extends _$SyncServer {
|
|||
return;
|
||||
}
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
_restore(
|
||||
jsonData["backupData"] is String
|
||||
? jsonDecode(jsonData["backupData"])
|
||||
: jsonData["backupData"]);
|
||||
_restore(jsonData["backupData"] is String
|
||||
? jsonDecode(jsonData["backupData"])
|
||||
: jsonData["backupData"]);
|
||||
ref
|
||||
.read(synchingProvider(syncId: syncId).notifier)
|
||||
.setLastDownload(DateTime.now().millisecondsSinceEpoch);
|
||||
|
|
@ -208,6 +206,7 @@ class SyncServer extends _$SyncServer {
|
|||
datas["chapters"] = data["chapters"];
|
||||
datas["tracks"] = data["tracks"];
|
||||
datas["history"] = data["history"];
|
||||
datas["feeds"] = data["feeds"];
|
||||
var encodedJson = jsonEncode(datas);
|
||||
return sha256.convert(utf8.encode(encodedJson)).toString();
|
||||
}
|
||||
|
|
@ -297,6 +296,13 @@ class SyncServer extends _$SyncServer {
|
|||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"extensions_preferences": sourcePreferences});
|
||||
final feeds = isar.feeds
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"feeds": feeds});
|
||||
return datas;
|
||||
}
|
||||
|
||||
|
|
@ -316,6 +322,8 @@ class SyncServer extends _$SyncServer {
|
|||
final history = (backup["history"] as List?)
|
||||
?.map((e) => History.fromJson(e))
|
||||
.toList();
|
||||
final feeds =
|
||||
(backup["feeds"] as List?)?.map((e) => Feed.fromJson(e)).toList();
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.clearSync();
|
||||
|
|
@ -341,6 +349,23 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar.feeds.clearSync();
|
||||
if (feeds != null) {
|
||||
final tempChapters =
|
||||
isar.chapters.filter().idIsNotNull().findAllSync().toList();
|
||||
for (var feed in feeds) {
|
||||
final matchingChapter = tempChapters
|
||||
.where((chapter) =>
|
||||
chapter.mangaId == feed.mangaId &&
|
||||
chapter.name == feed.chapterName)
|
||||
.firstOrNull;
|
||||
if (matchingChapter != null) {
|
||||
isar.feeds.putSync(feed..chapter.value = matchingChapter);
|
||||
feed.chapter.saveSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar.categorys.clearSync();
|
||||
|
|
@ -369,7 +394,6 @@ class SyncServer extends _$SyncServer {
|
|||
void _restore(Map<String, dynamic> backup) {
|
||||
if (backup['version'] == "1") {
|
||||
try {
|
||||
log("DEBUG: ${jsonEncode(backup["version"])}");
|
||||
final manga =
|
||||
(backup["manga"] as List?)?.map((e) => Manga.fromJson(e)).toList();
|
||||
final chapters = (backup["chapters"] as List?)
|
||||
|
|
@ -392,8 +416,8 @@ class SyncServer extends _$SyncServer {
|
|||
final extensionsPref = (backup["extensions_preferences"] as List?)
|
||||
?.map((e) => SourcePreference.fromJson(e))
|
||||
.toList();
|
||||
log("DEBUG 1: ${jsonEncode(backup["manga"])}");
|
||||
log("DEBUG 2: ${jsonEncode(manga)}");
|
||||
final feeds =
|
||||
(backup["feeds"] as List?)?.map((e) => Feed.fromJson(e)).toList();
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.clearSync();
|
||||
|
|
@ -419,6 +443,23 @@ class SyncServer extends _$SyncServer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar.feeds.clearSync();
|
||||
if (feeds != null) {
|
||||
final tempChapters =
|
||||
isar.chapters.filter().idIsNotNull().findAllSync().toList();
|
||||
for (var feed in feeds) {
|
||||
final matchingChapter = tempChapters
|
||||
.where((chapter) =>
|
||||
chapter.mangaId == feed.mangaId &&
|
||||
chapter.name == feed.chapterName)
|
||||
.firstOrNull;
|
||||
if (matchingChapter != null) {
|
||||
isar.feeds.putSync(feed..chapter.value = matchingChapter);
|
||||
feed.chapter.saveSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar.categorys.clearSync();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_server.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$syncServerHash() => r'0eda7252078d155750e8adc4fa9cefec80041faf';
|
||||
String _$syncServerHash() => r'e019e8870184d25f7a2659e35f6c3969bc683b50';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
Loading…
Reference in a new issue