mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 23:22:07 +00:00
refactor & fix lag when installing extension
This commit is contained in:
parent
7a003d7fde
commit
34c2ce3c37
3 changed files with 435 additions and 493 deletions
|
|
@ -27,79 +27,99 @@ class ExtensionScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
|
||||
final controller = ScrollController();
|
||||
final ScrollController controller = ScrollController();
|
||||
bool isUpdating = false;
|
||||
Future<void> _refreshSources() {
|
||||
return switch (widget.itemType) {
|
||||
ItemType.manga => ref.refresh(
|
||||
fetchMangaSourcesListProvider(id: null, reFresh: true).future,
|
||||
),
|
||||
ItemType.anime => ref.refresh(
|
||||
fetchAnimeSourcesListProvider(id: null, reFresh: true).future,
|
||||
),
|
||||
_ => ref.refresh(
|
||||
fetchNovelSourcesListProvider(id: null, reFresh: true).future,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> _updateSource(Source source) {
|
||||
switch (source.itemType) {
|
||||
case ItemType.manga:
|
||||
return ref.read(
|
||||
fetchMangaSourcesListProvider(id: source.id, reFresh: true).future,
|
||||
);
|
||||
case ItemType.anime:
|
||||
return ref.read(
|
||||
fetchAnimeSourcesListProvider(id: source.id, reFresh: true).future,
|
||||
);
|
||||
default:
|
||||
return ref.read(
|
||||
fetchNovelSourcesListProvider(id: source.id, reFresh: true).future,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (widget.itemType) {
|
||||
case ItemType.manga:
|
||||
ref.read(fetchMangaSourcesListProvider(id: null, reFresh: false));
|
||||
break;
|
||||
case ItemType.anime:
|
||||
ref.read(fetchAnimeSourcesListProvider(id: null, reFresh: false));
|
||||
break;
|
||||
default:
|
||||
ref.read(fetchNovelSourcesListProvider(id: null, reFresh: false));
|
||||
}
|
||||
|
||||
final streamExtensions = ref.watch(
|
||||
getExtensionsStreamProvider(widget.itemType),
|
||||
);
|
||||
if (widget.itemType == ItemType.manga) {
|
||||
ref.watch(fetchMangaSourcesListProvider(id: null, reFresh: false));
|
||||
} else if (widget.itemType == ItemType.anime) {
|
||||
ref.watch(fetchAnimeSourcesListProvider(id: null, reFresh: false));
|
||||
} else {
|
||||
ref.watch(fetchNovelSourcesListProvider(id: null, reFresh: false));
|
||||
}
|
||||
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh:
|
||||
() =>
|
||||
widget.itemType == ItemType.manga
|
||||
? ref.refresh(
|
||||
fetchMangaSourcesListProvider(
|
||||
id: null,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: widget.itemType == ItemType.anime
|
||||
? ref.refresh(
|
||||
fetchAnimeSourcesListProvider(
|
||||
id: null,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: ref.refresh(
|
||||
fetchNovelSourcesListProvider(
|
||||
id: null,
|
||||
reFresh: true,
|
||||
).future,
|
||||
),
|
||||
onRefresh: _refreshSources,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: streamExtensions.when(
|
||||
data: (data) {
|
||||
data =
|
||||
final filteredData =
|
||||
widget.query.isEmpty
|
||||
? data
|
||||
: data
|
||||
.where(
|
||||
(element) => element.name!.toLowerCase().contains(
|
||||
widget.query.toLowerCase(),
|
||||
),
|
||||
(element) =>
|
||||
element.name?.toLowerCase().contains(
|
||||
widget.query.toLowerCase(),
|
||||
) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
|
||||
final notInstalledEntries =
|
||||
data
|
||||
.where((element) => element.version == element.versionLast!)
|
||||
.where((element) => !element.isAdded!)
|
||||
.toList();
|
||||
final installedEntries =
|
||||
data
|
||||
.where((element) => element.version == element.versionLast!)
|
||||
.where((element) => element.isAdded!)
|
||||
.toList();
|
||||
final updateEntries =
|
||||
data
|
||||
.where(
|
||||
(element) =>
|
||||
compareVersions(
|
||||
element.version!,
|
||||
element.versionLast!,
|
||||
) <
|
||||
0,
|
||||
)
|
||||
.toList();
|
||||
final updateEntries = <Source>[];
|
||||
final installedEntries = <Source>[];
|
||||
final notInstalledEntries = <Source>[];
|
||||
|
||||
for (var element in filteredData) {
|
||||
final isLatestVersion = element.version == element.versionLast;
|
||||
|
||||
if (compareVersions(
|
||||
element.version ?? '',
|
||||
element.versionLast ?? '',
|
||||
) <
|
||||
0) {
|
||||
updateEntries.add(element);
|
||||
} else if (isLatestVersion) {
|
||||
if (element.isAdded ?? false) {
|
||||
installedEntries.add(element);
|
||||
} else {
|
||||
notInstalledEntries.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Scrollbar(
|
||||
interactive: true,
|
||||
controller: controller,
|
||||
|
|
@ -108,113 +128,12 @@ class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
|
|||
child: CustomScrollView(
|
||||
controller: controller,
|
||||
slivers: [
|
||||
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.itemType == ItemType.manga
|
||||
? await ref.watch(
|
||||
fetchMangaSourcesListProvider(
|
||||
id: source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: source.itemType == ItemType.anime
|
||||
? await ref.watch(
|
||||
fetchAnimeSourcesListProvider(
|
||||
id: source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: await ref.watch(
|
||||
fetchNovelSourcesListProvider(
|
||||
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),
|
||||
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),
|
||||
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!),
|
||||
order: GroupedListOrder.ASC,
|
||||
),
|
||||
if (updateEntries.isNotEmpty)
|
||||
_buildUpdateSection(updateEntries, l10n),
|
||||
if (installedEntries.isNotEmpty)
|
||||
_buildInstalledSection(installedEntries, l10n),
|
||||
if (notInstalledEntries.isNotEmpty)
|
||||
_buildNotInstalledSection(notInstalledEntries),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -222,21 +141,7 @@ class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
|
|||
error:
|
||||
(error, _) => Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (widget.itemType == ItemType.manga) {
|
||||
ref.invalidate(
|
||||
fetchMangaSourcesListProvider(id: null, reFresh: true),
|
||||
);
|
||||
} else if (widget.itemType == ItemType.anime) {
|
||||
ref.invalidate(
|
||||
fetchAnimeSourcesListProvider(id: null, reFresh: true),
|
||||
);
|
||||
} else {
|
||||
ref.invalidate(
|
||||
fetchNovelSourcesListProvider(id: null, reFresh: true),
|
||||
);
|
||||
}
|
||||
},
|
||||
onPressed: _refreshSources,
|
||||
child: Text(context.l10n.refresh),
|
||||
),
|
||||
),
|
||||
|
|
@ -245,4 +150,108 @@ class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUpdateSection(List<Source> updateEntries, dynamic l10n) {
|
||||
return SliverGroupedListView<Source, String>(
|
||||
elements: updateEntries,
|
||||
groupBy: (_) => "",
|
||||
groupSeparatorBuilder:
|
||||
(_) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return 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:
|
||||
isUpdating
|
||||
? null
|
||||
: () async {
|
||||
setState(() => isUpdating = true);
|
||||
try {
|
||||
for (var source in updateEntries) {
|
||||
await _updateSource(source);
|
||||
}
|
||||
} finally {
|
||||
setState(() => isUpdating = false);
|
||||
}
|
||||
},
|
||||
child:
|
||||
isUpdating
|
||||
? const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: Text(l10n.update_all),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
itemBuilder:
|
||||
(context, Source element) =>
|
||||
ref.watch(extensionListTileWidget(element)),
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
itemComparator:
|
||||
(item1, item2) => item1.name?.compareTo(item2.name ?? '') ?? 0,
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInstalledSection(List<Source> installedEntries, dynamic l10n) {
|
||||
return SliverGroupedListView<Source, String>(
|
||||
elements: installedEntries,
|
||||
groupBy: (_) => "",
|
||||
groupSeparatorBuilder:
|
||||
(_) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Text(
|
||||
l10n.installed,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
|
||||
),
|
||||
),
|
||||
itemBuilder:
|
||||
(context, Source element) =>
|
||||
ref.watch(extensionListTileWidget(element)),
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
itemComparator:
|
||||
(item1, item2) => item1.name?.compareTo(item2.name ?? '') ?? 0,
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNotInstalledSection(List<Source> notInstalledEntries) {
|
||||
return SliverGroupedListView<Source, String>(
|
||||
elements: notInstalledEntries,
|
||||
groupBy:
|
||||
(element) => completeLanguageName(element.lang?.toLowerCase() ?? ''),
|
||||
groupSeparatorBuilder:
|
||||
(String groupByValue) => Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Text(
|
||||
groupByValue,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
|
||||
),
|
||||
),
|
||||
itemBuilder:
|
||||
(context, Source element) =>
|
||||
ref.watch(extensionListTileWidget(element)),
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
itemComparator:
|
||||
(item1, item2) => item1.name?.compareTo(item2.name ?? '') ?? 0,
|
||||
order: GroupedListOrder.ASC,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
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/changed.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/services/fetch_anime_sources.dart';
|
||||
import 'package:mangayomi/services/fetch_manga_sources.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
|
|
@ -15,14 +12,13 @@ import 'package:mangayomi/utils/cached_network.dart';
|
|||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
|
||||
final extensionListTileWidget = Provider.family<Widget, Source>((ref, source) {
|
||||
return ExtensionListTileWidget(source: source);
|
||||
});
|
||||
|
||||
class ExtensionListTileWidget extends ConsumerStatefulWidget {
|
||||
final Source source;
|
||||
final bool isTestSource;
|
||||
const ExtensionListTileWidget({
|
||||
super.key,
|
||||
required this.source,
|
||||
this.isTestSource = false,
|
||||
});
|
||||
const ExtensionListTileWidget({super.key, required this.source});
|
||||
|
||||
@override
|
||||
ConsumerState<ExtensionListTileWidget> createState() =>
|
||||
|
|
@ -32,69 +28,94 @@ class ExtensionListTileWidget extends ConsumerStatefulWidget {
|
|||
class _ExtensionListTileWidgetState
|
||||
extends ConsumerState<ExtensionListTileWidget> {
|
||||
bool _isLoading = false;
|
||||
late final bool _updateAvailable;
|
||||
late final bool _sourceNotEmpty;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updateAvailable =
|
||||
compareVersions(widget.source.version!, widget.source.versionLast!) < 0;
|
||||
_sourceNotEmpty =
|
||||
widget.source.sourceCode != null &&
|
||||
widget.source.sourceCode!.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<void> _handleSourceFetch() async {
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
try {
|
||||
final future = switch (widget.source.itemType) {
|
||||
ItemType.manga => ref.watch(
|
||||
fetchMangaSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
),
|
||||
ItemType.anime => ref.watch(
|
||||
fetchAnimeSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
),
|
||||
_ => ref.watch(
|
||||
fetchNovelSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
),
|
||||
};
|
||||
|
||||
await future;
|
||||
} finally {
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTrailingButton(BuildContext context, String label) {
|
||||
return TextButton(
|
||||
onPressed:
|
||||
_isLoading
|
||||
? null
|
||||
: () {
|
||||
if (!_updateAvailable && _sourceNotEmpty) {
|
||||
context.push('/extension_detail', extra: widget.source);
|
||||
} else {
|
||||
_handleSourceFetch();
|
||||
}
|
||||
},
|
||||
child:
|
||||
_isLoading
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2.0),
|
||||
)
|
||||
: Text(label),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
final updateAivalable =
|
||||
widget.isTestSource
|
||||
? false
|
||||
: compareVersions(
|
||||
widget.source.version!,
|
||||
widget.source.versionLast!,
|
||||
) <
|
||||
0;
|
||||
final sourceNotEmpty =
|
||||
widget.source.sourceCode != null &&
|
||||
widget.source.sourceCode!.isNotEmpty;
|
||||
final buttonLabel =
|
||||
!_sourceNotEmpty
|
||||
? l10n.install
|
||||
: _updateAvailable
|
||||
? l10n.update
|
||||
: l10n.settings;
|
||||
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
if (sourceNotEmpty || widget.isTestSource) {
|
||||
if (widget.isTestSource) {
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(widget.source);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateExtension,
|
||||
widget.source.id,
|
||||
widget.source.toJson(),
|
||||
false,
|
||||
);
|
||||
});
|
||||
}
|
||||
context.push('/extension_detail', extra: widget.source);
|
||||
} else {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
widget.source.itemType == ItemType.manga
|
||||
? await ref.watch(
|
||||
fetchMangaSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: widget.source.itemType == ItemType.anime
|
||||
? await ref.watch(
|
||||
fetchAnimeSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: await ref.watch(
|
||||
fetchNovelSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
onTap:
|
||||
_isLoading
|
||||
? null
|
||||
: () {
|
||||
if (_sourceNotEmpty) {
|
||||
context.push('/extension_detail', extra: widget.source);
|
||||
} else {
|
||||
_handleSourceFetch();
|
||||
}
|
||||
},
|
||||
leading: Container(
|
||||
height: 37,
|
||||
width: 37,
|
||||
|
|
@ -145,59 +166,7 @@ class _ExtensionListTileWidgetState
|
|||
),
|
||||
],
|
||||
),
|
||||
trailing: TextButton(
|
||||
onPressed:
|
||||
widget.isTestSource || !updateAivalable && sourceNotEmpty
|
||||
? () {
|
||||
context.push('/extension_detail', extra: widget.source);
|
||||
}
|
||||
: () async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
widget.source.itemType == ItemType.manga
|
||||
? await ref.watch(
|
||||
fetchMangaSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: widget.source.itemType == ItemType.anime
|
||||
? await ref.watch(
|
||||
fetchAnimeSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
)
|
||||
: await ref.watch(
|
||||
fetchNovelSourcesListProvider(
|
||||
id: widget.source.id,
|
||||
reFresh: true,
|
||||
).future,
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
child:
|
||||
_isLoading
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2.0),
|
||||
)
|
||||
: Text(
|
||||
widget.isTestSource
|
||||
? l10n.settings
|
||||
: !sourceNotEmpty
|
||||
? l10n.install
|
||||
: updateAivalable
|
||||
? l10n.update
|
||||
: l10n.settings,
|
||||
),
|
||||
),
|
||||
trailing: _buildTrailingButton(context, buttonLabel),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,232 +24,196 @@ Future<void> fetchSourcesList({
|
|||
if (url == null) return;
|
||||
|
||||
final req = await http.get(Uri.parse(url));
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
|
||||
final sourceList =
|
||||
(jsonDecode(req.body) as List).map((e) => Source.fromJson(e)).toList();
|
||||
(jsonDecode(req.body) as List)
|
||||
.map((e) => Source.fromJson(e))
|
||||
.where(
|
||||
(source) =>
|
||||
source.itemType == itemType &&
|
||||
source.appMinVerReq != null &&
|
||||
compareVersions(info.version, source.appMinVerReq!) > -1,
|
||||
)
|
||||
.toList();
|
||||
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
isar.writeTxnSync(() async {
|
||||
for (var source in sourceList) {
|
||||
if (source.appMinVerReq != null) {
|
||||
if (compareVersions(info.version, source.appMinVerReq!) > -1) {
|
||||
if (source.itemType == itemType) {
|
||||
if (id != null) {
|
||||
if (id == source.id) {
|
||||
final sourc = isar.sources.getSync(id)!;
|
||||
final req = await http.get(Uri.parse(source.sourceCodeUrl!));
|
||||
final headers =
|
||||
getExtensionService(
|
||||
source..sourceCode = req.body,
|
||||
).getHeaders();
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(
|
||||
sourc
|
||||
..headers = jsonEncode(headers)
|
||||
..isAdded = true
|
||||
..sourceCode = req.body
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = id
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version
|
||||
..itemType = itemType
|
||||
..isFullData = source.isFullData ?? false
|
||||
..appMinVerReq = source.appMinVerReq
|
||||
..sourceCodeLanguage = source.sourceCodeLanguage
|
||||
..additionalParams = source.additionalParams ?? ""
|
||||
..isObsolete = false
|
||||
..repo = repo,
|
||||
);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateExtension,
|
||||
sourc.id,
|
||||
sourc.toJson(),
|
||||
false,
|
||||
);
|
||||
});
|
||||
// log("successfully installed or updated");
|
||||
}
|
||||
} else if (isar.sources.getSync(source.id!) != null) {
|
||||
// log("exist");
|
||||
final sourc = isar.sources.getSync(source.id!)!;
|
||||
if (sourc.isAdded!) {
|
||||
if (compareVersions(sourc.version!, source.version!) < 0) {
|
||||
// log("update available auto update");
|
||||
if (ref.watch(autoUpdateExtensionsStateProvider)) {
|
||||
final req = await http.get(
|
||||
Uri.parse(source.sourceCodeUrl!),
|
||||
);
|
||||
final headers =
|
||||
getExtensionService(
|
||||
source..sourceCode = req.body,
|
||||
).getHeaders();
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(
|
||||
sourc
|
||||
..headers = jsonEncode(headers)
|
||||
..isAdded = true
|
||||
..sourceCode = req.body
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = source.id
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version
|
||||
..itemType = itemType
|
||||
..isFullData = source.isFullData ?? false
|
||||
..appMinVerReq = source.appMinVerReq
|
||||
..sourceCodeLanguage = source.sourceCodeLanguage
|
||||
..additionalParams = source.additionalParams ?? ""
|
||||
..isObsolete = false
|
||||
..repo = repo,
|
||||
);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateExtension,
|
||||
sourc.id,
|
||||
sourc.toJson(),
|
||||
false,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// log("update aivalable");
|
||||
isar.sources.putSync(sourc..versionLast = source.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id != null) {
|
||||
final matchingSource = sourceList.firstWhere(
|
||||
(source) => source.id == id,
|
||||
orElse: () => Source(),
|
||||
);
|
||||
if (matchingSource.id != null) {
|
||||
await _updateSource(matchingSource, ref, repo, itemType);
|
||||
}
|
||||
} else {
|
||||
for (var source in sourceList) {
|
||||
final existingSource = isar.sources.getSync(source.id!);
|
||||
if (existingSource != null) {
|
||||
if (existingSource.isAdded! &&
|
||||
compareVersions(existingSource.version!, source.version!) < 0) {
|
||||
if (ref.watch(autoUpdateExtensionsStateProvider)) {
|
||||
await _updateSource(source, ref, repo, itemType);
|
||||
} else {
|
||||
final newSource =
|
||||
Source()
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = source.id
|
||||
..sourceCode = source.sourceCode
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version
|
||||
..itemType = itemType
|
||||
..sourceCodeLanguage = source.sourceCodeLanguage
|
||||
..isFullData = source.isFullData ?? false
|
||||
..appMinVerReq = source.appMinVerReq
|
||||
..isObsolete = false
|
||||
..repo = repo;
|
||||
isar.sources.putSync(newSource);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.addExtension,
|
||||
null,
|
||||
newSource.toJson(),
|
||||
false,
|
||||
);
|
||||
// log("new source");
|
||||
isar.sources.putSync(
|
||||
existingSource..versionLast = source.version,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_addNewSource(source, ref, repo, itemType);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
checkIfSourceIsObsolete(sourceList, repo!, itemType, ref);
|
||||
}
|
||||
|
||||
Future<void> _updateSource(
|
||||
Source source,
|
||||
Ref ref,
|
||||
Repo? repo,
|
||||
ItemType itemType,
|
||||
) async {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
final req = await http.get(Uri.parse(source.sourceCodeUrl!));
|
||||
final headers =
|
||||
getExtensionService(source..sourceCode = req.body).getHeaders();
|
||||
|
||||
final updatedSource =
|
||||
Source()
|
||||
..headers = jsonEncode(headers)
|
||||
..isAdded = true
|
||||
..sourceCode = req.body
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = source.id
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version
|
||||
..itemType = itemType
|
||||
..isFullData = source.isFullData ?? false
|
||||
..appMinVerReq = source.appMinVerReq
|
||||
..sourceCodeLanguage = source.sourceCodeLanguage
|
||||
..additionalParams = source.additionalParams ?? ""
|
||||
..isObsolete = false
|
||||
..repo = repo;
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(updatedSource);
|
||||
});
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateExtension,
|
||||
updatedSource.id,
|
||||
updatedSource.toJson(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
void _addNewSource(Source source, Ref ref, Repo? repo, ItemType itemType) {
|
||||
final newSource =
|
||||
Source()
|
||||
..sourceCodeUrl = source.sourceCodeUrl
|
||||
..id = source.id
|
||||
..sourceCode = source.sourceCode
|
||||
..apiUrl = source.apiUrl
|
||||
..baseUrl = source.baseUrl
|
||||
..dateFormat = source.dateFormat
|
||||
..dateFormatLocale = source.dateFormatLocale
|
||||
..hasCloudflare = source.hasCloudflare
|
||||
..iconUrl = source.iconUrl
|
||||
..typeSource = source.typeSource
|
||||
..lang = source.lang
|
||||
..isNsfw = source.isNsfw
|
||||
..name = source.name
|
||||
..version = source.version
|
||||
..versionLast = source.version
|
||||
..itemType = itemType
|
||||
..sourceCodeLanguage = source.sourceCodeLanguage
|
||||
..isFullData = source.isFullData ?? false
|
||||
..appMinVerReq = source.appMinVerReq
|
||||
..isObsolete = false
|
||||
..repo = repo;
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
isar.sources.putSync(newSource);
|
||||
});
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.addExtension, null, newSource.toJson(), false);
|
||||
}
|
||||
|
||||
void checkIfSourceIsObsolete(
|
||||
List<Source> sourceList,
|
||||
Repo repo,
|
||||
ItemType itemType,
|
||||
Ref ref,
|
||||
) {
|
||||
for (var source
|
||||
in isar.sources
|
||||
if (sourceList.isEmpty) return;
|
||||
|
||||
final sources =
|
||||
isar.sources
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.itemTypeEqualTo(itemType)
|
||||
.findAllSync()) {
|
||||
if (sourceList.isNotEmpty && !(source.isLocal ?? false)) {
|
||||
final ids =
|
||||
sourceList.where((e) => e.id != null).map((e) => e.id).toList();
|
||||
if (ids.isNotEmpty) {
|
||||
isar.writeTxnSync(() {
|
||||
if (source.isObsolete !=
|
||||
(!ids.contains(source.id) &&
|
||||
source.repo?.jsonUrl == repo.jsonUrl)) {
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateExtension,
|
||||
source.id,
|
||||
source.toJson(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
isar.sources.putSync(
|
||||
source
|
||||
..isObsolete =
|
||||
!ids.contains(source.id) &&
|
||||
source.repo?.jsonUrl == repo.jsonUrl,
|
||||
);
|
||||
});
|
||||
.and()
|
||||
.isLocalEqualTo(false)
|
||||
.findAllSync();
|
||||
|
||||
if (sources.isEmpty) return;
|
||||
|
||||
final sourceIds =
|
||||
sourceList.where((e) => e.id != null).map((e) => e.id!).toSet();
|
||||
|
||||
if (sourceIds.isEmpty) return;
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
for (var source in sources) {
|
||||
final isNowObsolete =
|
||||
!sourceIds.contains(source.id) &&
|
||||
source.repo?.jsonUrl == repo.jsonUrl;
|
||||
|
||||
if (source.isObsolete != isNowObsolete) {
|
||||
source.isObsolete = isNowObsolete;
|
||||
isar.sources.putSync(source);
|
||||
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(
|
||||
ActionType.updateExtension,
|
||||
source.id,
|
||||
source.toJson(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int compareVersions(String version1, String version2) {
|
||||
List<String> v1Components = version1.split('.');
|
||||
List<String> v2Components = version2.split('.');
|
||||
final v1Parts = version1.split('.');
|
||||
final v2Parts = version2.split('.');
|
||||
final minLength =
|
||||
v1Parts.length < v2Parts.length ? v1Parts.length : v2Parts.length;
|
||||
|
||||
for (int i = 0; i < v1Components.length && i < v2Components.length; i++) {
|
||||
int v1Value = int.parse(
|
||||
v1Components.length == i + 1 && v1Components[i].length == 1
|
||||
? "${v1Components[i]}0"
|
||||
: v1Components[i],
|
||||
);
|
||||
int v2Value = int.parse(
|
||||
v2Components.length == i + 1 && v2Components[i].length == 1
|
||||
? "${v2Components[i]}0"
|
||||
: v2Components[i],
|
||||
);
|
||||
for (var i = 0; i < minLength; i++) {
|
||||
final v1Value = int.parse(v1Parts[i].padRight(2, '0'));
|
||||
final v2Value = int.parse(v2Parts[i].padRight(2, '0'));
|
||||
|
||||
if (v1Value < v2Value) {
|
||||
return -1;
|
||||
} else if (v1Value > v2Value) {
|
||||
return 1;
|
||||
}
|
||||
final comparison = v1Value.compareTo(v2Value);
|
||||
if (comparison != 0) return comparison;
|
||||
}
|
||||
|
||||
if (v1Components.length < v2Components.length) {
|
||||
return -1;
|
||||
} else if (v1Components.length > v2Components.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return v1Parts.length.compareTo(v2Parts.length);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue