mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-03-11 21:35:32 +00:00
improve browser source & extension tab lag & some fix
This commit is contained in:
parent
b713d844d6
commit
fb8754df29
18 changed files with 380 additions and 294 deletions
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'library_state_provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$libraryDisplayTypeStateHash() =>
|
||||
r'f7355242bae652d8cf378b21626a0ca3138d5c23';
|
||||
r'8a7669eb2d0961135bc1a9469d8fbcc7dda0ca3c';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'download_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$downloadChapterHash() => r'6ae3da42911f2f4f2d382423b4063d017b7e0355';
|
||||
String _$downloadChapterHash() => r'41a09ffc4ef02f061eb0a5d0bfa6410d12dc6c4a';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pushMangaReaderView({
|
|||
.isAddedEqualTo(true)
|
||||
.findAllSync()
|
||||
.isNotEmpty;
|
||||
print(sourceExist);
|
||||
if (sourceExist ||
|
||||
useTestSourceCode ||
|
||||
chapter.manga.value!.isLocalArchive!) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'restore.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$doRestoreHash() => r'3c88ad8ba80c245a4b511961111f7ab79c0d330f';
|
||||
String _$doRestoreHash() => r'91d54be97447d51073f214b1f51deffa0045b1d0';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in a new issue