feature: pinned , last used source + fix

This commit is contained in:
kodjomoustapha 2023-05-27 20:34:05 +01:00
parent bc47bf00f0
commit dfd82b32f6
19 changed files with 586 additions and 214 deletions

View file

@ -94,7 +94,7 @@ class Settings {
this.dateFormat = "M/d/y",
this.relativeTimesTamps = 2,
this.flexSchemeColorIndex = 2,
this.themeIsDark = true,
this.themeIsDark = false,
this.incognitoMode = false,
this.chapterPageUrlsList,
this.showPagesNumber = true,

View file

@ -16,6 +16,8 @@ class Source {
bool? isAdded;
bool? isPinned;
bool? isNsfw;
@enumerated
@ -27,6 +29,8 @@ class Source {
bool? isCloudflare;
bool? lastUsed;
String? dateFormat;
String? dateFormatLocale;
@ -47,6 +51,8 @@ class Source {
this.isNsfw = false,
this.isFullData = false,
this.isCloudflare = false,
this.isPinned = false,
this.lastUsed = false,
this.apiUrl = "",
});
}

View file

@ -62,23 +62,33 @@ const SourceSchema = CollectionSchema(
name: r'isNsfw',
type: IsarType.bool,
),
r'lang': PropertySchema(
r'isPinned': PropertySchema(
id: 9,
name: r'isPinned',
type: IsarType.bool,
),
r'lang': PropertySchema(
id: 10,
name: r'lang',
type: IsarType.string,
),
r'lastUsed': PropertySchema(
id: 11,
name: r'lastUsed',
type: IsarType.bool,
),
r'logoUrl': PropertySchema(
id: 10,
id: 12,
name: r'logoUrl',
type: IsarType.string,
),
r'sourceName': PropertySchema(
id: 11,
id: 13,
name: r'sourceName',
type: IsarType.string,
),
r'typeSource': PropertySchema(
id: 12,
id: 14,
name: r'typeSource',
type: IsarType.byte,
enumMap: _SourcetypeSourceEnumValueMap,
@ -164,10 +174,12 @@ void _sourceSerialize(
writer.writeBool(offsets[6], object.isCloudflare);
writer.writeBool(offsets[7], object.isFullData);
writer.writeBool(offsets[8], object.isNsfw);
writer.writeString(offsets[9], object.lang);
writer.writeString(offsets[10], object.logoUrl);
writer.writeString(offsets[11], object.sourceName);
writer.writeByte(offsets[12], object.typeSource.index);
writer.writeBool(offsets[9], object.isPinned);
writer.writeString(offsets[10], object.lang);
writer.writeBool(offsets[11], object.lastUsed);
writer.writeString(offsets[12], object.logoUrl);
writer.writeString(offsets[13], object.sourceName);
writer.writeByte(offsets[14], object.typeSource.index);
}
Source _sourceDeserialize(
@ -187,11 +199,13 @@ Source _sourceDeserialize(
isCloudflare: reader.readBoolOrNull(offsets[6]),
isFullData: reader.readBoolOrNull(offsets[7]),
isNsfw: reader.readBoolOrNull(offsets[8]),
lang: reader.readStringOrNull(offsets[9]),
logoUrl: reader.readStringOrNull(offsets[10]),
sourceName: reader.readStringOrNull(offsets[11]),
isPinned: reader.readBoolOrNull(offsets[9]),
lang: reader.readStringOrNull(offsets[10]),
lastUsed: reader.readBoolOrNull(offsets[11]),
logoUrl: reader.readStringOrNull(offsets[12]),
sourceName: reader.readStringOrNull(offsets[13]),
typeSource:
_SourcetypeSourceValueEnumMap[reader.readByteOrNull(offsets[12])] ??
_SourcetypeSourceValueEnumMap[reader.readByteOrNull(offsets[14])] ??
TypeSource.single,
);
return object;
@ -223,12 +237,16 @@ P _sourceDeserializeProp<P>(
case 8:
return (reader.readBoolOrNull(offset)) as P;
case 9:
return (reader.readStringOrNull(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 10:
return (reader.readStringOrNull(offset)) as P;
case 11:
return (reader.readStringOrNull(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 12:
return (reader.readStringOrNull(offset)) as P;
case 13:
return (reader.readStringOrNull(offset)) as P;
case 14:
return (_SourcetypeSourceValueEnumMap[reader.readByteOrNull(offset)] ??
TypeSource.single) as P;
default:
@ -1128,6 +1146,32 @@ extension SourceQueryFilter on QueryBuilder<Source, Source, QFilterCondition> {
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> isPinnedIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'isPinned',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> isPinnedIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'isPinned',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> isPinnedEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'isPinned',
value: value,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> langIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -1273,6 +1317,32 @@ extension SourceQueryFilter on QueryBuilder<Source, Source, QFilterCondition> {
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> lastUsedIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'lastUsed',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> lastUsedIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'lastUsed',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> lastUsedEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'lastUsed',
value: value,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> logoUrlIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -1732,6 +1802,18 @@ extension SourceQuerySortBy on QueryBuilder<Source, Source, QSortBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByIsPinned() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isPinned', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByIsPinnedDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isPinned', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByLang() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lang', Sort.asc);
@ -1744,6 +1826,18 @@ extension SourceQuerySortBy on QueryBuilder<Source, Source, QSortBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByLastUsed() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lastUsed', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByLastUsedDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lastUsed', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByLogoUrl() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'logoUrl', Sort.asc);
@ -1902,6 +1996,18 @@ extension SourceQuerySortThenBy on QueryBuilder<Source, Source, QSortThenBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByIsPinned() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isPinned', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByIsPinnedDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isPinned', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByLang() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lang', Sort.asc);
@ -1914,6 +2020,18 @@ extension SourceQuerySortThenBy on QueryBuilder<Source, Source, QSortThenBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByLastUsed() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lastUsed', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByLastUsedDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'lastUsed', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByLogoUrl() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'logoUrl', Sort.asc);
@ -2011,6 +2129,12 @@ extension SourceQueryWhereDistinct on QueryBuilder<Source, Source, QDistinct> {
});
}
QueryBuilder<Source, Source, QDistinct> distinctByIsPinned() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isPinned');
});
}
QueryBuilder<Source, Source, QDistinct> distinctByLang(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -2018,6 +2142,12 @@ extension SourceQueryWhereDistinct on QueryBuilder<Source, Source, QDistinct> {
});
}
QueryBuilder<Source, Source, QDistinct> distinctByLastUsed() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'lastUsed');
});
}
QueryBuilder<Source, Source, QDistinct> distinctByLogoUrl(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -2100,12 +2230,24 @@ extension SourceQueryProperty on QueryBuilder<Source, Source, QQueryProperty> {
});
}
QueryBuilder<Source, bool?, QQueryOperations> isPinnedProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isPinned');
});
}
QueryBuilder<Source, String?, QQueryOperations> langProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'lang');
});
}
QueryBuilder<Source, bool?, QQueryOperations> lastUsedProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'lastUsed');
});
}
QueryBuilder<Source, String?, QQueryOperations> logoUrlProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'logoUrl');

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/modules/browse/extension/providers/refresh_source_list_data.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/browse/extension/extension_screen.dart';
import 'package:mangayomi/modules/browse/extension/refresh_source_list_data.dart';
import 'package:mangayomi/modules/browse/migrate_screen.dart';
import 'package:mangayomi/modules/browse/sources/sources_screen.dart';
import 'package:mangayomi/modules/library/search_text_form_field.dart';

