improve browser source & extension tab lag & some fix

This commit is contained in:
kodjomoustapha 2023-12-12 22:29:32 +01:00
parent b713d844d6
commit fb8754df29
18 changed files with 380 additions and 294 deletions

View file

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:bot_toast/bot_toast.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
@ -482,7 +483,6 @@ class MBridge {
//Utility to use RegExp
static String regExp(
//RegExp(r'\[a\]'), "[123]")
String expression,
String source,
String replace,
@ -550,14 +550,18 @@ class MBridge {
res = await request.send();
}
if (res.statusCode == 403 && (source?.hasCloudflare ?? false)) {
log("Http request: ${res.statusCode}, Cloudflare");
return await cloudflareBypass(
url: url, sourceId: source!.id.toString(), method: 0);
} else if (res.statusCode == 200) {
log("Http request: ${res.statusCode}");
return await res.stream.bytesToString();
} else {
log("Http request: ${res.statusCode}, reasonPhrase: ${res.reasonPhrase}");
return "error";
}
} catch (e) {
log("Http error: $e");
botToast(e.toString());
return "error";
}

View file

@ -71,8 +71,8 @@ class Source {
this.lastUsed = false,
this.apiUrl = "",
this.sourceCodeUrl = "",
this.version = "",
this.versionLast = "",
this.version = "0.0.1",
this.versionLast = "0.0.1",
this.sourceCode = '',
this.headers = '',
this.isManga = true,

View file

@ -166,6 +166,11 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
title: _firstVid.subtitles?.first.label,
language: _firstVid.subtitles?.first.label));
late final ValueNotifier<AudioTrack?> _audio = ValueNotifier(AudioTrack.uri(
_firstVid.audios?.first.file ?? "",
title: _firstVid.audios?.first.label,
language: _firstVid.audios?.first.label));
final ValueNotifier<double> _playbackSpeed = ValueNotifier(1.0);
bool _seekToCurrentPosition = true;
bool _initSubtitle = true;
@ -389,6 +394,40 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
],
),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 5)),
_videoAudios(context)
],
),
),
),
Container(
color: Colors.white,
width: 0.2,
height: mediaHeight(context, 1)),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
top: 8, left: 12, bottom: 5),
child: Row(
children: [
Text(
"l10n.subtitle",
style: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20),
),
],
),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 5)),
_videoSubtitle(context)
@ -405,8 +444,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
Widget _videoSubtitle(BuildContext context) {
List<VideoPrefs> videoSubtitle = _player.state.tracks.subtitle
.where((element) =>
element.title != null && element.language != null && widget.isLocal)
.where((element) => element.title != null && element.language != null)
.toList()
.map((e) => VideoPrefs(isLocal: true, subtitle: e))
.toList();
@ -460,9 +498,84 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
),
Expanded(
child: Text(
sub.subtitle!.title ??
sub.subtitle!.language ??
sub.subtitle!.channels ??
sub.subtitle?.title ??
sub.subtitle?.language ??
sub.subtitle?.channels ??
"None",
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 16,
color: selected
? Colors.white
: Colors.white.withOpacity(0.6)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}).toList(),
);
}
Widget _videoAudios(BuildContext context) {
List<VideoPrefs> videoAudio = _player.state.tracks.audio
.where((element) => element.title != null && element.language != null)
.toList()
.map((e) => VideoPrefs(isLocal: true, audio: e))
.toList();
AudioTrack? audio;
List<String> audios = [];
if (widget.videos.isNotEmpty && !widget.isLocal) {
for (var video in widget.videos) {
for (var audio in video.audios ?? []) {
if (!audios.contains(audio.file)) {
videoAudio.add(VideoPrefs(
isLocal: false,
audio: AudioTrack.uri(audio.file!,
title: audio.label, language: audio.label)));
audios.add(audio.file!);
}
}
}
}
if (widget.isLocal) {
audio = _player.state.track.audio;
} else {
try {
audio = _audio.value;
} catch (_) {}
}
videoAudio.sort((a, b) => a.audio!.title!.compareTo(b.audio!.title!));
videoAudio.insert(
0, VideoPrefs(isLocal: false, subtitle: SubtitleTrack.no()));
return Column(
children: videoAudio.toSet().toList().map((aud) {
final selected = audio != null && aud.audio == audio;
return GestureDetector(
onTap: () {
Navigator.pop(context);
try {
_player.setAudioTrack(aud.audio!);
if (!widget.isLocal) _audio.value = aud.audio;
} catch (_) {}
},
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Icon(
Icons.check,
color: selected ? Colors.white : Colors.transparent,
),
),
Expanded(
child: Text(
aud.audio?.title ??
aud.audio?.language ??
aud.audio?.channels ??
"None",
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontSize: 16,
@ -1017,8 +1130,13 @@ class MaterialMobilePositionIndicatorState
class VideoPrefs {
VideoTrack? videoTrack;
SubtitleTrack? subtitle;
AudioTrack? audio;
bool isLocal;
final Map<String, String>? headers;
VideoPrefs(
{this.videoTrack, this.isLocal = true, this.headers, this.subtitle});
{this.videoTrack,
this.isLocal = true,
this.headers,
this.subtitle,
this.audio});
}

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:grouped_list/sliver_grouped_list.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/browse/extension/providers/extensions_provider.dart';
import 'package:mangayomi/modules/browse/extension/providers/fetch_anime_sources.dart';
@ -21,8 +21,7 @@ class ExtensionScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final streamExtensions =
ref.watch(getExtensionsStreamProvider(isManga: isManga, query: query));
final streamExtensions = ref.watch(getExtensionsStreamProvider(isManga));
if (isManga) {
ref.watch(fetchMangaSourcesListProvider(id: null, reFresh: false));
} else {
@ -39,131 +38,129 @@ class ExtensionScreen extends ConsumerWidget {
padding: const EdgeInsets.only(top: 10),
child: streamExtensions.when(
data: (data) {
final entries1 = data
data = query.isEmpty
? data
: data
.where((element) => element.name!
.toLowerCase()
.contains(query.toLowerCase()))
.toList();
data = data
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.toList();
final notInstalledEntries = data
.where((element) => element.version == element.versionLast!)
.where((element) => !element.isAdded!)
.toList();
final entries2 = data
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
final installedEntries = data
.where((element) => element.version == element.versionLast!)
.where((element) => element.isAdded!)
.toList();
final entries = data
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
final updateEntries = data
.where((element) =>
compareVersions(element.version!, element.versionLast!) < 0)
.toList();
return SingleChildScrollView(
child: Column(
children: [
if (useTestSourceCode)
for (var e in testSourceModelList)
ExtensionListTileWidget(
source: e, isTestSource: useTestSourceCode),
GroupedListView<Source, String>(
elements: entries,
groupBy: (element) => "",
groupSeparatorBuilder: (_) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
l10n.update_pending,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
ElevatedButton(
onPressed: () async {
for (var source in entries) {
source.isManga!
? await ref.watch(
fetchMangaSourcesListProvider(
id: source.id, reFresh: true)
.future)
: await ref.watch(
fetchAnimeSourcesListProvider(
id: source.id, reFresh: true)
.future);
}
},
child: Text(l10n.update_all))
],
),
return CustomScrollView(
slivers: [
if (useTestSourceCode)
SliverList.builder(
itemCount: testSourceModelList.length,
itemBuilder: (context, index) => ExtensionListTileWidget(
source: testSourceModelList[index],
isTestSource: true,
)),
SliverGroupedListView<Source, String>(
elements: updateEntries,
groupBy: (element) => "",
groupSeparatorBuilder: (_) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
l10n.update_pending,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
ElevatedButton(
onPressed: () async {
for (var source in updateEntries) {
source.isManga!
? await ref.watch(
fetchMangaSourcesListProvider(
id: source.id, reFresh: true)
.future)
: await ref.watch(
fetchAnimeSourcesListProvider(
id: source.id, reFresh: true)
.future);
}
},
child: Text(l10n.update_all))
],
),
itemBuilder: (context, Source element) {
return ExtensionListTileWidget(
source: element,
);
},
groupComparator: (group1, group2) =>
group1.compareTo(group2),
shrinkWrap: true,
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
GroupedListView<Source, String>(
elements: entries2,
groupBy: (element) => "",
groupSeparatorBuilder: (_) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(
l10n.installed,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
itemBuilder: (context, Source element) {
return ExtensionListTileWidget(
source: element,
);
},
groupComparator: (group1, group2) => group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
SliverGroupedListView<Source, String>(
elements: installedEntries,
groupBy: (element) => "",
groupSeparatorBuilder: (_) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(
l10n.installed,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
itemBuilder: (context, Source element) {
return ExtensionListTileWidget(source: element);
},
groupComparator: (group1, group2) =>
group1.compareTo(group2),
shrinkWrap: true,
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
GroupedListView<Source, String>(
elements: entries1,
groupBy: (element) =>
completeLanguageName(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 ExtensionListTileWidget(source: element);
},
groupComparator: (group1, group2) => group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
SliverGroupedListView<Source, String>(
elements: notInstalledEntries,
groupBy: (element) =>
completeLanguageName(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 ExtensionListTileWidget(
source: element,
);
},
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
shrinkWrap: true,
order: GroupedListOrder.ASC,
),
],
),
itemBuilder: (context, Source element) {
return ExtensionListTileWidget(
source: element,
);
},
groupComparator: (group1, group2) => group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
],
);
},
error: (error, stackTrace) => ErrorText(error),
error: (error, _) => ErrorText(error),
loading: () => const ProgressCenter(),
),
),

View file

@ -41,8 +41,8 @@ SourcePreference getSourcePreferenceEntry(String key, int sourceId) {
.sourceIdEqualTo(sourceId)
.keyEqualTo(key)
.findFirstSync();
final source = isar.sources.getSync(sourceId)!;
if (sourcePreference == null) {
final source = isar.sources.getSync(sourceId)!;
sourcePreference = getSourcePreference(source: source).firstWhere(
(element) => element.key == key,
orElse: () => throw "Error when getting source preference");

View file

@ -5,22 +5,13 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'extensions_provider.g.dart';
@riverpod
Stream<List<Source>> getExtensionsStream(GetExtensionsStreamRef ref,
{required String query, required bool? isManga}) async* {
yield* query.isNotEmpty
? isar.sources
.filter()
.nameContains(query.toLowerCase(), caseSensitive: false)
.idIsNotNull()
.and()
.isActiveEqualTo(true)
.isMangaEqualTo(isManga)
.watch(fireImmediately: true)
: isar.sources
.filter()
.idIsNotNull()
.and()
.isActiveEqualTo(true)
.isMangaEqualTo(isManga)
.watch(fireImmediately: true);
Stream<List<Source>> getExtensionsStream(
GetExtensionsStreamRef ref, bool? isManga) async* {
yield* isar.sources
.filter()
.idIsNotNull()
.and()
.isActiveEqualTo(true)
.isMangaEqualTo(isManga)
.watch(fireImmediately: true);
}

View file

@ -7,7 +7,7 @@ part of 'extensions_provider.dart';
// **************************************************************************
String _$getExtensionsStreamHash() =>
r'2f3ed044cd49c1b08587196c27d727ed9d815468';
r'010b67dcc4ccc0736c5ca779c33751302ab28cd8';
/// Copied from Dart SDK
class _SystemHash {
@ -40,13 +40,11 @@ class GetExtensionsStreamFamily extends Family<AsyncValue<List<Source>>> {
const GetExtensionsStreamFamily();
/// See also [getExtensionsStream].
GetExtensionsStreamProvider call({
required String query,
required bool? isManga,
}) {
GetExtensionsStreamProvider call(
bool? isManga,
) {
return GetExtensionsStreamProvider(
query: query,
isManga: isManga,
isManga,
);
}
@ -55,8 +53,7 @@ class GetExtensionsStreamFamily extends Family<AsyncValue<List<Source>>> {
covariant GetExtensionsStreamProvider provider,
) {
return call(
query: provider.query,
isManga: provider.isManga,
provider.isManga,
);
}
@ -79,14 +76,12 @@ class GetExtensionsStreamFamily extends Family<AsyncValue<List<Source>>> {
class GetExtensionsStreamProvider
extends AutoDisposeStreamProvider<List<Source>> {
/// See also [getExtensionsStream].
GetExtensionsStreamProvider({
required String query,
required bool? isManga,
}) : this._internal(
GetExtensionsStreamProvider(
bool? isManga,
) : this._internal(
(ref) => getExtensionsStream(
ref as GetExtensionsStreamRef,
query: query,
isManga: isManga,
isManga,
),
from: getExtensionsStreamProvider,
name: r'getExtensionsStreamProvider',
@ -97,7 +92,6 @@ class GetExtensionsStreamProvider
dependencies: GetExtensionsStreamFamily._dependencies,
allTransitiveDependencies:
GetExtensionsStreamFamily._allTransitiveDependencies,
query: query,
isManga: isManga,
);
@ -108,11 +102,9 @@ class GetExtensionsStreamProvider
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.query,
required this.isManga,
}) : super.internal();
final String query;
final bool? isManga;
@override
@ -128,7 +120,6 @@ class GetExtensionsStreamProvider
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
query: query,
isManga: isManga,
),
);
@ -141,15 +132,12 @@ class GetExtensionsStreamProvider
@override
bool operator ==(Object other) {
return other is GetExtensionsStreamProvider &&
other.query == query &&
other.isManga == isManga;
return other is GetExtensionsStreamProvider && other.isManga == isManga;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, query.hashCode);
hash = _SystemHash.combine(hash, isManga.hashCode);
return _SystemHash.finish(hash);
@ -157,9 +145,6 @@ class GetExtensionsStreamProvider
}
mixin GetExtensionsStreamRef on AutoDisposeStreamProviderRef<List<Source>> {
/// The parameter `query` of this provider.
String get query;
/// The parameter `isManga` of this provider.
bool? get isManga;
}
@ -169,8 +154,6 @@ class _GetExtensionsStreamProviderElement
with GetExtensionsStreamRef {
_GetExtensionsStreamProviderElement(super.provider);
@override
String get query => (origin as GetExtensionsStreamProvider).query;
@override
bool? get isManga => (origin as GetExtensionsStreamProvider).isManga;
}

View file

@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/browse/extension/providers/fetch_anime_sources.dart';
import 'package:mangayomi/modules/browse/extension/providers/fetch_manga_sources.dart';
@ -37,6 +38,9 @@ class _ExtensionListTileWidgetState
return ListTile(
onTap: () async {
if (sourceNotEmpty || widget.isTestSource) {
if (widget.isTestSource) {
isar.writeTxnSync(() => isar.sources.putSync(widget.source));
}
context.push('/extension_detail', extra: widget.source);
} else {
setState(() {

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:grouped_list/sliver_grouped_list.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
@ -16,6 +16,7 @@ class SourcesScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final showNSFW = ref.watch(showNSFWStateProvider);
final l10n = l10nLocalizations(context)!;
return Padding(
padding: const EdgeInsets.only(top: 10),
@ -33,117 +34,106 @@ class SourcesScreen extends ConsumerWidget {
if (!snapshot.hasData) {
return Center(child: Text(l10n.no_result));
}
final entries = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.where((element) => element.lastUsed == true)
List<Source> sources = snapshot.data!
.where((element) => showNSFW ? true : element.isNsfw == false)
.toList();
final entries1 = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.where((element) => element.isPinned == true)
.toList();
final entries2 = snapshot.data!
.where((element) => ref.watch(showNSFWStateProvider)
? true
: element.isNsfw == false)
.where((element) => element.isPinned == false)
.toList();
return SingleChildScrollView(
child: Column(
children: [
if (useTestSourceCode)
for (var e in testSourceModelList)
SourceListTile(source: e, isManga: isManga),
GroupedListView<Source, String>(
elements: entries,
groupBy: (element) => "",
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
l10n.last_used,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
final lastUsedEntries =
sources.where((element) => element.lastUsed!).toList();
final isPinnedEntries =
sources.where((element) => element.isPinned!).toList();
final allEntriesWithoutIspinned =
sources.where((element) => !element.isPinned!).toList();
return CustomScrollView(
slivers: [
if (useTestSourceCode)
SliverList.builder(
itemCount: testSourceModelList.length,
itemBuilder: (context, index) => SourceListTile(
source: testSourceModelList[index],
isManga: isManga)),
SliverGroupedListView<Source, String>(
elements: lastUsedEntries,
groupBy: (element) => "",
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
l10n.last_used,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
isManga: isManga,
);
},
shrinkWrap: true,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
GroupedListView<Source, String>(
elements: entries1,
groupBy: (element) => "",
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
l10n.pinned,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
isManga: isManga,
);
},
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
SliverGroupedListView<Source, String>(
elements: isPinnedEntries,
groupBy: (element) => "",
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(left: 12),
child: Row(
children: [
Text(
l10n.pinned,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 13),
),
],
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
isManga: isManga,
);
},
shrinkWrap: true,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
GroupedListView<Source, String>(
elements: entries2,
groupBy: (element) =>
completeLanguageName(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,
isManga: isManga,
);
},
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
SliverGroupedListView<Source, String>(
elements: allEntriesWithoutIspinned,
groupBy: (element) =>
completeLanguageName(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,
isManga: isManga,
);
},
shrinkWrap: true,
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
],
),
itemBuilder: (context, Source element) {
return SourceListTile(
source: element,
isManga: isManga,
);
},
groupComparator: (group1, group2) =>
group1.compareTo(group2),
itemComparator: (item1, item2) =>
item1.name!.compareTo(item2.name!),
order: GroupedListOrder.ASC,
),
],
);
}));
}

View file

@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/sources/source_test.dart';
import 'package:mangayomi/utils/colors.dart';
import 'package:mangayomi/utils/language.dart';
import 'package:cached_network_image/cached_network_image.dart';
@ -17,6 +18,9 @@ class SourceListTile extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile(
onTap: () {
if (useTestSourceCode) {
isar.writeTxnSync(() => isar.sources.putSync(source));
}
final sources = isar.sources
.filter()
.idIsNotNull()

View file

@ -7,7 +7,7 @@ part of 'library_state_provider.dart';
// **************************************************************************
String _$libraryDisplayTypeStateHash() =>
r'f7355242bae652d8cf378b21626a0ca3138d5c23';
r'8a7669eb2d0961135bc1a9469d8fbcc7dda0ca3c';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -40,6 +40,7 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
bool _isLoading = true;
@override
Widget build(BuildContext context) {
print(widget.mangaId);
final manga =
ref.watch(getMangaDetailStreamProvider(mangaId: widget.mangaId));
return Scaffold(
@ -59,10 +60,12 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
.isAddedEqualTo(true)
.watch(fireImmediately: true),
builder: (context, snapshot) {
final sourceExist = snapshot.hasData && snapshot.data!.isNotEmpty;
final sourceExist = useTestSourceCode
? true
: snapshot.hasData && snapshot.data!.isNotEmpty;
return RefreshIndicator(
onRefresh: () async {
if (sourceExist || useTestSourceCode) {
if (sourceExist) {
await ref.read(updateMangaDetailProvider(
mangaId: manga.id, isInit: false)
.future);

View file

@ -6,7 +6,7 @@ part of 'download_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$downloadChapterHash() => r'6ae3da42911f2f4f2d382423b4063d017b7e0355';
String _$downloadChapterHash() => r'41a09ffc4ef02f061eb0a5d0bfa6410d12dc6c4a';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -35,18 +35,18 @@ class FilterWidget extends StatelessWidget {
final state = filterState.state;
widget = CheckboxListTile(
dense: true,
value: state == 0
? false
: state == 1
? true
: null,
value: switch (state) {
0 => false,
1 => true,
_ => null,
},
onChanged: (value) {
filterList[idx] = filterState
..state = value == null
? 2
: value == true
? 1
: 0;
..state = switch (value) {
null => 2,
true => 1,
_ => 0,
};
onChanged(filterList);
},
title: Text(filterState.name),

View file

@ -23,6 +23,7 @@ pushMangaReaderView({
.isAddedEqualTo(true)
.findAllSync()
.isNotEmpty;
print(sourceExist);
if (sourceExist ||
useTestSourceCode ||
chapter.manga.value!.isLocalArchive!) {

View file

@ -6,7 +6,7 @@ part of 'restore.dart';
// RiverpodGenerator
// **************************************************************************
String _$doRestoreHash() => r'3c88ad8ba80c245a4b511961111f7ab79c0d330f';
String _$doRestoreHash() => r'91d54be97447d51073f214b1f51deffa0045b1d0';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -31,8 +31,7 @@ class MoreScreen extends StatelessWidget {
const Divider(),
// ListTile(
// onTap: () {},
// leading:
// const SizedBox(height: 40, child: Icon(Icons.cloud_off)),
// leading: const SizedBox(height: 40, child: Icon(Icons.cloud_off)),
// subtitle: const Text('Filter all entries in your library'),
// title: const Text('Donloaded only'),
// trailing: Switch(

View file

@ -1,16 +1,8 @@
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/sources/source_test.dart';
Source? getSource(String lang, String name) {
if (testSourceModelList.isNotEmpty && useTestSourceCode) {
if (lang.isEmpty || name.isEmpty) {
return testSourceModelList.first;
}
return testSourceModelList
.firstWhere((element) => element.lang == lang && element.name == name);
}
try {
final sourcesList = isar.sources.filter().idIsNotNull().findAllSync();
return sourcesList.firstWhere(