View file

@ -43,7 +43,7 @@ class ExtensionScreen extends ConsumerWidget {
Text(
groupByValue,
style: const TextStyle(
fontWeight: FontWeight.w300, fontSize: 12),
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),

View file

@ -28,6 +28,7 @@ refreshSourceListData(RefreshSourceListDataRef ref) {
..typeSource = source.typeSource
..isFullData = source.isFullData
..lang = source.lang
..isNsfw = source.isNsfw
..sourceName = source.sourceName);
}
}

View file

@ -7,7 +7,7 @@ part of 'refresh_source_list_data.dart';
// **************************************************************************
String _$refreshSourceListDataHash() =>
r'f77838cebad50f030d2038d07584199b4509f407';
r'c6ab103b0519d79afc8530ab7027a988203de5d3';
/// See also [refreshSourceListData].
@ProviderFor(refreshSourceListData)

View file

@ -3,12 +3,14 @@ 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/manga.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/services/search_manga.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/sources/service.dart';
import 'package:mangayomi/sources/source_list.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/modules/library/search_text_form_field.dart';
@ -31,7 +33,7 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
@override
Widget build(BuildContext context) {
final sourceList = ref.watch(onlyIncludePinnedSourceStateProvider)
? isar.sources.filter().isAddedEqualTo(true).findAllSync()
? isar.sources.filter().isPinnedEqualTo(true).findAllSync()
: sourcesList;
return Scaffold(
appBar: AppBar(
@ -62,7 +64,7 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
children: [
for (var i = 0; i < sourceList.length; i++)
SizedBox(
height: 230,
height: 260,
child: SourceSearchScreen(
query: query,
source: sourceList[i],
@ -91,7 +93,7 @@ class SourceSearchScreen extends ConsumerWidget {
.watch(searchMangaProvider(source: source.sourceName!, query: query));
return Scaffold(
body: SizedBox(
height: 240,
height: 260,
child: Column(
children: [
ListTile(
@ -175,43 +177,93 @@ class _MangaGlobalImageCardState extends ConsumerState<MangaGlobalImageCard>
pushToMangaReaderDetail(
context: context, getManga: data, lang: widget.lang);
},
child: SizedBox(
width: 90,
child: Column(children: [
cachedNetworkImage(
headers: ref.watch(headersProvider(source: data.source!)),
imageUrl: data.imageUrl!,
width: 80,
height: 120,
fit: BoxFit.fill),
BottomTextWidget(
fontSize: 12.0,
text: widget.manga.name!,
isLoading: true,
isComfortableGrid: true,
)
]),
),
child: StreamBuilder(
stream: isar.mangas
.filter()
.langEqualTo(widget.lang)
.nameEqualTo(data.name)
.sourceEqualTo(data.source)
.favoriteEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
return Padding(
padding: const EdgeInsets.only(left: 10),
child: Stack(
children: [
SizedBox(
width: 100,
child: Column(children: [
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: cachedNetworkImage(
headers: ref.watch(
headersProvider(source: data.source!)),
imageUrl: data.imageUrl!,
width: 100,
height: 140,
fit: BoxFit.fill),
),
BottomTextWidget(
fontSize: 12.0,
text: widget.manga.name!,
isLoading: true,
isComfortableGrid: true,
)
]),
),
Container(
width: 100,
height: 140,
color: snapshot.hasData && snapshot.data!.isNotEmpty
? Colors.black.withOpacity(0.7)
: null,
),
if (snapshot.hasData && snapshot.data!.isNotEmpty)
Positioned(
top: 0,
left: 0,
child: Padding(
padding: const EdgeInsets.all(4),
child: Container(
decoration: BoxDecoration(
color: primaryColor(context),
borderRadius: BorderRadius.circular(5)),
child: const Padding(
padding: EdgeInsets.all(2),
child: Text(
"In library",
style: TextStyle(fontSize: 10),
),
),
),
)),
],
),
);
}),
);
},
loading: () => SizedBox(
width: 60,
child: Column(children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
color: Theme.of(context).cardColor,
width: 80,
height: 120,
loading: () => Padding(
padding: const EdgeInsets.only(left: 10),
child: SizedBox(
width: 100,
child: Column(children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
color: Theme.of(context).cardColor,
width: 100,
height: 140,
),
),
),
BottomTextWidget(
fontSize: 12.0,
text: widget.manga.name!,
isLoading: true,
isComfortableGrid: true,
)
]),
BottomTextWidget(
fontSize: 12.0,
text: widget.manga.name!,
isLoading: true,
isComfortableGrid: true,
)
]),
),
),
error: (error, stackTrace) => Center(child: Text(error.toString())),
);

View file

@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/browse/sources/widgets/source_list_tile.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
@ -15,124 +14,156 @@ class SourcesScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.only(top: 10),
child: StreamBuilder(
stream: isar.sources
.filter()
.idIsNotNull()
.isAddedEqualTo(true)
.and()
.isActiveEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: Text("Empty"));
}
final entries = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.toList();
return GroupedListView<Source, String>(
elements: entries,
groupBy: (element) => completeLang(element.lang!.toLowerCase()),
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
groupByValue,
style: const TextStyle(
fontWeight: FontWeight.w300, fontSize: 12),
),
],
),
),
itemBuilder: (context, Source element) {
return ListTile(
onTap: () {
context.push('/mangaHome',
extra: MangaType(
isFullData: element.isFullData,
lang: element.lang,
source: element.sourceName));
},
leading: Container(
height: 37,
width: 37,
decoration: BoxDecoration(
color: Theme.of(context)
.secondaryHeaderColor
.withOpacity(0.5),
borderRadius: BorderRadius.circular(5)),
child:
// element.logoUrl!.isEmpty
// ?
const Icon(Icons.source_outlined)
// : CachedNetworkImage(
// httpHeaders: ref.watch(
// headersProvider(source: element.sourceName!)),
// imageUrl: element.logoUrl!,
// fit: BoxFit.contain,
// width: 37,
// height: 37,
// errorWidget: (context, url, error) {
// return const SizedBox(
// width: 37,
// height: 37,
// child: Center(
// child: Icon(Icons.source_outlined),
// ),
// );
// },
// ),
),
subtitle: Row(
children: [
Text(
completeLang(element.lang!.toLowerCase()),
style: const TextStyle(
fontWeight: FontWeight.w300, fontSize: 12),
),
if (element.isNsfw!)
Row(
padding: const EdgeInsets.only(top: 10),
child: SingleChildScrollView(
child: Column(
children: [
StreamBuilder(
stream: isar.sources
.filter()
.idIsNotNull()
.isAddedEqualTo(true)
.and()
.isActiveEqualTo(true)
.and()
.lastUsedEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: Text(""));
}
final entries = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.toList();
return GroupedListView<Source, String>(
elements: entries,
groupBy: (element) => "",
groupSeparatorBuilder: (String groupByValue) =>
const Padding(
padding: EdgeInsets.only(left: 12),
child: Row(
children: [
const SizedBox(
width: 2,
),
Text(
"18+",
"Last used",
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 10,
color: Colors.redAccent
.withBlue(5)
.withOpacity(0.8)),
fontWeight: FontWeight.bold, fontSize: 13),
),
],
)
],
),
title: Text(element.sourceName!),
trailing: const SizedBox(
width: 110,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
Icons.push_pin_outlined,
color: Colors.black,
)
],
)),
);
},
groupComparator: (group1, group2) => group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.sourceName!.compareTo(item2.sourceName!),
order: GroupedListOrder.ASC,
);
}),
);
),
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
);
},
shrinkWrap: true,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.sourceName!.compareTo(item2.sourceName!),
order: GroupedListOrder.ASC,
);
}),
StreamBuilder(
stream: isar.sources
.filter()
.idIsNotNull()
.isAddedEqualTo(true)
.and()
.isActiveEqualTo(true)
.and()
.isPinnedEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: Text(""));
}
final entries = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.toList();
return GroupedListView<Source, String>(
elements: entries,
groupBy: (element) => "",
groupSeparatorBuilder: (String groupByValue) =>
const Padding(
padding: EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
"Pinned",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
);
},
shrinkWrap: true,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.sourceName!.compareTo(item2.sourceName!),
order: GroupedListOrder.ASC,
);
}),
StreamBuilder(
stream: isar.sources
.filter()
.idIsNotNull()
.isAddedEqualTo(true)
.and()
.isActiveEqualTo(true)
.and()
.isPinnedEqualTo(false)
.watch(fireImmediately: true),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: Text("Empty"));
}
final entries = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.toList();
return GroupedListView<Source, String>(
elements: entries,
groupBy: (element) =>
completeLang(element.lang!.toLowerCase()),
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
groupByValue,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
);
},
shrinkWrap: true,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.sourceName!.compareTo(item2.sourceName!),
order: GroupedListOrder.ASC,
);
}),
],
),
));
}
}

View file

@ -0,0 +1,95 @@
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/manga_type.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/lang.dart';
class SourceListTile extends StatelessWidget {
final Source source;
const SourceListTile({super.key, required this.source});
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
final sources = isar.sources.filter().idIsNotNull().findAllSync();
isar.writeTxnSync(() {
for (var src in sources) {
isar.sources
.putSync(src..lastUsed = src.id == source.id ? true : false);
}
});
context.push('/mangaHome',
extra: MangaType(
isFullData: source.isFullData,
lang: source.lang,
source: source.sourceName));
},
leading: Container(
height: 37,
width: 37,
decoration: BoxDecoration(
color: Theme.of(context).secondaryHeaderColor.withOpacity(0.5),
borderRadius: BorderRadius.circular(5)),
child:
// source.logoUrl!.isEmpty
// ?
const Icon(Icons.source_outlined)
// : CachedNetworkImage(
// httpHeaders: ref.watch(
// headersProvider(source: source.sourceName!)),
// imageUrl: source.logoUrl!,
// fit: BoxFit.contain,
// width: 37,
// height: 37,
// errorWidget: (context, url, error) {
// return const SizedBox(
// width: 37,
// height: 37,
// child: Center(
// child: Icon(Icons.source_outlined),
// ),
// );
// },
// ),
),
subtitle: Row(
children: [
Text(
completeLang(source.lang!.toLowerCase()),
style: const TextStyle(fontWeight: FontWeight.w300, fontSize: 12),
),
if (source.isNsfw!)
Row(
children: [
const SizedBox(
width: 2,
),
Text(
"18+",
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 10,
color: Colors.redAccent.withBlue(5).withOpacity(0.8)),
),
],
)
],
),
title: Text(source.sourceName!),
trailing: IconButton(
onPressed: () {
isar.writeTxnSync(() =>
isar.sources.putSync(source..isPinned = !source.isPinned!));
},
icon: Icon(
Icons.push_pin_outlined,
color: source.isPinned! ? primaryColor(context) : null,
)),
);
}
}

View file

@ -16,13 +16,13 @@ class DarkModeButton extends ConsumerStatefulWidget {
class _DarkModeButtonState extends ConsumerState<DarkModeButton> {
@override
Widget build(BuildContext context) {
bool isLight = ref.watch(themeModeStateProvider);
bool isDark = ref.watch(themeModeStateProvider);
return SwitchListTile(
onChanged: (value) {
if (value) {
ref.read(themeModeStateProvider.notifier).setLightTheme();
} else {
ref.read(themeModeStateProvider.notifier).setDarkTheme();
} else {
ref.read(themeModeStateProvider.notifier).setLightTheme();
}
},
title: const Text(
@ -30,10 +30,10 @@ class _DarkModeButtonState extends ConsumerState<DarkModeButton> {
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
isLight ? 'Off' : 'On',
!isDark ? 'Off' : 'On',
style: TextStyle(fontSize: 11, color: secondaryColor(context)),
),
value: isLight,
value: !isDark,
);
}
}

View file

@ -8,6 +8,7 @@ import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/sources/service.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/headers.dart';
import 'package:mangayomi/modules/widgets/bottom_text_widget.dart';
import 'package:mangayomi/modules/widgets/cover_view_widget.dart';
@ -30,16 +31,50 @@ class MangaImageCardWidget extends ConsumerWidget {
pushToMangaReaderDetail(
context: context, getManga: getMangaDetail!, lang: lang);
},
child: CoverViewWidget(children: [
cachedNetworkImage(
headers:
ref.watch(headersProvider(source: getMangaDetail!.source!)),
imageUrl: getMangaDetail!.imageUrl!,
width: 200,
height: 270,
fit: BoxFit.cover),
BottomTextWidget(text: getMangaDetail!.name!)
]),
child: StreamBuilder(
stream: isar.mangas
.filter()
.langEqualTo(lang)
.nameEqualTo(getMangaDetail!.name)
.sourceEqualTo(getMangaDetail!.source)
.favoriteEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
return CoverViewWidget(children: [
cachedNetworkImage(
headers: ref
.watch(headersProvider(source: getMangaDetail!.source!)),
imageUrl: getMangaDetail!.imageUrl!,
width: 200,
height: 270,
fit: BoxFit.cover),
Container(
color: snapshot.hasData && snapshot.data!.isNotEmpty
? Colors.black.withOpacity(0.7)
: null,
),
if (snapshot.hasData && snapshot.data!.isNotEmpty)
Positioned(
top: 0,
left: 0,
child: Padding(
padding: const EdgeInsets.all(4),
child: Container(
decoration: BoxDecoration(
color: primaryColor(context),
borderRadius: BorderRadius.circular(5)),
child: const Padding(
padding: EdgeInsets.all(2),
child: Text(
"In library",
style: TextStyle(fontSize: 12),
),
),
),
)),
BottomTextWidget(text: getMangaDetail!.name!)
]);
}),
);
}
}

View file

@ -151,7 +151,7 @@ Future<String> cloudflareBypassHtml(CloudflareBypassHtmlRef ref,
}
return false;
});
await Future.delayed(Duration(seconds: 10));
await Future.delayed(const Duration(seconds: 10));
html = await controller.evaluateJavascript(
source:
"window.document.getElementsByTagName('html')[0].outerHTML;");

View file

@ -84,19 +84,11 @@ List<Source> _madaraSourcesList = [
dateFormat: "MMMM dd, yyyy",
dateFormatLocale: "tr",
),
Source(
sourceName: "Comictoon",
baseUrl: "https://comictoonthaith-new.com",
lang: "th",
typeSource: TypeSource.madara,
logoUrl: '',
dateFormat: "MMMMM dd, yyyy",
dateFormatLocale: "th",
),
Source(
sourceName: "CookieToon",
baseUrl: "https://cookietoon.online",
lang: "pt-BR",
lang: "pt-br",
typeSource: TypeSource.madara,
logoUrl: '',
dateFormat: "dd/MM/yyyy",
@ -105,7 +97,7 @@ List<Source> _madaraSourcesList = [
Source(
sourceName: "Drope Scan",
baseUrl: "https://dropescan.com",
lang: "pt-BR",
lang: "pt-br",
typeSource: TypeSource.madara,
logoUrl: '',
dateFormat: "dd/MM/yyyy",
@ -123,7 +115,7 @@ List<Source> _madaraSourcesList = [
Source(
sourceName: "Final Scans",
baseUrl: "https://finalscans.com",
lang: "pt-BR",
lang: "pt-br",
typeSource: TypeSource.madara,
logoUrl: '',
isNsfw: true,
@ -190,7 +182,7 @@ List<Source> _madaraSourcesList = [
Source(
sourceName: "Kami Sama Explorer",
baseUrl: "https://leitor.kamisama.com.br",
lang: "pt-BR",
lang: "pt-br",
typeSource: TypeSource.madara,
logoUrl: '',
dateFormat: "dd 'de' MMMM 'de' yyyy",

View file

@ -1,6 +1,4 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:html/dom.dart';
import 'package:mangayomi/models/chapter.dart';

View file

@ -4,7 +4,7 @@ const logoUrl =
'https://comick.app/_next/image?url=%2Fstatic%2Ficons%2Funicorn-64.png&w=144&q=75';
const apiUrl = 'https://api.comick.fun';
const baseUrl = 'https://comick.app';
const isNsfw = true;
List<Source> get comickSourcesList => _comickSourcesList;
List<Source> _comickSourcesList = [
Source(
@ -15,6 +15,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -24,6 +25,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -33,6 +35,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -42,6 +45,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -51,6 +55,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -60,6 +65,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -69,6 +75,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -78,6 +85,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -87,6 +95,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -96,6 +105,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -105,6 +115,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -114,6 +125,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -123,6 +135,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -132,6 +145,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -141,6 +155,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -150,6 +165,7 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
Source(
sourceName: 'Comick',
@ -159,5 +175,6 @@ List<Source> _comickSourcesList = [
typeSource: TypeSource.comick,
logoUrl: logoUrl,
dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'",
isNsfw: isNsfw,
dateFormatLocale: "en"),
];

View file

@ -103,18 +103,18 @@ class Comick extends MangaYomiServices {
required AutoDisposeFutureProviderRef ref}) async {
final response = await ref.watch(httpGetProvider(
url:
'https://api.comick.fun/search?q=${query.trim()}&tachiyomi=true&page=1',
'${getMangaAPIUrl(source)}/v1.0/search?q=${query.trim()}&tachiyomi=true&limit=50&page=1',
source: source,
resDom: false)
.future) as String?;
var popularManga = jsonDecode(response!) as List;
var popularMangaList =
popularManga.map((e) => MangaSearchModelComick.fromJson(e)).toList();
var search = jsonDecode(response!) as List;
var searchList =
search.map((e) => MangaSearchModelComick.fromJson(e)).toList();
for (var popular in popularMangaList) {
url.add("/comic/${popular.slug}");
name.add(popular.title);
image.add(popular.coverUrl);
for (var search in searchList) {
url.add("/comic/${search.hid}#");
name.add(search.title);
image.add(search.coverUrl);
}
return mangaRes();

View file

@ -32,6 +32,7 @@ class MangaSearchModelComick {
title = json['title'];
slug = json['slug'];
hid = json['hid'];
coverUrl = json['cover_url'];
}

View file

@ -212,10 +212,12 @@ class MangaKawaii extends MangaYomiServices {
}
return pageUrls;
}
@override
Future<List<GetManga?>> getLatestUpdatesManga({required String source, required int page, required AutoDisposeFutureProviderRef ref}) {
// TODO: implement getLatestUpdatesManga
Future<List<GetManga?>> getLatestUpdatesManga(
{required String source,
required int page,
required AutoDisposeFutureProviderRef ref}) {
throw UnimplementedError();
}
}